Skip to content

Latest commit

 

History

28 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SwasthyaNet

Federated AI for national-scale health resource and supply-chain resilience.

SwasthyaNet is a hackathon MVP for improving visibility into medicine availability, bed occupancy, and staff attendance across a simulated Primary Health Centre (PHC) network in India. It turns local telemetry into explainable stockout forecasts, early warnings, and cross-district redistribution recommendations without sending raw local records to a central aggregator.

This is a synthetic demonstration, not a clinical or operational system. All names, coordinates, inventory, attendance, and forecasts are generated locally and should not be used for medical, procurement, staffing, or emergency decisions.

Quick start

The fastest path is Docker Compose:

docker compose up --build

Open http://localhost:5173 for the dashboard and http://localhost:8000/docs for the API explorer. The backend can also run directly:

cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload

The frontend is a Vite React application. If running it outside Docker, use npm install && npm run dev and set VITE_API_URL=http://localhost:8000.

Problem understanding

Public-health managers need a current, network-wide view of supplies and capacity, but PHCs are distributed local facilities with uneven connectivity, sensitive operational records, and different demand patterns. The hardest challenge is not rendering a dashboard; it is producing a useful network signal while preserving the boundary around local data. SwasthyaNet makes that trade-off explicit: local nodes retain raw snapshots, while the central layer receives aggregates and model updates.

The MVP deliberately focuses on six PHCs across Nashik, Pune, and Satara, 12 medicine SKUs, and 30 days of seeded history. The simulator advances every eight seconds and also exposes a deterministic manual Simulate update button so a judge can trigger the live moment reliably.

Architecture

flowchart LR
    A[PHC local simulator] --> B[Local snapshots]
    B --> C[Weighted moving-average forecast]
    B --> D[Alert engine]
    C --> D
    B --> E[Aggregate-only update]
    E --> F[Federated aggregator]
    C --> G[Surplus-deficit matcher]
    G --> H[Transfer recommendation]
    D --> I[FastAPI REST/WebSocket]
    H --> I
    F --> I
    I --> J[React dashboard]
Loading

The backend/app/services directory intentionally separates the core mechanisms. simulator.py creates realistic-but-synthetic daily fluctuations. forecasting.py, alerts.py, redistribution.py, and federation.py contain the inspectable AI and network logic. main.py exposes those services through stable API contracts.

Google AI (Gemini) integration

backend/app/services/ai_client.py calls the Gemini API directly (GEMINI_API_KEY env var) to generate three kinds of content on top of the statistical forecasting layer:

  • Forecast explanations (GET /api/forecasts/{phc_id}) — Gemini reads the recent consumption history and the statistical model's prediction, then writes a short plain-language explanation of the pattern and a risk level.
  • Redistribution reasoning (GET /api/redistribution/insights) — Gemini writes a one-sentence operational justification for each recommended inter-PHC transfer.
  • Multilingual district briefings (GET /api/insights/{phc_id}?lang=en|hi|mr) — Gemini generates a short executive summary in English, Hindi, or Marathi for a district health officer.

Every one of these calls has a transparent, automatic fallback: if GEMINI_API_KEY is unset, the request times out, or the response can't be parsed, the endpoint returns the underlying statistical/rule-based result instead and marks the response with "is_ai_generated": false and "method_used". The API never breaks because of an AI provider issue — this is verified by backend/tests/test_ai_integration.py.

Set GEMINI_API_KEY (and optionally GEMINI_MODEL, default gemini-1.5-flash) as an environment variable to enable live AI generation; see backend/.env.example.

All three of these are now surfaced directly in the frontend (frontend/src/main.tsx), not just the API:

  • The Explainable Forecast panel shows the Gemini-generated explanation and risk pill under the chart for the selected PHC's top medicine.
  • A new Gemini District Briefing panel generates a live executive summary for the selected PHC, with an EN / HI / MR language switch.
  • The Redistribution Engine panel shows Gemini's one-sentence justification for each recommended transfer (state-official view).
  • A floating SwasthyaNet Assistant chatbot (POST /api/chat, bottom-right on every page) answers free-form questions ("which PHC is most at risk right now?") using Gemini, grounded in a server-built summary of the live dashboard data the signed-in user is allowed to see.

Each of these renders a small badge — "Powered by Google Gemini" when the AI call succeeded, or the underlying fallback method name (e.g. "weighted_moving_average", "rule_based_template") when it didn't — so it's always transparent which content came from Gemini versus the statistical baseline. Check GET /api/health on your deployed backend (gemini_configured) to confirm your GEMINI_API_KEY is actually set before judging.

Rate-limit protection

