{"id":50310189,"url":"https://github.com/opesam42/sicklecare","last_synced_at":"2026-05-28T20:30:21.978Z","repository":{"id":353126344,"uuid":"1201248265","full_name":"opesam42/sicklecare","owner":"opesam42","description":"Predictive health-tech platform for Sickle Cell crisis detection. Built with FastAPI \u0026 SQLModel. Features a data-driven Stability Index \u0026 Offline QR Triage. 4th Place @ HSIL 2026 Lagos.","archived":false,"fork":false,"pushed_at":"2026-05-11T10:34:33.000Z","size":679,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T12:38:57.993Z","etag":null,"topics":["fastapi","hackathon-project","healthcare-application"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/opesam42.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-04T12:17:45.000Z","updated_at":"2026-04-14T16:03:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/opesam42/sicklecare","commit_stats":null,"previous_names":["opesam42/sicklecare"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/opesam42/sicklecare","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opesam42%2Fsicklecare","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opesam42%2Fsicklecare/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opesam42%2Fsicklecare/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opesam42%2Fsicklecare/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/opesam42","download_url":"https://codeload.github.com/opesam42/sicklecare/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opesam42%2Fsicklecare/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33626136,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastapi","hackathon-project","healthcare-application"],"created_at":"2026-05-28T20:30:20.425Z","updated_at":"2026-05-28T20:30:21.914Z","avatar_url":"https://github.com/opesam42.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🛡️ SickleShield (SickleCare)\n\n\u003e **Real-time Physiological Monitoring \u0026 Crisis Prediction for Sickle Cell Management.**\n\u003e *Awarded 4th Place — HSIL Hackathon 2026, Lagos Hub*\n\nSickleShield is a proactive health-tech system designed to close the gap between daily biometric monitoring and emergency clinical intervention for Sickle Cell patients. By continuously analysing Heart Rate Variability (HRV), ambient temperature, hydration levels, and urine colour, the system computes a **Stability Index** to detect the *prodromal phase* of a Vaso-Occlusive Crisis (VOC) — up to 72 hours before collapse.\n\n---\n\n## 📋 Table of Contents\n\n- [The Mission](#-the-mission)\n- [How It Works](#-how-it-works)\n- [System Architecture](#-system-architecture)\n- [Key Features](#-key-features)\n- [Demo Patients](#-demo-patients)\n- [Tech Stack](#-tech-stack)\n- [Project Structure](#-project-structure)\n- [Installation \u0026 Setup](#-installation--setup)\n- [API Reference](#-api-reference)\n- [Data Models](#-data-models)\n- [ML Model](#-ml-model--isolation-forest)\n- [Frontend Architecture](#-frontend-architecture)\n- [Technical Debt \u0026 Hackathon Bypasses](#-technical-debt--hackathon-bypasses)\n- [Future Roadmap](#-future-roadmap)\n- [Team](#-team)\n- [Legal Disclaimer](#-legal-disclaimer)\n\n---\n\n## 🚀 The Mission\n\nIn the Nigerian healthcare context, Sickle Cell patients (HbSS, HbSC genotypes) frequently arrive at hospitals already in full-blown Vaso-Occlusive Crisis. Emergency rooms have no prior context about the patient's baseline physiology, genotype, or recent biometric trajectory.\n\n**SickleShield** addresses three failure points:\n\n| Problem | Our Response |\n|---|---|\n| Crisis detected too late | Isolation Forest anomaly detection on continuous wearable data |\n| Clinic has no context at ER | Offline-first QR triage page — works in airplane mode |\n| Patient can't self-assess | 4-category pain scale + urine colour wheel with heuristic override |\n\n**The 72-hour window** is the core thesis: physiological signals (HRV decline, dehydration, environmental cold fronts) consistently precede crisis onset. SickleShield monitors these signals daily and fires SMS alerts the moment the Stability Index crosses a critical threshold — before the patient knows they are in crisis.\n\n---\n\n## ⚙️ How It Works\n\n```\nPatient logs daily                 Wearable auto-populates\n(urine, hydration, pain)           (HRV, HR, steps, RMSSD)\n         │                                    │\n         └──────────────┬─────────────────────┘\n                        ▼\n              POST /api/submit-log/\n                        │\n                        ▼\n         ┌─────────────────────────────┐\n         │  Heuristic Rule Engine      │\n         │  dark_amber_urine → bypass  │\n         │  pain ≥ 6 → exclude baseline│\n         └────────────┬────────────────┘\n                      │\n          ┌───────────┴──────────────┐\n          │ Heuristic trigger?        │\n         YES                         NO\n          │                          │\n          ▼                          ▼\n    CRITICAL (instant)     Isolation Forest model\n    risk_level = CRITICAL  anomaly_score → risk_level\n          │                          │\n          └───────────┬──────────────┘\n                      ▼\n             Stability Index (0.00–1.00)\n             0.00 = CRITICAL  1.00 = STABLE\n                      │\n          ┌───────────┴──────────────┐\n          │ Is crisis?                │\n         YES                         NO\n          │                          │\n          ▼                          ▼\n   Twilio SMS (patient      Dashboard updates\n   + clinic phone)          5-second polling\n```\n\n---\n\n## 🏗️ System Architecture\n\nThe system follows a **Backend-First Architecture**, prioritising data integrity and high-concurrency biometric polling.\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                        Frontend (PWA)                           │\n│  Alpine.js · Tailwind CSS · IBM Plex Sans/Mono                  │\n│  Pages: /log  /dashboard  /qr  /triage/{id}                     │\n│  All data fetched client-side via API.getPatient() + polling    │\n└────────────────────┬────────────────────────────────────────────┘\n                     │  HTTP/REST\n┌────────────────────▼────────────────────────────────────────────┐\n│                     FastAPI (Python 3.12+)                       │\n│  Routers:                                                        │\n│  /api/auth       — login, get-patient                           │\n│  /api/submit-log — log ingestion + ML inference                 │\n│  /apistatus      — stitched patient status (5s polling)         │\n│  /api/dashboard  — sparkline, SMS status                        │\n│  /api/qr         — QR generation, wallet card                   │\n│  /demo/{state}   — demo state machine                           │\n│  Template routes — thin Jinja2 shell renderers                  │\n└────────────────────┬────────────────────────────────────────────┘\n                     │  SQLModel ORM\n┌────────────────────▼────────────────────────────────────────────┐\n│                    PostgreSQL                                    │\n│  Tables: patients · daily_logs · analysis_results               │\n│          baseline_snapshots                                      │\n└─────────────────────────────────────────────────────────────────┘\n                     │\n        ┌────────────┴────────────┐\n        │                         │\n┌───────▼───────┐         ┌───────▼───────┐\n│  diskcache    │         │  Twilio SMS   │\n│  Poll results │         │  Patient +    │\n│  + weather    │         │  Clinic alert │\n└───────────────┘         └───────────────┘\n```\n\n---\n\n## ✨ Key Features\n\n### 1. Stability Index (ML Engine)\n\nThe core signal. Computed on every log submission from 10 biometric features:\n\n| Feature | Source | Clinical Meaning |\n|---|---|---|\n| `hrv_sdnn` | Wearable | Total autonomic resilience |\n| `rmssd` | Wearable | Parasympathetic withdrawal |\n| `hr_mean` | Wearable | Cardiac load |\n| `steps` | Wearable | Activity context |\n| `hydration_score` | Self-report | Primary VOC trigger |\n| `urine_color` | Self-report | Haemolysis marker |\n| `pain_level` | Self-report | Crisis threshold gate |\n| `body_temp` | Wearable | Internal stress |\n| `environment_temp` | Lagos Weather API | Cold front multiplier |\n| `HRV_deviation` | Computed | Delta vs 7-day baseline |\n\nScore range: `1.00` (perfectly stable) → `0.00` (immediate crisis).\n\n**Heuristic Override:** `dark_amber_urine` bypasses the ML entirely and fires `CRITICAL` immediately. Haemolysis cannot wait for a model confidence score.\n\n**Baseline exclusion:** Logs with `pain_level ≥ 6` (Moderate/Severe) are excluded from the 7-day baseline calculation, preventing crisis-day readings from polluting the patient's healthy profile.\n\n### 2. Emergency QR \u0026 Offline Triage\n\nEvery patient has a QR code that links to `/triage/{patient_id}` — a server-rendered Jinja2 page with **zero JavaScript and zero CDN dependencies**. It displays:\n\n- Patient name, genotype (HbSS/HbSC), comorbidities\n- 7-day biometric log table with crisis days highlighted\n- Current anomaly score + method badge\n- Baseline snapshot (HRV avg, hydration avg, HR avg)\n- Last crisis event timestamp\n\nDesigned to load in hospital basements. The QR page also generates a downloadable wallet-size PDF (html2canvas + jsPDF, auto-sized to content — no clipping).\n\n### 3. Smart SMS Alerts (Twilio)\n\nTriggered when `is_crisis = True`:\n- **MEDIUM risk:** Patient SMS only\n- **CRITICAL risk:** Patient SMS + Clinic SMS (dual-channel)\n\nSMS payload includes patient name, risk level, stability score, and a shortened triage URL.\n\n### 4. Wearable Integration\n\nContinuous 5-second polling via `/apistatus/latest` (stitched endpoint) populates:\n- HRV SDNN, RMSSD (from Apple Watch / Xiaomi Mi Fitness)\n- Heart rate, steps (BLE sync)\n- Environmental temperature (Visual Crossing Weather API, Lagos)\n\nWearable fields are **display-only** in the log form — the patient cannot edit them. `buildPayload()` reads directly from `$store.vitals`.\n\n### 5. Dashboard — Mission Control\n\nReal-time dashboard with 5-second auto-refresh:\n- **Risk banner** — pulses on CRITICAL\n- **2×2 score card grid** — stability score, risk level, HRV today, vs-baseline delta\n- **7-day sparkline** — bar chart coloured by daily risk level\n- **Triggered flags strip** — fadein on escalation\n- **SMS status rows** — live delivery confirmation\n- **QR button** — visible only on MEDIUM/CRITICAL, pulses on CRITICAL\n- **Demo state switcher** — Ada (stable) / Chidi (weather) / Ngozi (critical VOC)\n\n---\n\n## 👥 Demo Patients\n\nThree seeded patients covering all three clinical states:\n\n| State | Patient | Genotype | UUID (first 8) |\n|---|---|---|---|\n| `stable` | Ada Okonkwo | HbSC | `12cac1f5` |\n| `weather_triggered` | Chidi Eze | HbSS | `6f2b4d3b` |\n| `critical_voc` | Ngozi Adeyemi | HbSS + Asthma | `7df78b03` |\n\n**Ngozi's demo scenario:** Dark amber urine detected → heuristic override fires → `CRITICAL` regardless of ML score → dual SMS fired to patient + clinic → emergency QR shows \"Haemolysis likely.\"\n\nAccess demo states at `/dashboard?patient=stable`, `/dashboard?patient=weather_triggered`, `/dashboard?patient=critical_voc`.\n\n---\n\n## 🛠️ Tech Stack\n\n| Layer | Technology | Why |\n|---|---|---|\n| Backend | FastAPI (Python 3.12+) | Async event loop, auto OpenAPI docs, speed |\n| ORM | SQLModel (SQLAlchemy + Pydantic) | Type-safe models, single source of truth |\n| Database | PostgreSQL | JSONB for flags/metadata, relational integrity |\n| ML | Isolation Forest (scikit-learn, joblib) | Unsupervised — no labelled crisis data needed |\n| Cache | diskcache | Zero-config, filesystem-backed poll cache |\n| Frontend | Alpine.js 3.14 + Tailwind CSS CDN | Low-JS, fast on low-end Nigerian mobile devices |\n| Fonts | IBM Plex Sans + IBM Plex Mono | Medical/clinical aesthetic, Google Fonts |\n| Templates | Jinja2 | Thin shell renders — all data fetched by JS |\n| SMS | Twilio | Programmable SMS, Nigerian numbers supported |\n| QR | qrcode[pil] | Python-native QR generation, ERROR_CORRECT_M |\n| PDF | html2canvas + jsPDF (CDN) | Auto-sized to content — zero clipping |\n| Weather | Visual Crossing API | Lagos ambient temperature enrichment |\n| Deploy | Render | Free tier, PostgreSQL add-on |\n\n---\n\n## 📁 Project Structure\n\n```\nsickleshield/\n├── src/\n│   ├── main.py                    # FastAPI app, lifespan, CORS, router registration\n│   ├── config.py                  # Pydantic settings (DATABASE_URL, API keys)\n│   ├── database.py                # Engine, session factory, SessionDep\n│   ├── logger.py                  # Shared logging config\n│   ├── cache.py                   # diskcache setup\n│   ├── ml_model.py                # Isolation Forest load/unload + feature map\n│   ├── models/\n│   │   └── models.py              # Patient, DailyLog, AnalysisResult, BaselineSnapshot\n│   ├── schemas/\n│   │   └── schema.py              # LoginRequest, LoginResponse, Pydantic payloads\n│   ├── routers/\n│   │   ├── api.py                 # /api/submit-log/, /apistatus/latest, /demo/*\n│   │   ├── auth.py                # /api/auth/login, /api/auth/get-patient\n│   │   ├── dashboard_router.py    # /api/dashboard/sparkline, /api/dashboard/sms-status\n│   │   └── qr_router.py          # /api/qr/data, /api/qr/wallet-card\n│   ├── templates.py               # Thin Jinja2 template routes\n│   └── services.py                # ML inference, heuristic engine, SMS dispatch\n├── templates/\n│   ├── base.html                  # Alpine.js load order, Tailwind config, script slots\n│   ├── _header.html               # Shared app header partial (drawer + stats strip)\n│   ├── login.html                 # Auth page\n│   ├── log.html                   # 3-step daily log form (urine → hydration → pain)\n│   ├── dashboard.html             # Mission control dashboard\n│   ├── qr.html                    # Emergency QR + wallet card PDF download\n│   └── triage.html                # Offline ER triage (zero JS, zero CDN)\n├── static/\n│   ├── script/\n│   │   ├── components/\n│   │   │   ├── config.js          # BASE_URL (localhost vs production)\n│   │   │   ├── vitals.js          # $store.vitals + vitalsPoller (5s polling)\n│   │   │   ├── log.js             # logForm Alpine component\n│   │   │   ├── dashboard.js       # dashboardApp Alpine component\n│   │   │   ├── login.js           # loginForm Alpine component\n│   │   │   ├── urine-wheel.js     # urineWheel Alpine component\n│   │   │   └── hydration-log.js   # hydrationLog Alpine component\n│   │   ├── api.js                 # API singleton (all endpoints, getPatient())\n│   │   └── auth.js                # Auth utilities (getKey, require, logout)\n│   └── style/\n│       └── main.css               # Custom CSS (app-header, card styles, etc.)\n├── models/\n│   └── isolation.pkl              # Trained Isolation Forest model\n├── seed.py                        # Seeds 3 demo patients + 30-day logs\n├── requirements.txt\n└── .env\n```\n\n---\n\n## 🔧 Installation \u0026 Setup\n\n### Prerequisites\n\n- Python 3.12+\n- PostgreSQL 14+\n- Node.js (optional — only if building Tailwind locally)\n\n### 1. Clone the repository\n\n```bash\ngit clone https://github.com/yourusername/sickleshield.git\ncd sickleshield\n```\n\n### 2. Set up environment variables\n\nCreate a `.env` file in the project root:\n\n```env\nDATABASE_URL=postgresql://user:password@localhost:5432/sickleshield\nVISUAL_CROSSING_WEATHER_API_KEY=your_key_here\nAPP_URL=https://your-domain.com\n```\n\n\u003e **Render note:** If your `DATABASE_URL` starts with `postgres://`, the app automatically rewrites it to `postgresql://` for SQLAlchemy compatibility.\n\n### 3. Install dependencies\n\n```bash\npip install -r requirements.txt\n```\n\nKey dependencies:\n```\nfastapi[standard]\nsqlmodel\npsycopg2-binary\nuvicorn\ntwilio\nqrcode[pil]\njoblib\nscikit-learn\ndiskcache\npython-dotenv\npydantic-settings\nhttpx\n```\n\n### 4. Initialise the database\n\n```bash\n# Tables are auto-created on first startup via SQLModel.metadata.create_all()\nuvicorn src.main:app --reload\n```\n\n### 5. Seed demo patients\n\n```bash\npython seed.py\n```\n\nThis creates Ada, Chidi, and Ngozi with 30 days of synthetic logs and analysis results.\n\n### 6. Run the server\n\n```bash\n# Development\nuvicorn src.main:app --reload --port 8000\n\n# Production (Render uses this automatically)\nuvicorn src.main:app --host 0.0.0.0 --port $PORT\n```\n\nThe app is available at `http://127.0.0.1:8000`. API docs at `/docs`.\n\n---\n\n## 📡 API Reference\n\n### Authentication\n\n| Method | Endpoint | Description |\n|---|---|---|\n| `POST` | `/api/auth/login` | Email + password → `{ patient_id, name, status }` |\n| `GET` | `/api/auth/get-patient?id=\u003cuuid\u003e` | Fetch patient by UUID |\n\nAll protected endpoints require `X-Patient-ID: \u003cuuid\u003e` header.\n\n### Core\n\n| Method | Endpoint | Description |\n|---|---|---|\n| `POST` | `/api/submit-log/` | Submit daily log → triggers ML inference |\n| `GET` | `/apistatus/latest` | Stitched patient status (vitals + analysis) |\n| `GET` | `/api/poll-status/` | Legacy cache endpoint |\n| `GET` | `/api/hrv/latest` | Live HRV stub (wearable integration) |\n\n### Dashboard\n\n| Method | Endpoint | Description |\n|---|---|---|\n| `GET` | `/api/dashboard/sparkline` | 7-day HRV bars for sparkline |\n| `GET` | `/api/dashboard/sms-status` | Latest SMS delivery state |\n\n### QR\n\n| Method | Endpoint | Description |\n|---|---|---|\n| `GET` | `/api/qr/data` | QR image (base64) + risk state |\n| `GET` | `/api/qr/wallet-card?patient_id=\u003cuuid\u003e` | Printable PDF card (stub → 501) |\n\n### Demo\n\n| Method | Endpoint | Description |\n|---|---|---|\n| `GET` | `/demo/stable` | Ada's stable state |\n| `GET` | `/demo/weather_triggered` | Chidi's weather state |\n| `GET` | `/demo/critical_voc` | Ngozi's critical VOC state |\n\n### Template Routes (Jinja2)\n\n| Route | Page |\n|---|---|\n| `GET /` | Index |\n| `GET /login` | Login |\n| `GET /log?patient={state}` | Daily log form |\n| `GET /dashboard?patient={state}` | Dashboard |\n| `GET /qr?patient={state}` | Emergency QR |\n| `GET /triage/{patient_id}` | Offline ER triage |\n| `GET /triage/demo/{state}` | Demo triage (no QR scan needed) |\n\n### `/apistatus/latest` Response Shape\n\n```json\n{\n  \"patient_id\": \"12cac1f5-...\",\n  \"patient_info\": { \"name\": \"Ada Okonkwo\", \"genotype\": \"HbSC\" },\n  \"vitals\": { \"hr\": 65.2, \"hrv\": 75.0, \"temp\": 0, \"steps\": 0 },\n  \"manual\": { \"hydration\": 1, \"pain\": 0, \"urine\": \"pale_yellow\" },\n  \"analysis\": {\n    \"stability_score\": 0.64,\n    \"risk_level\": \"low\",\n    \"is_crisis\": false,\n    \"flags\": [],\n    \"last_updated\": \"2026-04-11T09:41:00Z\"\n  }\n}\n```\n\n\u003e **Score convention:** `stability_score` where `1.0 = LOW risk` and `0.0 = CRITICAL`. The frontend inverts colour logic accordingly.\n\n---\n\n## 🗃️ Data Models\n\n```python\nclass Patient(SQLModel, table=True):\n    id:           uuid.UUID   # Primary key\n    email:        EmailStr\n    password:     str\n    name:         str\n    genotype:     str          # \"HbSS\" | \"HbSC\"\n    has_asthma:   bool\n    phone_number: str\n    clinic_phone: str\n    created_at:   datetime\n\nclass DailyLog(SQLModel, table=True):\n    id:               int\n    patient_id:       uuid.UUID\n    steps:            Optional[int]\n    rmssd:            Optional[float]\n    hrv_sdnn:         Optional[float]\n    hr_mean:          Optional[float]\n    body_temp:        Optional[float]   # Internal patient temp\n    environment_temp: Optional[float]   # Lagos ambient temp\n    hydration_score:  Optional[float]   # 0.0–1.0 (8 glasses = 1.0)\n    urine_color:      Optional[str]     # very_pale | pale_yellow | yellow | dark_yellow | dark_amber\n    pain_level:       Optional[int]     # 0 | 3 | 6 | 9\n    logged_at:        datetime\n\nclass AnalysisResult(SQLModel, table=True):\n    id:               int\n    log_id:           int               # FK → daily_logs\n    anomaly_score:    float\n    raw_anomaly_score: float\n    risk_level:       str               # low | medium | critical\n    is_crisis:        bool\n    method_used:      str               # isolation_forest | heuristic_override\n    triggered_flags:  List[str]         # JSONB\n    ml_metadata:      dict              # JSONB\n\nclass BaselineSnapshot(SQLModel, table=True):\n    patient_id:    uuid.UUID            # PK — one snapshot per patient\n    hr_mean_avg:   float\n    hrv_sdnn_avg:  float\n    hydration_avg: float\n    updated_at:    datetime\n```\n\n---\n\n## 🤖 ML Model — Isolation Forest\n\n### Why Isolation Forest?\n\nSickle Cell crisis data is rare by definition — patients are not in crisis most days. Supervised learning requires labelled crisis examples we don't have at hackathon scale. Isolation Forest is **unsupervised**: it learns the patient's healthy baseline distribution and flags statistical outliers as anomalous.\n\n### Feature Map (10 dimensions)\n\n```\n1.  hr_mean          — Cardiac load (tachycardia = stress signal)\n2.  steps            — Activity context (lethargy at high HR = crisis signal)\n3.  rmssd            — High-frequency HRV (vagus nerve withdrawal)\n4.  hrv_sdnn         — Total autonomic resilience\n5.  environment_temp — Lagos ambient temperature (cold = viscosity spike)\n6.  hydration_score  — Primary VOC trigger\n7.  HR_deviation     — Current HR minus 24h mean (spike detection)\n8.  steps_deviation  — Sudden lethargy vs mean (prodromal signal)\n9.  HRV_deviation    — Delta-HRV (loss of physiological flexibility)\n10. rmssd_deviation  — Parasympathetic withdrawal delta\n```\n\n### Heuristic Safety Net\n\nThe ML model can be bypassed by clinical rules that are deterministic:\n\n```python\nif urine_color == \"dark_amber\":\n    risk_level = \"CRITICAL\"\n    method_used = \"heuristic_override\"\n    # Dark amber urine = haemolysis — cannot wait for model confidence\n```\n\nPain-level ≥ 6 (Moderate/Severe) excludes the log from baseline recalculation, preventing crisis-phase readings from polluting the patient's healthy distribution.\n\n### Model File\n\n```\nmodels/isolation.pkl   # Trained Isolation Forest, joblib format\n```\n\nIf the model file is missing at startup, the system falls back to the heuristic-only engine and logs a warning.\n\n---\n\n## 🖥️ Frontend Architecture\n\n### Philosophy\n\n\"Low-JS\" — the browser does as little as possible. Template routes are thin shells that render HTML with `state` and `prefill` only. All patient data, risk state, and analysis are fetched client-side via `API.getPatient()` and the 5-second polling loop.\n\n### Script Load Order (critical)\n\n```html\n\u003c!-- base.html — synchronous, before Alpine --\u003e\n\u003cscript src=\"/static/script/components/config.js\"\u003e\u003c/script\u003e   \u003c!-- BASE_URL --\u003e\n\u003cscript src=\"/static/script/auth.js\"\u003e\u003c/script\u003e                 \u003c!-- Auth.getKey() --\u003e\n\u003cscript src=\"/static/script/api.js\"\u003e\u003c/script\u003e                  \u003c!-- API singleton --\u003e\n\u003cscript src=\"/static/script/components/urine-wheel.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"/static/script/components/hydration-log.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"/static/script/components/vitals.js\"\u003e\u003c/script\u003e    \u003c!-- $store.vitals --\u003e\n\u003cscript src=\"/static/script/components/log.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"/static/script/components/dashboard.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"/static/script/components/login.js\"\u003e\u003c/script\u003e\n\n{% block scripts %}{% endblock %}                              \u003c!-- page-level --\u003e\n\n\u003c!-- Alpine LAST, with defer — fires alpine:init after all above --\u003e\n\u003cscript defer src=\"https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js\"\u003e\u003c/script\u003e\n```\n\n### Authentication Flow\n\n```\nUser submits email + password\n    → POST /api/auth/login\n    → { patient_id: \"uuid\" }\n    → localStorage.setItem('X-Patient-ID', uuid)\n    → redirect to /log\n\nEvery subsequent API call:\n    → API.get(path) adds header: X-Patient-ID: uuid\n    → API.getPatient() fetches /api/auth/get-patient?id=uuid\n    → 401/404 → Auth.logout() → redirect to /login?next=...\n```\n\n### Polling Architecture\n\n```\nvitalsPoller (mounted on every page via base.html):\n  ├── GET /apistatus/latest    every 5s  → $store.vitals.* + dispatches vitals:analysis event\n  ├── GET /api/api/temperature every 60s → $store.vitals.temp\n  └── GET /api/hrv/latest      every 10s → $store.vitals.hrv (wearable stub)\n\ndashboardApp (dashboard page only):\n  ├── listens to vitals:analysis events\n  ├── GET /api/dashboard/sparkline    every 30s → sparklineBars[]\n  └── GET /api/dashboard/sms-status  every 10s → smsStatus{patient, clinic}\n```\n\n### Pain Scale Design Decision\n\nThe log form uses 4 clinical categories instead of a 0–10 slider:\n\n| Button | Value sent | Baseline exclusion |\n|---|---|---|\n| None 😌 | 0 | No |\n| Mild 😐 | 3 | No |\n| Moderate 😣 | 6 | Yes (`pain_level ≥ 4`) |\n| Severe 😖 | 9 | Yes |\n\nReason: a 0–10 slider has a precision illusion. Patients anchor on round numbers and can't reliably reproduce the same value day-to-day, making the exclusion threshold noisy. Four clinical categories map directly to medical terminology and are reproducible.\n\n---\n\n## 🔩 Technical Debt \u0026 Hackathon Bypasses\n\nTo ensure 100% demo reliability during HSIL 2026, the following engineering trade-offs were made deliberately:\n\n| Bypass | Detail | Production fix |\n|---|---|---|\n| **Weather fallback** | If Visual Crossing times out or hits rate limit, defaults to `31.0°C` (Lagos constant) | Implement retry + circuit breaker |\n| **X-Patient-ID auth** | Custom header used instead of OAuth2/JWT | Replace with JWT (RS256) + refresh tokens |\n| **Postgres prefix** | `postgres://` → `postgresql://` auto-rewrite for Render | Standardise in ENV |\n| **Plain text passwords** | Passwords stored as-is for demo speed | bcrypt hashing + salting |\n| **In-process ML** | Model loaded into main FastAPI process | Move to dedicated inference service or Celery worker |\n| **Wallet card PDF** | `GET /api/qr/wallet-card` returns 501 | Implement ReportLab or WeasyPrint server-side PDF |\n| **No migrations** | Tables created via `SQLModel.metadata.create_all()` | Alembic versioned migrations |\n| **Demo patients hardcoded** | UUIDs baked into `DEMO_PATIENT_MAP` | Dynamic patient discovery |\n\n---\n\n## 📈 Future Roadmap\n\n**Backend**\n- [ ] Alembic migrations — versioned schema management\n- [ ] SMART on FHIR integration — international interoperability standards\n- [ ] Celery + Redis — async SMS and PDF generation workers\n- [ ] JWT hardening — rotatable token-based auth (RS256)\n- [ ] bcrypt password hashing\n- [ ] Rate limiting on `/api/submit-log/` (prevent duplicate submissions)\n\n**ML**\n- [ ] Per-patient model fine-tuning as baseline data accumulates\n- [ ] Gradient Boosting ensemble alongside Isolation Forest\n- [ ] Federated learning — train on aggregated patient cohort without exposing individual data\n\n**Wearable**\n- [ ] Native Apple HealthKit integration (iOS HealthKit API)\n- [ ] Xiaomi Mi Fitness BLE direct sync\n- [ ] RMSSD real-time stream via WebSocket\n\n**Clinical**\n- [ ] Clinician dashboard (separate role + auth)\n- [ ] Medication tracking integration\n- [ ] Hospital EHR API (NHIS Nigeria)\n\n---\n\n## 👥 Team\n\nBuilt at **HSIL Hackathon 2026 — Lagos Hub** in 24 hours.\n\n| Role | Contribution |\n|---|---|\n| **Backend Lead** | FastAPI architecture, rules engine, template routes, frontend integration |\n| **ML Engineer** | Isolation Forest training, feature map, joblib export |\n| **AI Engineer** | LLM prompt engineering, Twilio SMS integration |\n| **PM / Designer** | Pitch deck, UI/UX, business model, demo choreography |\n\n**Developed by:** Gbenga Opeyemi — Computer Science, University of Lagos.\n\n---\n\n## ⚖️ Legal Disclaimer\n\n**FOR EDUCATIONAL PURPOSES ONLY.**\n\nThis project was built for a hackathon competition. It is **not** a clinically validated medical device and has **not** been assessed by any regulatory body (NAFDAC, FDA, CE). Do not use this software for the diagnosis, treatment, prevention, or monitoring of any medical condition.\n\nAny clinical decisions should be made by qualified medical professionals. The anomaly scores produced by this system are statistical indicators, not medical diagnoses.\n\n---\n\n## 📄 Licence\n\nMIT — see `LICENSE` for details.\n\n---\n\n*SickleShield · HSIL 2026 · Built for Nigeria · HbSS · HbSC*","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopesam42%2Fsicklecare","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopesam42%2Fsicklecare","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopesam42%2Fsicklecare/lists"}