Skip to content

DHIS2 Analytics Trigger

A Python utility to trigger DHIS2 analytics via POST and (optionally) watch progress, log output, and send alerts (generic webhook + Telegram).

The primary use case is continuous analytics on high-volume tracker systems where standard analytics runs are too slow. By using lastYears=0, only recently changed data is processed, allowing frequent short runs instead of one long nightly job.

What it does

  • Triggers analytics runs using modes defined in your config file. Each mode is a set of DHIS2 analytics query parameters. Three common modes ship in config.json.sample:
    • Continuous: lastYears=0, skips resource tables and aggregate tables — only recently changed tracker data. Use for high-frequency scheduling (e.g. every 2h).
    • Incremental: skips resource tables; lastYears=1
    • Full: includes resource tables; processes all years You can rename these, add your own, or remove ones you don't need.
  • Polls /api/system/tasks/ANALYTICS_TABLE/<id> until completion, then classifies the outcome.
  • Sends alerts:
    • Webhook (any JSON endpoint)
    • Telegram (bot token + chat id)
    • Respects only_on_failure for both paths.

Files

  • dhis2_analytics_trigger.py – main script
  • telegram_alerts.py – Telegram helper
  • requirements.txt

Install

sudo mkdir -p /opt/tool-analytics-trigger
cd /opt/tool-analytics-trigger
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

Configure

Keep your config in /opt/tool-analytics-trigger/config.json alongside the script. Copy config.json.sample as a starting point:

cp config.json.sample /opt/tool-analytics-trigger/config.json

Then edit it:

{
  "dhis": {
    "base_url": "https://your-dhis2-instance/",
    "token": "<PASTE_TOKEN>",
    "verify_ssl": true,
    "timeout_seconds": 60,
    "resource_tables_dirty_view": null
  },
  "alerting": {
    "webhook_url": null,
    "only_on_failure": true,
    "telegram": {
      "bot_token": "123456:ABCDEF...",
      "chat_id": "-1001234567890"
    }
  },
  "modes": {
    "continuous": {
      "skipResourceTables": "true",
      "skipAggregate": "true",
      "lastYears": "0"
    },
    "incremental": {
      "skipResourceTables": "true",
      "skipOrgUnitOwnership": "true",
      "skipTrackedEntities": "true",
      "skipOutliers": "true",
      "lastYears": "1"
    },
    "full": {
      "skipOutliers": "true"
    }
  }
}

For local/dev DHIS2 use e.g. http://localhost:8080 as base_url.

resource_tables_dirty_view is optional — leave it null unless you've set up the SQL view described in "Resource table dirty-check preflight" below.

If you want to use the Telegram alerts, create a bot with BotFather and get your chat ID with @userinfobot. For group chats, add the bot to the group and promote it to admin.

Resource table dirty-check preflight (optional)

continuous mode (lastYears=0) never rebuilds resource tables on its own — DHIS2 silently ignores skipResourceTables in that case. If you want resource tables kept up to date anyway, set dhis.resource_tables_dirty_view in your config to a DHIS2 SQL View UID that reports whether they're currently dirty; the script will rebuild them first, before the analytics run, whenever the view says so. This is entirely optional and manual — this project does not create or manage the SQL function or the SQL View. Set it up once per DHIS2 instance:

1. Install the SQL function on the database. The resource_tables_dirty(timestamp) Postgres function lives in this repo's resource_tables_dirty.sql:

psql -f resource_tables_dirty.sql -d <your-dhis2-db>

2. Create the SQL View in DHIS2. In the Maintenance app, create a new SQL View with:

SELECT resource_tables_dirty(now()::timestamp);

Copy its UID into your config as dhis.resource_tables_dirty_view.

3. Set the SQL View's sharing correctly. Open its sharing settings and set:

  • Public access: No access
  • External access: No access
  • The DHIS2 account that runs this script (whichever user your configured API token belongs to): Data → Can view only. Metadata access doesn't matter here — only data-read does, since the preflight executes the view via GET /api/sqlViews/{uid}/data.json.

If the triggering account is missing data-read sharing, you'll get a 409 Conflict ("not authorised to read data from SQL view") on every run. That fails safe — the preflight treats it as "unknown" and skips the rebuild, so analytics still runs normally — but silently, so it's easy to not notice the dirty-check simply isn't doing anything.

Upgrading from older versions

If you are upgrading from a version that had hardcoded modes, existing configs without a modes block will still work — the script falls back to built-in defaults for continuous, incremental, and full. However, adding an explicit modes block to your config is recommended so you can customise parameters. Copy the modes section from config.json.sample into your config, or see the example above.

Run

# Dry run
/opt/tool-analytics-trigger/.venv/bin/python dhis2_analytics_trigger.py --mode continuous --config /opt/tool-analytics-trigger/config.json --dry-run

# Continuous analytics (recently changed data only)
/opt/tool-analytics-trigger/.venv/bin/python dhis2_analytics_trigger.py --mode continuous --config /opt/tool-analytics-trigger/config.json

# Full analytics run
/opt/tool-analytics-trigger/.venv/bin/python dhis2_analytics_trigger.py --mode full --config /opt/tool-analytics-trigger/config.json

CLI flags: --poll-interval, --max-wait, --no-watch.

Cron

Recommended schedule for a high-volume tracker-only system:

  • Continuous analytics every 2 hours during business hours (07:00–17:00), Mon–Sat
  • Full rebuild once a week (early Sunday morning)
TZ=Africa/Nairobi
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Continuous: every 2h during business hours (07:00–17:00), Mon–Sat
0 7-18/2 * * 1-6 /usr/bin/flock -n /var/lock/dhis2-analytics.lock /opt/tool-analytics-trigger/.venv/bin/python /opt/tool-analytics-trigger/dhis2_analytics_trigger.py --mode continuous --config /opt/tool-analytics-trigger/config.json >> /var/log/dhis2_trigger.log 2>&1

# Full: weekly on Sunday at 01:00 (allow up to 12h)
0 1 * * 0 /usr/bin/flock -n /var/lock/dhis2-analytics.lock /opt/tool-analytics-trigger/.venv/bin/python /opt/tool-analytics-trigger/dhis2_analytics_trigger.py --mode full --max-wait 43200 --config /opt/tool-analytics-trigger/config.json >> /var/log/dhis2_trigger.log 2>&1

How success/failure is detected

  • Success: an INFO event with completed:true and a message like "Analytics tables updated: …".
  • Failure: latest completed:true event is ERROR/FATAL, or any fatal signal with no success marker.
  • A short grace window is used after the first completed:true to catch trailing events.

Troubleshooting

  • Auth: uses Authorization: ApiToken <token>. For Basic auth, set DHIS2_USERNAME / DHIS2_PASSWORD env vars. Note that the use of Basic Authentication is discouraged.
  • SSL: set verify_ssl:false temporarily to diagnose CA issues (fix CA properly for prod).
  • Overlaps: flock prevents concurrent runs.
  • Logs: /var/log/dhis2_trigger.log; set up logrotate if needed.

Tweak lastYears and other parameters in the modes block of your config file to match your DHIS2 instance's needs.

About

A script to help you keep analytics running and get alerts if something goes wrong.

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages