|
| 1 | +# CommCare Connect |
| 2 | + |
| 3 | +Django 4.2 + PostGIS monolith for managing community health worker opportunities, payments, and workflows. Integrates with CommCare HQ and ConnectID services. |
| 4 | + |
| 5 | +## Commands |
| 6 | + |
| 7 | +```bash |
| 8 | +# Local services (PostgreSQL/PostGIS + Redis) |
| 9 | +inv up # docker compose up |
| 10 | +inv down # docker compose down |
| 11 | + |
| 12 | +# Django |
| 13 | +./manage.py migrate # run migrations (dev) |
| 14 | +./manage.py migrate_multi # run migrations on both primary + secondary DB (prod) |
| 15 | +./manage.py runserver # dev server |
| 16 | + |
| 17 | +# JavaScript/CSS |
| 18 | +npm ci # install deps |
| 19 | +inv build-js # dev build |
| 20 | +inv build-js -w # dev build with watch |
| 21 | +inv build-js --prod # production build |
| 22 | + |
| 23 | +# Celery (local dev) |
| 24 | +celery -A config.celery_app worker -B -l info |
| 25 | + |
| 26 | +# Tests |
| 27 | +pytest # run all tests |
| 28 | +pytest path/to/test_file.py::test_name # run single test |
| 29 | + |
| 30 | +# Linting (runs black, isort, flake8, pyupgrade, django-upgrade, prettier) |
| 31 | +pre-commit run -a |
| 32 | + |
| 33 | +# Requirements (pip-tools) |
| 34 | +inv requirements # recompile .txt from .in files |
| 35 | +inv requirements --upgrade-package <pkg> |
| 36 | + |
| 37 | +# Translations |
| 38 | +inv translations |
| 39 | +``` |
| 40 | + |
| 41 | +## Architecture |
| 42 | + |
| 43 | +- **Monolith**: Django serves both HTML templates (Tailwind + Alpine.js + htmx) and a DRF REST API |
| 44 | +- **URL pattern**: Most views scoped under `/a/<org_slug>/` via `OrganizationMiddleware` |
| 45 | +- **API versioning**: `AcceptHeaderVersioning` with versions `1.0` and `2.0` |
| 46 | +- **Background tasks**: Celery with Redis broker; beat scheduler uses DB |
| 47 | +- **Feature flags**: django-waffle with custom `Flag` model; constants in `commcare_connect/flags/switch_names.py` |
| 48 | +- **Audit trail**: django-pghistory stores `username` + `user_email` in context (survives user deletion) |
| 49 | +- **Database**: PostgreSQL + PostGIS. `ATOMIC_REQUESTS = True` (all requests are transactions) |
| 50 | +- **Deployment**: Kamal (Docker-based) + Ansible. NOT Elastic Beanstalk despite README mention |
| 51 | + |
| 52 | +### Key directories |
| 53 | + |
| 54 | +``` |
| 55 | +commcare_connect/ |
| 56 | + opportunity/ # Core domain: opportunities, visits, payments (largest app) |
| 57 | + organization/ # Org management, membership roles |
| 58 | + program/ # Program management, linking orgs/opportunities |
| 59 | + users/ # Custom User model, ConnectID links |
| 60 | + commcarehq/ # CommCare HQ server integration |
| 61 | + connect_id_client/ # HTTP client for ConnectID service |
| 62 | + form_receiver/ # Receives xforms from CommCare HQ |
| 63 | + microplanning/ # Maps, catchment areas (Mapbox) |
| 64 | + reports/ # KPI and admin reports |
| 65 | + flags/ # Waffle feature flag/switch name constants |
| 66 | + multidb/ # Secondary DB support + logical replication |
| 67 | + utils/ # BaseModel, middleware, caching, permissions |
| 68 | +config/ |
| 69 | + settings/ # base.py, local.py, test.py, staging.py, production.py |
| 70 | + api_router.py # DRF API URL routing |
| 71 | + celery_app.py # Celery config |
| 72 | + urls.py # Root URL config |
| 73 | +``` |
| 74 | + |
| 75 | +## Code Style |
| 76 | + |
| 77 | +- **Python**: black + isort (line length 119, target py311). flake8 for linting |
| 78 | +- **JS/CSS**: prettier (tab-width 2, single-quote). Templates excluded from prettier |
| 79 | +- **Pre-commit hooks enforce all of the above** plus pyupgrade (--py311-plus) and django-upgrade (--target-version 4.1) |
| 80 | +- Django models should extend `BaseModel` from `commcare_connect/utils/db.py` (provides `created_by`, `modified_by`, `date_created`, `date_modified`) |
| 81 | +- Custom `User` model uses single `name` field instead of `first_name`/`last_name` |
| 82 | + |
| 83 | +## Testing |
| 84 | + |
| 85 | +- **Framework**: pytest + pytest-django + factory-boy |
| 86 | +- **Test location**: `commcare_connect/<app>/tests/` with `factories.py`, `test_*.py` |
| 87 | +- **Global fixtures** in `commcare_connect/conftest.py`: `organization`, `user`, `opportunity`, `mobile_user`, `mobile_user_with_connect_link`, `org_user_member`, `org_user_admin`, `api_rf`, `api_client` |
| 88 | +- **autouse fixtures**: `media_storage` (redirects to tmpdir), `ensure_currency_country_data` (repopulates Currency/Country flushed between tests) |
| 89 | +- HTTP mocking: `pytest-httpx` for httpx calls |
| 90 | + |
| 91 | +## Gotchas |
| 92 | + |
| 93 | +- **PostGIS required everywhere** (including tests). Local dev needs `gdal`, `geos`, `proj` system libs. On macOS, set `GDAL_LIBRARY_PATH` and `GEOS_LIBRARY_PATH` in `.env` |
| 94 | +- **`--reuse-db` + Currency/Country data**: These models get flushed between tests. The `ensure_currency_country_data` autouse fixture handles this — don't remove it |
| 95 | +- **API UUID transition**: The `API_UUID` waffle switch controls whether API endpoints accept integer PKs or UUIDs. Use `get_object_or_list_by_uuid_or_int()` from `utils/db.py` for API lookups |
| 96 | +- **CSRF via sessions**: `CSRF_USE_SESSIONS = True`. Templates use `hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'` on `<body>` for htmx |
| 97 | +- **Webpack bundle tracker**: Frontend builds write `webpack-stats.json`. Templates reference bundles from `staticfiles/bundles/` |
| 98 | +- **CI uses**: `postgis/postgis:15-3.5` image, Python 3.11, requires `gdal-bin libproj-dev` apt packages |
0 commit comments