🔗 Live demo: https://smartpark-its.onrender.com · API docs:
/api/docsDemo admin login —admin@parking.com/admin123. Hosted on a free tier, so the first request may take ~30–60s to wake the instance.
SmartPark ITS is an end-to-end smart-parking platform: drivers discover live slot availability, get demand-based dynamic pricing, reserve or book a slot (with a strict concurrency guarantee), and receive a QR-verifiable digital receipt. Admins get a real-time analytics dashboard, slot management, and full system auditing. The whole thing is observable, tested, containerized, and shipped through CI/CD.
It is intentionally engineered to production standards — not as a CRUD demo, but as something you can defend, end-to-end, in a senior-level system-design conversation.
| Area | What it does |
|---|---|
| ⚡ Real-time | Slot status pushed to every client over WebSockets (Socket.IO + Redis pub/sub), with graceful polling fallback. |
| 🔒 Concurrency-safe booking | Double-booking is impossible by construction — a partial unique DB index + row-level locking + transactional writes (proven by a 50-thread Postgres test). |
| 🗺️ Geospatial map | Multi-lot support with coordinates; a live Leaflet map shows availability per lot and finds the nearest lot to your location (Haversine; PostGIS-ready). |
| 💳 Payments | Razorpay checkout for the parking fee, with HMAC-SHA256 webhook signature verification and idempotency (mock provider for local dev). |
| 📷 ANPR | License-plate recognition (OpenCV detection + Tesseract OCR) auto-fills the vehicle number from a photo. |
| 💸 Dynamic pricing | Surge multiplier ramps with live occupancy (configurable threshold & cap). |
| 🧠 Occupancy forecasting | Exponentially-weighted hour-of-week demand model → 24h forecast + cheapest-slot recommendations. |
| ⏳ Reservations | Hold a slot with a TTL; a Celery beat job auto-releases expired holds. |
| 🧾 QR receipts | Each completed session yields a scannable, verifiable receipt. |
| 📊 Analytics | Revenue trend, peak-hour histogram, vehicle mix, live occupancy (Chart.js). |
| 🔭 Observability | Structured JSON logs w/ request IDs, Sentry, Prometheus /metrics, /healthz, Grafana dashboards + alert rules. |
| 🔌 REST API | Versioned /api/v1, JWT-authenticated, self-documenting Swagger UI. |
| 🛡️ Hardened auth | Hashing, CSRF, rate limiting, login lockout, secure cookies, 12-factor config. |
| ✅ Tested + load-tested | pytest unit + integration (incl. a real-Postgres concurrency proof), ~88% coverage gate in CI, Locust load tests (0% errors, p95 43ms @ 80 users). |
flowchart TB
subgraph Clients
BR["🌐 Browser<br/>Jinja + Socket.IO"]
AP["🔌 API clients<br/>JWT"]
end
subgraph Edge["Gunicorn · gevent WebSocket workers"]
FL["⚙️ Flask app factory · WhiteNoise<br/><b>Blueprints</b> public · auth · user · admin · api/v1 · ops<br/><b>Services</b> booking · slot · pricing · forecast · payment · anpr · geo"]
end
subgraph Data["Data & Infra"]
PG[("🐘 PostgreSQL<br/>+ Alembic")]
RD[("🧠 Redis<br/>cache · pub/sub · rate-limit · queue")]
end
subgraph Async["Background"]
CW["🔧 Celery worker"]
CB["⏰ Celery beat<br/>RedBeat"]
end
subgraph Obs["Observability"]
PM["📈 Prometheus"]
GF["📊 Grafana"]
SN["🛰️ Sentry"]
end
BR -->|HTTP / WebSocket| FL
AP -->|/api/v1 JWT| FL
FL --> PG
FL <-->|live slot push| RD
CW --> PG
CW --> RD
CB --> RD
FL --> PM --> GF
FL --> SN
Layout
app/
├── __init__.py # application factory
├── extensions.py # db, migrate, csrf, cache, limiter, socketio
├── models.py # ORM: User, ParkingSlot, Booking (+ partial unique indexes)
├── security.py # auth, guards, lockout
├── logging_config.py # structured JSON logs + request IDs
├── observability.py # Sentry, Prometheus, /healthz, /metrics
├── realtime.py # Socket.IO events + broadcast
├── tasks.py # Celery tasks + beat schedule
├── cli.py # `flask seed`, `flask create-admin`
├── services/ # booking · slot · pricing · analytics · forecast · notification
└── blueprints/ # public · auth · user · admin · api/v1 · errors
wsgi.py · celery_worker.py · config.py · migrations/ · tests/ · docker-compose.yml
Three ways to run it — pick one:
| Mode | What to do | Stack |
|---|---|---|
| 🌐 Live demo | Just open smartpark-its.onrender.com — nothing to install | Render + Neon + Upstash |
| ⚡ Local — quick | python wsgi.py → http://localhost:5000 |
SQLite + in-memory (zero infra) |
| 🐳 Local — full | docker compose up --build → http://localhost:5000 |
Postgres + Redis + Celery |
Admin login (all modes):
admin@parking.com/admin123. The live free instance sleeps when idle — the first request takes ~30–60s to wake, then it's fast.
# A SECRET_KEY is required (compose refuses to start without one — by design).
export SECRET_KEY=$(python -c "import secrets; print(secrets.token_urlsafe(48))") # Windows PowerShell: $env:SECRET_KEY=...
docker compose up --build
# → http://localhost:5000 (admin: admin@parking.com / admin123)The web container runs Gunicorn with a gevent WebSocket worker (native WebSockets + high connection concurrency); emits fan out across workers via the Redis message queue.
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
flask --app wsgi seed # create schema + demo data
python wsgi.py # → http://localhost:5000With no
DATABASE_URL/REDIS_URLset, the app falls back to SQLite + in-memory cache so it boots with zero configuration. Set them (see .env.example) to use Postgres/Redis. In production (FLASK_ENV=production) the app refuses to start without a realDATABASE_URLandREDIS_URL— it never silently runs on SQLite.
Ship to a public URL on free tiers (Render + Neon + Upstash) — see DEPLOY_FREE.md.
- Swagger UI:
/api/docs· OpenAPI spec:/api/v1/openapi.json - Auth:
POST /api/v1/auth/login→{ access_token }, thenAuthorization: Bearer <token>
TOKEN=$(curl -s localhost:5000/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@parking.com","password":"admin123"}' | jq -r .access_token)
curl localhost:5000/api/v1/slots
curl -X POST localhost:5000/api/v1/bookings -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"slot_id":1,"vehicle_number":"KA01AB1234"}'| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/v1/slots |
GET | – | Live availability + counts |
/api/v1/slots/recommend |
GET | – | Cheapest available slots |
/api/v1/forecast |
GET | – | 24h occupancy forecast |
/api/v1/bookings |
GET/POST | JWT | List / create bookings |
/api/v1/bookings/{id}/exit |
POST | JWT | Exit + receipt |
/api/v1/analytics |
GET | JWT (admin) | KPIs + chart series |
/api/v1/lots · /api/v1/lots/nearest |
GET | – | Lots w/ availability · nearest to a point |
/api/v1/anpr |
POST | – | License-plate recognition (multipart image) |
/api/v1/auth/register · /auth/login |
POST | – | Get a JWT |
/api/webhooks/razorpay |
POST | HMAC | Payment webhook (signature-verified) |
/healthz · /metrics |
GET | – | Liveness · Prometheus |
Two drivers tapping "Book A1" at the same instant cannot both succeed:
sequenceDiagram
participant A as Driver A
participant B as Driver B
participant S as booking_service
participant DB as PostgreSQL
par Simultaneous requests
A->>S: POST /book (slot 1)
and
B->>S: POST /book (slot 1)
end
S->>DB: SELECT … FOR UPDATE (slot 1)
Note over DB: row locked — B's txn waits
S->>DB: INSERT active booking (A)
DB-->>S: OK · partial unique index satisfied
S-->>A: 201 Booked ✅
S->>DB: INSERT active booking (B)
DB-->>S: IntegrityError · unique index violated
S-->>B: 409 "that slot was just taken" 🚫
SELECT … FOR UPDATElocks the slot row (PostgreSQL), serializing contenders.- A partial unique index is the hard backstop:
The second writer gets an
CREATE UNIQUE INDEX uq_active_booking_per_slot ON bookings (slot_id) WHERE status = 'active';
IntegrityError, which the service translates into a clean "that slot was just taken."
This is proven by a test that bypasses the service layer and asserts the database itself rejects a second active booking (tests/test_booking.py::test_db_constraint_blocks_two_active_bookings).
Engineered to scale horizontally by configuration, not rewrites:
| Concern | How it's handled |
|---|---|
| WSGI / WebSockets | Gunicorn gevent WebSocket worker — native WebSockets + thousands of connections/worker. WEB_CONCURRENCY workers fan out via the Redis message queue (use a sticky-session LB across instances). |
| DB connection pool | Tuned Postgres pool (pool_size/max_overflow/pool_timeout + pre_ping/recycle), env-tunable; pair with a pooled endpoint / PgBouncer at scale. |
| Read-heavy availability | Short-TTL Redis cache on the hot availability read, invalidated in the service layer on every booking / reservation / slot change. |
| Fail-fast config | In production the app refuses to boot without DATABASE_URL + REDIS_URL and rejects a SQLite fallback or an insecure SECRET_KEY — no silent data-loss misconfigs. |
| Background jobs | Celery tasks with retry/back-off + broker reconnection; RedBeat (Redis-backed) scheduler survives restarts. |
| Static assets | Served by WhiteNoise with long-lived cache headers, off the request workers. |
| Health | /healthz verifies Postgres (hard) and Redis (reported) so a load balancer sees real dependency state. |
Sizing guidance and a free-tier deploy are in DEPLOY_FREE.md.
pytest # unit + integration, coverage gate (≥85%)
locust -f loadtest/locustfile.py ... # load test (see loadtest/README.md)
docker compose up # app + db + redis + worker + beat + prometheus + grafana- ~88% coverage; suite covers auth/lockout, the concurrency guarantee, pricing, forecasting, geo, payments (signature + idempotency), ANPR, the REST API, and admin.
- Integration tests spin a real PostgreSQL (testcontainers) and prove 50 concurrent bookers racing one slot → exactly one wins.
- Load test baseline: 0% errors, p95 43ms @ 80 users (loadtest/README.md).
- Observability: Grafana dashboards + Prometheus alert rules under
monitoring/(Grafana :3000, Prometheus :9090 via compose). - Design docs: system design + ADRs.
- The Jenkins pipeline gates on tests, then SonarCloud → Trivy → Docker → push → deploy:
flowchart LR
G["📥 GitHub push"] --> T["✅ pytest<br/>coverage gate"]
T --> S["🔍 SonarCloud"]
S --> V["🛡️ Trivy scan"]
V --> D["🐳 Docker build"]
D --> H["📦 Docker Hub"]
H --> R["🚀 Render deploy"]
| Layer | Technologies |
|---|---|
| Backend | Python 3.11 · Flask 3 (app factory + blueprints) · SQLAlchemy 2 · Alembic |
| Data & Infra | PostgreSQL 16 · Redis 7 · Celery (worker + beat) |
| Realtime & API | Flask-SocketIO (gevent WebSocket worker) · Flask-JWT-Extended · OpenAPI / Swagger |
| Security | Werkzeug password hashing · Flask-WTF (CSRF) · Flask-Limiter · login lockout |
| Payments & Vision | Razorpay (HMAC-verified webhooks) · OpenCV + Tesseract (ANPR) |
| Geospatial | Leaflet + OpenStreetMap · Haversine distance (PostGIS-ready) |
| Serving & Scale | Gunicorn (gevent WebSocket worker) · WhiteNoise · RedBeat · tuned SQLAlchemy pool |
| Observability | Structured JSON logging · Sentry · Prometheus · Grafana |
| Testing | pytest · pytest-cov · testcontainers · Locust |
| Frontend | Jinja2 · vanilla JS · Chart.js · Socket.IO client · custom dark UI (Space Grotesk / DM Sans) |
| DevOps & CI/CD | Docker (multi-stage, non-root) · docker-compose · Jenkins · SonarCloud · Trivy · Render |
MIT — see LICENSE.