The free Gemini tier only allows a handful of requests per minute, and the dashboard used to re-request AI content on every live simulation tick (every 8s) — enough to trip HTTP 429 errors within seconds. ai_client.py now:

  • Caches successful Gemini responses in-memory for GEMINI_CACHE_TTL_SECONDS (default 90s), so unchanged data doesn't re-call the API.
  • Enforces a shared call budget (GEMINI_MAX_CALLS_PER_MINUTE, default 10) across all Gemini call sites; once exhausted for the current minute, calls skip straight to the statistical/rule-based fallback instead of hitting the API and failing.
  • Retries once with a short backoff on an HTTP 429 before giving up gracefully.
  • The chat endpoint additionally rate-limits each signed-in user (CHAT_MAX_CALLS_PER_MINUTE, default 12) to stop one user's chat session from starving everyone else's quota.

On the frontend, the three Gemini-powered panels also moved off the fast 8-second live tick onto their own 60-second refresh clock, since dashboard occupancy changing every few seconds never needs a fresh AI narrative that often.

Security notes

  • All AI/chat endpoints require a valid signed-in session (Authorization: Bearer); the chat assistant's context is built server-side from data the authenticated user is already permitted to see (a PHC admin never sees another district's data), so a user can't inject fake "facts" through the chat input to manipulate what the model treats as ground truth.
  • The chat system prompt explicitly instructs Gemini to ignore in-message attempts to override its instructions, reveal the prompt, or go off-topic, and every response is capped in length before being sent back.
  • CORS defaults to permissive for a smooth hackathon deploy, but set CORS_ALLOWED_ORIGINS (comma-separated exact origins) in production; the API now also sends X-Content-Type-Options, X-Frame-Options, Referrer-Policy, and Permissions-Policy headers on every response.
  • Set a real, random SWASTHYANET_JWT_SECRET in your Render environment — the code ships a dev-only-change-this-secret fallback purely so local dev doesn't crash, and it must never be used in a deployed instance.
  • GEMINI_API_KEY is read only on the backend (ai_client.py); it is never sent to or exposed in the frontend bundle.
  • If any unexpected error occurs anywhere in the API (a bug, a bad edge case, a dependency hiccup), a middleware safety net returns a clean JSON {"detail": ...} response with security headers intact instead of letting a raw HTML/plaintext error page reach the frontend — this is what makes response.json() on the client reliable even on failure paths.

A note on Render's free tier

If your backend is on Render's free plan, it spins down after ~15 minutes of no traffic and takes 30–60s to wake up on the next request. The chat widget shows "Still working — the server may be waking up from idle…" if a reply takes more than 6 seconds, and times out with a clear message after 45s rather than hanging silently — but for a live judging demo, it's worth opening the site (or hitting /api/health) a minute or two beforehand so the backend is already warm.

Persistence

