Backend service for PitWatch: user/admin authentication, pothole reporting, geospatial proximity checks, authority notifications, dashboard analytics, and async ML pothole detection.
- Overview
- Core Features
- Tech Stack
- System Architecture (mmd)
- Repository Structure
- Quick Start (Local)
- Run with Docker Compose
- Environment Variables
- Authentication Model
- API Conventions
- Complete Endpoint Reference
- Mermaid Flows (mmd)
- AI Disclosure
- Operational Notes
- Testing
- Troubleshooting
- AI Usage Disclosure
PitWatch backend is a Django REST API with Celery workers for async ML inference.
It provides:
- Account signup/login for users and admins
- JWT auth via Authorization header and secure cookies
- Report submission with geospatial duplicate checks (PostGIS functions)
- Nearby report lookup and severity clustering
- Authority and emergency notifications via Brevo email API
- Async ONNX pothole inference queue via Celery + Redis
- Admin dashboard summaries and report status management
- User auth and profile endpoints
- Admin-only report moderation and dashboard summary
- Pothole report creation and pagination
- Nearby pothole lookup by coordinates and radius
- Report status transitions (pending/in_progress/resolved/rejected)
- Emergency notification endpoint
- ML detect (sync) and submit/status (async)
- Python 3.12
- Django 5.1
- Django REST Framework
- SimpleJWT
- PostgreSQL (with PostGIS functions used in SQL)
- Redis (cache + Celery broker/result)
- Celery
- ONNX Runtime + OpenCV + NumPy
- Cloudinary storage
- Brevo SMTP API (transactional email)
- Docker + Docker Compose
flowchart LR
FE[Client App / Admin Panel]
API[Django API\nGunicorn]
DB[(PostgreSQL)]
REDIS[(Redis)]
WORKER[Celery Worker]
MODEL[ONNX Model\nmodels/best.onnx]
CLOUD[Cloudinary]
BREVO[Brevo Email API]
OSM[OSM APIs\nOverpass + Nominatim]
FE -->|HTTP| API
API --> DB
API --> REDIS
API --> CLOUD
API --> OSM
API --> BREVO
API -->|queue task| REDIS
REDIS --> WORKER
WORKER --> MODEL
WORKER --> DB
Top-level highlights:
pitwatch/pitwatch/: Django project config (settings.py,urls.py,celery.py)pitwatch/accounts/: user/admin auth logicpitwatch/reports/: report CRUD, geospatial queries, authority notificationspitwatch/ml/: sync detect + async inference task pipelinepitwatch/dashboard/: admin summary endpointpitwatch/models/: ONNX model files (best.onnxorpothole_model.onnx)docker-compose.yml,Dockerfile: container runtime
python -m venv env
# Windows PowerShell
env\Scripts\Activate.ps1pip install -r requirements.txtCreate a .env in project root and populate required values (see Environment Variables).
cd pitwatch
python manage.py migratepython manage.py runservercd pitwatch
celery -A pitwatch worker -l info --without-gossip --without-mingle --without-heartbeatdocker compose up --buildCompose services:
web: runs migrations and starts Gunicorn on port 8000worker: runs Celery worker for ML inference
Note: current compose file expects external DB/Redis endpoints via .env.
Commonly used settings in pitwatch/pitwatch/settings.py:
DJANGO_SECRET_KEY(default:dev-only-secret)DJANGO_DEBUG(true/false)DJANGO_ALLOWED_HOSTS(comma-separated)CORS_ALLOWED_ORIGINS(comma-separated)CORS_ALLOW_ALL_ORIGINS(true/false)
DB_NAMEDB_USERDB_PASSWORDDB_HOSTDB_PORTDB_SSLMODE(default:require)
JWT_COOKIE_SECUREJWT_COOKIE_SAMESITE(default:None)
CACHE_URL(default:redis://localhost:6379/1)REDIS_URL(default broker:redis://localhost:6379/0)CELERY_RESULT_BACKEND(defaults to broker URL)
DRF_ANON_THROTTLE_RATEDRF_USER_THROTTLE_RATEDRF_AUTH_LOGIN_THROTTLE_RATEDRF_AUTH_SIGNUP_THROTTLE_RATEDRF_AUTH_REFRESH_THROTTLE_RATEDRF_ML_PUBLIC_DETECT_THROTTLE_RATEDRF_ML_SUBMIT_THROTTLE_RATEDRF_ML_STATUS_THROTTLE_RATEDRF_REPORTS_THROTTLE_RATEDRF_ADMIN_REPORTS_LIST_THROTTLE_RATEDRF_DASHBOARD_SUMMARY_THROTTLE_RATE
CLOUDINARY_CLOUD_NAMECLOUDINARY_API_KEYCLOUDINARY_API_SECRET
BREVO_API_URL(default:https://api.brevo.com/v3/smtp/email)BREVO_API_KEYBREVO_SENDER_EMAILBREVO_SENDER_NAME
AUTHORITY_EMAIL_NHAIAUTHORITY_EMAIL_STATE_PWDAUTHORITY_EMAIL_MUNICIPAL_AUTHORITYAUTHORITY_EMAIL_MUNICIPAL_CORPORATIONAUTHORITY_EMAIL_UNKNOWN
POTHOLE_CLUSTER_RADIUS_METERS(default:130)POTHOLE_CLUSTER_THRESHOLD(default:8)
PitWatch uses JWT with both token responses and cookie support.
- Access token cookie:
access_token - Refresh token cookie:
refresh_token - Header auth:
Authorization: Bearer <access_token>
Notes:
- Login/signup responses include
accessandrefreshin JSON and set cookies. - Some endpoints require strict
IsAuthenticated. - Admin endpoints additionally check
user.is_superuser.
- Base route:
/api/<version>/...(example version:v1) - Content type:
- JSON for most endpoints
multipart/form-datafor image upload endpoints
- Pagination pattern (where used):
page,page_size
All paths below are shown with v1 example prefix.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /admin/ |
Django admin session | Django admin site |
| GET | /api/v1/dashboard/summary/ |
Access token + superuser | Aggregate dashboard totals and 7-day trend |
Dashboard auth details:
- Accepts Bearer header token, otherwise falls back to
access_tokencookie. - Returns 401 for missing/invalid token and 403 for non-superusers.
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/v1/accounts/signup/ |
Public | Create normal user account |
| POST | /api/v1/accounts/login/ |
Public | User login |
| POST | /api/v1/accounts/token/refresh/ |
User authenticated | Refresh user access token |
| POST | /api/v1/accounts/logout/ |
User authenticated | User logout and clear cookies |
| GET | /api/v1/accounts/me/ |
User authenticated | Current user profile |
| POST | /api/v1/accounts/admin/login/ |
Public (must be superuser creds) | Admin login |
| POST | /api/v1/accounts/admin/token/refresh/ |
Public (with refresh token) | Admin access refresh |
| POST | /api/v1/accounts/admin/logout/ |
Public (with refresh token optional) | Admin logout |
| GET | /api/v1/accounts/admin/me/ |
User authenticated + superuser | Admin profile |
Signup:
POST /api/v1/accounts/signup/
Content-Type: application/json
{
"username": "alice",
"email": "alice@example.com",
"password": "StrongPass123!",
"first_name": "Alice",
"last_name": "Doe"
}User login:
POST /api/v1/accounts/login/
Content-Type: application/json
{
"username": "alice",
"password": "StrongPass123!"
}| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/v1/reports/ |
User authenticated | List current user reports (paginated) |
| POST | /api/v1/reports/ |
User authenticated | Create new pothole report |
| GET | /api/v1/reports/counts/ |
Public (scoped by user if authenticated) | Count reports by status |
| GET | /api/v1/reports/admin/all/ |
User authenticated + superuser | Admin list + high-severity zone analytics |
| GET | /api/v1/reports/nearby/ |
User authenticated | Nearby reports around coordinates |
| PATCH | /api/v1/reports/<report_id>/status/ |
User authenticated + superuser | Update report status |
| POST | /api/v1/reports/emergency/ |
Public | Send emergency notification email payload |
{
"title": "Large pothole near crossing",
"description": "Deep pothole causing traffic slowdown",
"latitude": 28.6139,
"longitude": 77.2090,
"pothole_severity": "high"
}Behavior notes:
- Duplicate protection: if another non-resolved/non-rejected report exists within 10m, API returns existing report instead of creating a new one.
- Road authority is inferred via OSM and notification is attempted via Brevo.
lat(required)lng(required)radius_km(optional)limit(optional, default 10, max 100)
Response includes:
warning:None/Moderate/Highcluster_countthreshold- nearest report rows with
distance_m
- Report status choices:
pendingin_progressresolvedrejected
- Pothole severity choices:
lowmediumhigh
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/v1/ml/detect/ |
Public | Synchronous pothole probability from uploaded image |
| POST | /api/v1/ml/detect/submit/ |
User authenticated | Queue asynchronous inference task |
| GET | /api/v1/ml/detect/status/<task_id>/ |
User authenticated | Poll task status/result for own task |
| GET | /api/v1/ml/detect/my-reports/ |
User authenticated | List user reports |
- Content type:
multipart/form-data - Form field:
image
Success response:
{
"pothole": true,
"confidence": 0.91
}- Content type:
multipart/form-data - Form fields:
image(required)latorlatitude(optional)lngorlongitude(optional)
Accepted response:
{
"status": "queued",
"task_id": "<celery-task-id>",
"message": "Image received"
}Status response states:
queuedrunningsuccesswithresult: { pothole, confidence }failedwitherror
sequenceDiagram
participant C as Client
participant A as Accounts API
C->>A: POST /accounts/login (username, password)
A-->>C: access + refresh in JSON
A-->>C: Set-Cookie access_token, refresh_token
C->>A: GET /accounts/me (Bearer or cookie)
A-->>C: user profile
C->>A: POST /accounts/token/refresh (refresh token)
A-->>C: new access_token
flowchart TD
U[Authenticated User] --> R1[POST /reports/]
R1 --> D{Existing report\nwithin 10m?}
D -- Yes --> E[Return existing report]
D -- No --> A[Infer road authority via OSM]
A --> S[Save report in DB]
S --> N[Send Brevo authority notification]
N --> O[Return report + notification_sent]
ADM[Admin User] --> L[GET /reports/admin/all/]
L --> C[Cluster analysis and high-severity zones]
ADM --> P[PATCH /reports/<id>/status]
P --> DB[(Update status/resolved_at)]
flowchart LR
C[Client] --> S1[POST /ml/detect/submit/]
S1 --> Q[Celery task queued in Redis]
Q --> W[Worker pulls task]
W --> M[Load ONNX model]
M --> I[Run inference]
I --> J[Update InferenceJob]
J --> K{pothole true?}
K -- Yes --> R[Create Report + PotholeReport]
K -- No --> X[No report created]
C --> P1[GET /ml/detect/status/<task_id>]
P1 --> J
flowchart TD
C[Admin Client] --> T[GET /dashboard/summary/]
T --> V{Bearer token?}
V -- Yes --> H[Validate header token]
V -- No --> CK[Validate access_token cookie]
H --> A{is_superuser?}
CK --> A
A -- No --> F[403 Forbidden]
A -- Yes --> G[Aggregate totals + 7-day trend]
G --> R[Return dashboard payload]
PitWatch includes AI-assisted pothole detection via ONNX model inference (sync and async ML endpoints).
- AI outputs are probabilistic and may produce false positives or false negatives.
- Detection confidence should be treated as decision support, not definitive proof.
- Administrative action and emergency handling should include human review.
- Model behavior can vary with image quality, lighting, weather, camera angle, and road context.
- The system does not provide legal, safety, or engineering guarantees.
- ML model lookup order:
pitwatch/models/pothole_model.onnx- fallback to
pitwatch/models/best.onnx
- PostGIS SQL functions are used directly (
ST_DWithin,ST_Distance), so DB must support them. DEFAULT_PERMISSION_CLASSESisIsAuthenticated; public endpoints explicitly override permission classes.
Run tests:
cd pitwatch
python manage.py testThere are API tests for ML route edge cases in pitwatch/ml/test_api.py.
- 401 on authenticated routes:
- Ensure
Authorization: Bearer <access>is present, or cookie is set.
- Ensure
- 503 on ML submit:
- Check Redis connectivity and running Celery worker.
- 503 or failed notification on reports/emergency:
- Verify
BREVO_API_KEYand sender config.
- Verify
- Geospatial query errors:
- Confirm PostgreSQL has PostGIS extension available.
AI tools were used to assist parts of this project's development and documentation. Final implementation decisions and validation were performed by myself.