Custom Display Scripts

Write your own TouchPoint Python script and surface the result on DisplayCache signage.

Overview

Pick the Custom display type in DisplayCache when none of the built-in types (Birthdays, Events, Milestones, Volunteers) match what you want to show. You write a TouchPoint Python script that returns rows as JSON, and DisplayCache renders them on your screens using the same Cards / Table / Bulletin layouts as the built-in types.

Quick recipe:
  1. Save a Python script in TouchPoint under Admin → Special Content → Python.
  2. Make it print a JSON array between <!--DC_START--> and <!--DC_END--> markers.
  3. In DisplayCache, set the display type to Custom, paste the script name, click Detect from Script, then Save & Publish.

1. Install your script in TouchPoint

  1. Sign in to TouchPoint with an account that has both the Developer and APIOnly roles. (See the TouchPoint setup guide if you don't have them yet.)
  2. Go to Admin → Special Content → Python.
  3. Click New and give your script a name without spaces — for example, DisplayCacheBuildingUse. This name is what you'll paste into the Custom display in DisplayCache.
  4. Paste your script body (see the example below for a starting point).
  5. Click Save. TouchPoint will compile the script; fix any syntax errors it reports.

That's it — you don't have to expose the script publicly or share a URL. DisplayCache calls it through the same authenticated /PythonAPI endpoint the connection used during setup.

2. Output format

Your script must print a JSON array (one row per record) wrapped in DisplayCache markers so the integration can extract it cleanly from TouchPoint's HTML response.

print("<!--DC_START-->")
print(json.dumps(rows))
print("<!--DC_END-->")

Each row is a JSON object. Field names become column keys; you can use any keys you like. Examples that work well on screen:

[
  {
    "name": "Sanctuary",
    "next_event": "Sunday Service",
    "starts_at": "Sun 10:00 AM",
    "status": "Open"
  },
  {
    "name": "Fellowship Hall",
    "next_event": "Wednesday Dinner",
    "starts_at": "Wed 6:00 PM",
    "status": "Reserved"
  }
]
Errors: if something goes wrong, return a single object with an error key — DisplayCache will surface it in the admin instead of failing silently.
print("<!--DC_START-->")
print(json.dumps({"error": "Permission denied"}))
print("<!--DC_END-->")

3. Reading parameters

DisplayCache passes any parameters you configure on the display (under Parameters on the Custom display card) into your script as query-string arguments. Inside TouchPoint they're available on the Data object.

days = int(Data.days) if hasattr(Data, 'days') and Data.days else 7
org_id = Data.oid if hasattr(Data, 'oid') and Data.oid else None
limit = int(Data.limit) if hasattr(Data, 'limit') and Data.limit else 50

Two important rules:

  • Always check hasattr(Data, 'foo') before reading Data.foo — the attribute is missing if the user didn't set it.
  • Everything is a string when it arrives, so wrap numbers with int(...) or float(...).

To add a parameter in DisplayCache, open the Custom display, click + Parameter, and enter a name (matching the attribute you read in the script) plus a value. Common parameters used by the built-in scripts:

NameTypical use
daysNumber of days ahead to look
oidTouchPoint Involvement / Organization ID
search_nameSearch Builder saved-search name
limitMaximum rows to return

4. Detect columns

Once your script is saved and your parameters are set, click Detect from Script on the Custom display card. DisplayCache will run the script against your live TouchPoint data, look at the first row, and auto-populate the Columns table with every field it finds.

The Columns table controls which fields show on screen and what labels they use:

  • Key — must match a field name in your JSON output (e.g. starts_at).
  • Label — the human-friendly header shown on the Table format and as a prefix on Cards.
  • Order — drag to reorder; delete any column you don't want to display.

You can also add columns manually if your dataset is sparse (some rows missing certain fields) so they don't get skipped by detection.

Tip: after editing the script in TouchPoint, click Detect from Script again to refresh. The Preview button on the same card lets you see the rendered display before saving.

5. Example: Building usage this week

Here's a complete, runnable script that lists eSPACE-style building reservations directly from TouchPoint's Meetings table for the next 7 days. Save it in TouchPoint as DisplayCacheBuildingUse, then enter that same name in DisplayCache and click Detect from Script.

# version: 1
# ============================================================================
# DisplayCache - Building Usage (example custom script)
# Lists upcoming meetings/reservations for the next N days.
#
# PARAMETERS (set on the Custom display in DisplayCache):
#   days   - Number of days ahead to look (default: 7)
#   limit  - Max rows to return (default: 50)
# ============================================================================
#API
import json
import sys

# Guard: running outside TouchPoint? Bail out cleanly.
try:
    _check = Data
except NameError:
    print("This script is designed to run inside TouchPoint.")
    sys.exit(0)

try:
    from datetime import datetime, timedelta

    days = int(Data.days) if hasattr(Data, 'days') and Data.days else 7
    limit = int(Data.limit) if hasattr(Data, 'limit') and Data.limit else 50

    start = datetime.now()
    end = start + timedelta(days=days)

    sql = '''
    SELECT TOP (@limit)
        m.MeetingId,
        o.OrganizationName AS [involvement],
        m.MeetingDate,
        m.Location AS [location],
        m.Description AS [description]
    FROM dbo.Meetings m
    JOIN dbo.Organizations o ON o.OrganizationId = m.OrganizationId
    WHERE m.MeetingDate BETWEEN @start AND @end
    ORDER BY m.MeetingDate ASC
    '''

    rows = q.QuerySql(sql, {
        'limit': limit,
        'start': start,
        'end': end,
    })

    results = []
    for row in rows:
        when = row.MeetingDate.strftime("%a %b %d %I:%M %p") if row.MeetingDate else ""
        results.append({
            "involvement": row.involvement or "",
            "location": row.location or "TBD",
            "starts_at": when,
            "description": row.description or "",
        })

    print("<!--DC_START-->")
    print(json.dumps(results))
    print("<!--DC_END-->")
except:
    e = sys.exc_info()
    print("<!--DC_START-->")
    print(json.dumps({"error": str(e[1])}))
    print("<!--DC_END-->")

What it produces

After clicking Detect from Script, the Columns table will fill with involvement, location, starts_at, and description. Pick a Display Format (Cards is a good default), set a Display Title like “This Week at Church,” and click Save & Publish.

Tweaking it

  • Filter by location: add AND m.Location LIKE '%Sanctuary%' to the WHERE clause, or accept a location parameter.
  • Filter by involvement: pass oid as a parameter, then add AND m.OrganizationId = @oid.
  • Hide private meetings: add AND o.IsHidden = 0.

Troubleshooting

SymptomWhat to check
"Script returned no data to detect columns from" The script ran but printed an empty list. Confirm the SQL returns rows in TouchPoint's SQL tab; then double-check your days / oid parameters in DisplayCache.
"Detection failed: ..." The script raised an error before printing the JSON. Open the script in TouchPoint and run it from there — the error message will appear directly. Common causes: missing parameter, SQL typo, referencing a column that doesn't exist on a row.
Columns detected but Preview is blank Likely a key mismatch. Open the Raw data tab of the preview and confirm the JSON keys match the columns you defined.
Photos / images not loading For TouchPoint people photos, use the field name picture_url — DisplayCache rewrites that one through an authenticated proxy. Other image URLs must be publicly reachable.
"Access denied to PythonAPI" The connected TouchPoint user is missing the APIOnly role. Email TouchPoint support to enable it — usually one business day.

Need help building a script? Reach out — we're happy to look at your TouchPoint schema and sketch one for you.