backend/app/services/db_manager.py persists the simulation snapshot (day, tick, and every PHC's inventory/history) to SQLite (DATABASE_PATH, default /tmp/swasthyanet.db) on every simulated day advance. On startup, the app restores the last saved snapshot instead of reseeding from scratch, so demo state survives a server restart or redeploy.

Core mechanism

For each medicine at each PHC, the forecast calculates a weighted moving average over the most recent seven daily consumption observations. Recent observations have higher weight. The projected stock curve is current quantity minus predicted daily use across a seven-day horizon, and days until stockout is current quantity divided by predicted daily use. This is simple, auditable, dependency-light, and easy to defend during a technical review.

The alert engine combines forecast and policy signals. It marks inventory when the stockout estimate is seven days or less or quantity is at/below the reorder threshold. It marks beds as a warning above 78% occupancy and critical above 90%. Attendance below 78% contributes a staffing warning. The recommendation engine matches a deficit to the nearest safe surplus, preferring the same district, then neighboring districts.

Federated/privacy representation

Each PHC is represented as a local node. Its raw inventory histories, staffing histories, and event-level values stay inside the simulation state. The federated_summary service shares only weighted occupancy, mean attendance, node count, district count, and a documented privacy-boundary statement. This is an architectural simulation of federated learning rather than a claim of secure production FL. A production deployment would add authenticated node identity, encrypted transport, secure aggregation, differential privacy, audit logs, and formal threat modeling.

Experimental evidence

The seeded scenario creates a known shortage of paracetamol at Rajapur PHC and a safe surplus at Sinnar PHC. Run the tests:

pytest backend/tests -q

The controlled claims are: the weighted forecast returns a non-empty predicted curve and positive demand; the alert engine detects the intentional Rajapur stock risk; and the recommender proposes a paracetamol transfer from Sinnar to Rajapur. The exact values are deterministic under seed 42, so judges can reproduce the central technical moment rather than relying on random luck.

Resilience and failure behavior

The simulation is intentionally self-contained and requires no real hospital integration or external map tile. The map-like visualization uses synthetic coordinates and approximate haversine distance, so it remains usable offline. A manual tick is available if the periodic feed is inconvenient during judging. The frontend shows a loading state while the API is unavailable rather than rendering fabricated data. In a production version, the next resilience steps would be durable event storage, per-node connectivity status, retry queues, stale-data timestamps, and a clear degraded-mode banner.

Production path and real-world impact

A deployment could replace SimulationState with adapters for state health inventory systems, DHIS2, facility registries, and authenticated staff/bed feeds. The API and service contracts would remain stable. PHCs could submit signed local summaries during intermittent connectivity; the state layer could prioritize transfers using lead time, cold-chain requirements, expiry dates, and road conditions. Clinical governance, procurement policy, human review, and a full privacy impact assessment would be required before operational use.

Two-minute judge demo

  1. Open the dashboard and point out the Synthetic Dataset banner and five network KPIs.
  2. Click the red or yellow Rajapur node on the command view. Explain that status is computed from capacity, attendance, and supply alerts.
  3. In the forecast panel, show the stock trajectory, weighted moving-average method, daily use, stockout ETA, and confidence.
  4. In the alert feed, point to the Rajapur stock-risk event and explain the threshold-plus-forecast rule.
  5. Scroll to Recommended transfers and show Sinnar’s safe paracetamol surplus matched to Rajapur, including quantity, priority, and approximate route distance.
  6. Finish at Federated learning boundary: six local nodes send aggregate updates to the state layer; raw histories stay local. Press Simulate update to demonstrate the live-style tick.

Alternatives and limitations

A moving average was chosen over Prophet/ARIMA because it installs quickly, is deterministic for a hackathon, and is straightforward to audit. PostgreSQL can replace the in-memory simulation through the repository boundary, but SQLite or memory is more reliable for a one-command demo. Flower was not required because the important judging differentiator is the privacy architecture and visible aggregate boundary; the service is intentionally designed so a Flower client/aggregator can be inserted later.

Repository map

backend/app/services/simulator.py   synthetic data and live tick
backend/app/services/forecasting.py  weighted moving-average forecast
backend/app/services/alerts.py       threshold and forecast alerts
backend/app/services/redistribution.py distance-aware transfer matching
backend/app/services/federation.py   aggregate-only network metrics
backend/app/main.py                 FastAPI and WebSocket API
backend/tests/test_ai.py            core mechanism tests
frontend/src/main.tsx               dashboard composition and data flow
frontend/src/style.css              visual system and responsive layout
docker-compose.yml                  one-command demo

References

The implementation baseline is the hackathon brief supplied by the project owner. Production integration with DHIS2, secure federated learning, authentication, and clinical governance is intentionally left as a future engineering and policy phase rather than represented as completed functionality.

Authentication and role-based access

The dashboard now requires authentication. Two demo roles are available:

Role Username Demo password Access
State official state.official State@2026 Network-wide PHC dashboard, alerts, redistribution recommendations, and simulation controls
PHC administrator rajapur.admin Rajapur@2026 Rajapur PHC data only; cannot access other PHCs or advance the global simulation

The backend issues signed bearer sessions through POST /api/auth/login. Protected routes validate the signature, expiry, user identity, and role. PHC administrators are scoped to their assigned PHC, while state officials can view the network. The frontend persists the session token locally, adds it to API requests, provides role-aware controls, and supports sign out.

For Render, add this environment variable under the backend service:

SWASTHYANET_JWT_SECRET=<long-random-secret>

Generate a value locally with:

python3 -c "import secrets; print(secrets.token_urlsafe(48))"

Never commit the secret or place it in Vercel. The frontend only needs VITE_API_URL; the JWT signing secret must remain server-side in Render. The built-in demo accounts are suitable for judging only. A real deployment should replace them with an identity provider, hashed credentials in a secure store, refresh-token rotation, audit logs, and stricter CORS origins.

Outbreak trends and reporting

The dashboard includes a clearly labeled synthetic disease-signal panel with interactive disease selection and 8-week/16-week windows. The backend endpoint is GET /api/outbreaks, and it is scoped to the authenticated viewer: state officials see the network while PHC administrators see their assigned facility.

State officials can download state-level reports directly from the dashboard. CSV reports are available at GET /api/reports/state.csv, and formatted PDF reports are available at GET /api/reports/state.pdf. Both endpoints require a state-official bearer session and include PHC capacity, staffing, alerts, and weekly disease-signal summaries. PHC administrators are intentionally denied these state-level exports.

The automatic logout issue was caused by the eight-second refresh interval retaining the pre-login empty token in its closure. The refresh effect now depends on the current token and is disabled until a valid authenticated session exists, so subsequent dashboard refreshes continue sending the active bearer token.

Live outbreak notifications and regional map

The dashboard now surfaces a real-time-style critical disease notification banner whenever the authenticated outbreak feed reports a critical regional signal. The banner includes the affected PHC, district, disease, synthetic report count, and a dismiss control. It is refreshed with the authenticated eight-second dashboard cycle and is intentionally labeled as synthetic.

The PHC command view now uses an interactive Leaflet map with pan, zoom, PHC markers, status colors, outbreak-intensity sizing, and clickable popups. Popups show the selected disease signal, district, regional risk, and bed occupancy. The selected disease in the outbreak trends panel also changes the map’s regional visualization.

The authenticated GET /api/outbreaks response now includes:

trends
regional
critical_alerts
summary

The regional map uses OpenStreetMap tiles for geography and synthetic PHC coordinates for the demo. If external tiles are unavailable, the surrounding dashboard and underlying data remain usable.

Multi-page operations console

The frontend now provides four client-side operations pages with hash URLs so each view can be opened directly and shared during a demo:

Page Route Purpose
Overview #/overview Network KPIs, PHC map, alerts, and executive command summary
Outbreak intelligence #/outbreaks Disease trends, interactive regional map, critical signals, and alert feed
Supply logistics #/logistics PHC map, inventory forecast, live capacity pulse, and transfer recommendations
Federation & reports #/governance Privacy boundary, state reporting, and PDF/CSV export controls

A new live capacity pulse chart compares rolling bed occupancy and staff attendance for the selected PHC. All live-style charts refresh with the authenticated simulation cycle and remain labeled as synthetic telemetry.

The login screen now uses the clearer message: Demo accounts are provided for this demonstration.

Predictive outbreak forecasting

The outbreak intelligence page now includes an explainable four-week projection for the selected disease. The service fits a deterministic least-squares trend over the latest six synthetic weekly observations and returns direction, slope, next-week estimate, four-week projected total, and a bounded confidence score. The chart separates observed reports from dashed predicted reports and displays the model explanation beside the visualization. These projections are synthetic planning signals, not clinical diagnoses or epidemiological forecasts.

Dashboard typography has also been increased across navigation, chart legends, panel labels, body copy, alerts, controls, authentication fields, and report actions. The compact layout and responsive behavior are preserved while improving readability on normal desktop and mobile screens.

Custom outbreak date ranges and visual assets

The outbreak intelligence page supports both the existing 8-week and 16-week shortcuts and a validated custom calendar range. Custom ranges must be between 14 and 365 days and are passed to the authenticated GET /api/outbreaks?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD endpoint. The selected range updates the observed series, summary window, and four-week predictive context. The implementation uses local calendar formatting so date controls remain correct across time zones.

The frontend includes two restrained open-source unDraw SVG illustrations in frontend/public/assets: medical-care.svg and dashboard.svg. Their source pages and intended use are recorded in frontend/public/assets/ASSET_SOURCES.md. Operational charts remain data-driven; the illustrations are decorative context only and do not represent health statistics.

Public landing and sign-in experience

The application now opens to a public #/landing experience when no session is present. The page introduces the operational problem, explains the Observe → Predict → Coordinate workflow, presents three healthcare/analytics visuals, and routes users to #/login through the Get started CTA. The login view uses a responsive split layout with a large healthcare illustration and explanatory copy on the left and the secure role-based sign-in form on the right. The password field includes an accessible visibility toggle so users can reveal or hide the entered password.

The landing illustrations are sourced from official unDraw pages and are stored locally in frontend/public/assets so the deployed dashboard does not depend on remote image hosting. Source details remain in frontend/public/assets/ASSET_SOURCES.md.

All-pages dark mode

Light and dark themes are now available consistently on the public landing page, split login page, and authenticated command center. The shared theme control uses an animated icon transition, smooth surface/color transitions, dark-mode-specific illustration treatment, and a persisted browser preference so the selected theme survives navigation and reloads. The login and landing pages expose the same toggle before authentication; the command center exposes it in the authenticated header.

Automated end-to-end testing

The frontend includes a Playwright browser suite in frontend/e2e/app.spec.ts and its configuration in frontend/playwright.config.ts. The suite starts or reuses the FastAPI and Vite services, then verifies the public landing-page CTA, light/dark theme switching and persistence, password visibility, state-official authentication, navigation across all dashboard pages, logout, and PHC-administrator scoping.

From frontend, install the browser once and run the suite with:

npm install
npx playwright install chromium
npm run test:e2e

The HTML report can be opened locally with:

npx playwright show-report

The tests use the existing demonstration accounts and local endpoints only; no production credentials are stored in the test files.

About

SwasthyaNet — a federated resilience dashboard for rural Primary Health Centres, powered by Google Gemini for explainable stockout forecasts, multilingual district briefings, redistribution reasoning, and an in-app AI assistant. Built for the 'Google Cloud hackathon' BRICS challenge.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages