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.
- 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.
- Continuous:
- 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_failurefor both paths.
dhis2_analytics_trigger.py– main scripttelegram_alerts.py– Telegram helperrequirements.txt
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.txtKeep 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.jsonThen 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:8080asbase_url.
resource_tables_dirty_viewis optional — leave itnullunless 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.
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.
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.
# 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.jsonCLI flags: --poll-interval, --max-wait, --no-watch.
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- Success: an
INFOevent withcompleted:trueand a message like "Analytics tables updated: …". - Failure: latest
completed:trueevent isERROR/FATAL, or any fatal signal with no success marker. - A short grace window is used after the first
completed:trueto catch trailing events.
- Auth: uses
Authorization: ApiToken <token>. For Basic auth, setDHIS2_USERNAME/DHIS2_PASSWORDenv vars. Note that the use of Basic Authentication is discouraged. - SSL: set
verify_ssl:falsetemporarily to diagnose CA issues (fix CA properly for prod). - Overlaps:
flockprevents 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.