Election results data entry and verification software built by Ona Systems, commissioned by the Libyan High National Elections Commission and UNDP.
git clone git@github.com:onaio/tally-ho.gitPrerequisites: uv and PostgreSQL.
uv syncuv will install the pinned Python interpreter from .python-version and create .venv with all dependencies from uv.lock.
Install libpq-dev for PostgreSQL headers:
sudo apt-get install libpq-devInstall memcached and Redis:
sudo apt-get update && sudo apt-get install -y memcached redis-serverEnable pre-commit hook checks:
uv run pre-commit installuv run celery -A tally_ho.celeryapp worker --loglevel=infoThe docker-compose stack auto-seeds a demo tally on boot via
create_demo_tally — 2 ballots, 6 candidates, 2 centers, 4 stations,
8 result forms, plus quarantine checks. It's idempotent on name;
re-run with --clean to wipe and re-create.
To run management commands against the running stack, use the
./build.sh wrapper:
./build.sh create_demo_tally --cleanNote: Result form barcodes must be at least 11 characters — the Data Entry barcode-verify UI enforces this client-side. The demo tally uses 11-digit barcodes starting at
10000000001.
For the PVP upload flow specifically, create_demo_pvp_bundle emits a
bundle zip targeting every result form in a tally. The zip lands at
data/demo_pvp_bundle_<tally_id>.zip (visible on the host via the
project-root bind mount).
./build.sh create_demo_pvp_bundle --tally-id <id>See docs/overview/pvp.md for the full PVP end-to-end walkthrough.
With Docker and docker-compose installed, build and run:
docker-compose build
docker-compose upVisit 127.0.0.1:8000. For production, modify the docker-compose.yml file:
- Change NGINX port from 8000 to 80.
- Add your host to
ALLOWED_HOSTSintally_ho/settings/docker.py.
Run tests with:
uv run pytest tally_hoFollow these steps for managing Arabic translations:
-
Add Arabic Language: Update
settings.py:# settings.py LANGUAGES = [ ('en', 'English'), ('ar', 'Arabic'), ] # Ensure LANGUAGE_CODE is set to a default language (e.g., 'en') LANGUAGE_CODE = 'en'
-
Generate Arabic Translation Files:
django-admin makemessages -l ar
-
Edit Arabic Translations: Update
locale/ar/LC_MESSAGES/django.po. -
Compile Translations:
django-admin compilemessages
Install graphviz. The dev dependency group already includes django-extensions and pydot.
Generate all model graphs:
uv run python manage.py graph_models --settings=tally_ho.settings.dev --pydot -a -g -o tally-ho-all-models.pngGenerate specific app model graphs:
uv run python manage.py graph_models --settings=tally_ho.settings.dev --pydot -a -X GroupObjectPermission,... -g -o tally-ho-app-models.pngUse the create_demo_users command to create demo users with usernames like super_administrator, and password data.
File upload limit is set to 10MB in MAX_FILE_UPLOAD_SIZE within tally_ho/settings/common.py.
- Article: Writing Python Code to Decide an Election
- PyConZA 2014 presentation: Writing Python Code to Decide an Election