An open API service indexing awesome lists of open source software.

https://github.com/opesam42/sicklecare

Predictive health-tech platform for Sickle Cell crisis detection. Built with FastAPI & SQLModel. Features a data-driven Stability Index & Offline QR Triage. 4th Place @ HSIL 2026 Lagos.
https://github.com/opesam42/sicklecare

fastapi hackathon-project healthcare-application

Last synced: 30 days ago
JSON representation

Predictive health-tech platform for Sickle Cell crisis detection. Built with FastAPI & SQLModel. Features a data-driven Stability Index & Offline QR Triage. 4th Place @ HSIL 2026 Lagos.

Awesome Lists containing this project

README

          

# πŸ›‘οΈ SickleShield (SickleCare)

> **Real-time Physiological Monitoring & Crisis Prediction for Sickle Cell Management.**
> *Awarded 4th Place β€” HSIL Hackathon 2026, Lagos Hub*

SickleShield 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.

---

## πŸ“‹ Table of Contents

- [The Mission](#-the-mission)
- [How It Works](#-how-it-works)
- [System Architecture](#-system-architecture)
- [Key Features](#-key-features)
- [Demo Patients](#-demo-patients)
- [Tech Stack](#-tech-stack)
- [Project Structure](#-project-structure)
- [Installation & Setup](#-installation--setup)
- [API Reference](#-api-reference)
- [Data Models](#-data-models)
- [ML Model](#-ml-model--isolation-forest)
- [Frontend Architecture](#-frontend-architecture)
- [Technical Debt & Hackathon Bypasses](#-technical-debt--hackathon-bypasses)
- [Future Roadmap](#-future-roadmap)
- [Team](#-team)
- [Legal Disclaimer](#-legal-disclaimer)

---

## πŸš€ The Mission

In 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.

**SickleShield** addresses three failure points:

| Problem | Our Response |
|---|---|
| Crisis detected too late | Isolation Forest anomaly detection on continuous wearable data |
| Clinic has no context at ER | Offline-first QR triage page β€” works in airplane mode |
| Patient can't self-assess | 4-category pain scale + urine colour wheel with heuristic override |

**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.

---

## βš™οΈ How It Works

```
Patient logs daily Wearable auto-populates
(urine, hydration, pain) (HRV, HR, steps, RMSSD)
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
POST /api/submit-log/
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Heuristic Rule Engine β”‚
β”‚ dark_amber_urine β†’ bypass β”‚
β”‚ pain β‰₯ 6 β†’ exclude baselineβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Heuristic trigger? β”‚
YES NO
β”‚ β”‚
β–Ό β–Ό
CRITICAL (instant) Isolation Forest model
risk_level = CRITICAL anomaly_score β†’ risk_level
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
Stability Index (0.00–1.00)
0.00 = CRITICAL 1.00 = STABLE
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Is crisis? β”‚
YES NO
β”‚ β”‚
β–Ό β–Ό
Twilio SMS (patient Dashboard updates
+ clinic phone) 5-second polling
```

---

## πŸ—οΈ System Architecture

The system follows a **Backend-First Architecture**, prioritising data integrity and high-concurrency biometric polling.

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Frontend (PWA) β”‚
β”‚ Alpine.js Β· Tailwind CSS Β· IBM Plex Sans/Mono β”‚
β”‚ Pages: /log /dashboard /qr /triage/{id} β”‚
β”‚ All data fetched client-side via API.getPatient() + polling β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ HTTP/REST
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ FastAPI (Python 3.12+) β”‚
β”‚ Routers: β”‚
β”‚ /api/auth β€” login, get-patient β”‚
β”‚ /api/submit-log β€” log ingestion + ML inference β”‚
β”‚ /apistatus β€” stitched patient status (5s polling) β”‚
β”‚ /api/dashboard β€” sparkline, SMS status β”‚
β”‚ /api/qr β€” QR generation, wallet card β”‚
β”‚ /demo/{state} β€” demo state machine β”‚
β”‚ Template routes β€” thin Jinja2 shell renderers β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ SQLModel ORM
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PostgreSQL β”‚
β”‚ Tables: patients Β· daily_logs Β· analysis_results β”‚
β”‚ baseline_snapshots β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”
β”‚ diskcache β”‚ β”‚ Twilio SMS β”‚
β”‚ Poll results β”‚ β”‚ Patient + β”‚
β”‚ + weather β”‚ β”‚ Clinic alert β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

---

## ✨ Key Features

### 1. Stability Index (ML Engine)

The core signal. Computed on every log submission from 10 biometric features:

| Feature | Source | Clinical Meaning |
|---|---|---|
| `hrv_sdnn` | Wearable | Total autonomic resilience |
| `rmssd` | Wearable | Parasympathetic withdrawal |
| `hr_mean` | Wearable | Cardiac load |
| `steps` | Wearable | Activity context |
| `hydration_score` | Self-report | Primary VOC trigger |
| `urine_color` | Self-report | Haemolysis marker |
| `pain_level` | Self-report | Crisis threshold gate |
| `body_temp` | Wearable | Internal stress |
| `environment_temp` | Lagos Weather API | Cold front multiplier |
| `HRV_deviation` | Computed | Delta vs 7-day baseline |

Score range: `1.00` (perfectly stable) β†’ `0.00` (immediate crisis).

**Heuristic Override:** `dark_amber_urine` bypasses the ML entirely and fires `CRITICAL` immediately. Haemolysis cannot wait for a model confidence score.

**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.

### 2. Emergency QR & Offline Triage

Every 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:

- Patient name, genotype (HbSS/HbSC), comorbidities
- 7-day biometric log table with crisis days highlighted
- Current anomaly score + method badge
- Baseline snapshot (HRV avg, hydration avg, HR avg)
- Last crisis event timestamp

Designed to load in hospital basements. The QR page also generates a downloadable wallet-size PDF (html2canvas + jsPDF, auto-sized to content β€” no clipping).

### 3. Smart SMS Alerts (Twilio)

Triggered when `is_crisis = True`:
- **MEDIUM risk:** Patient SMS only
- **CRITICAL risk:** Patient SMS + Clinic SMS (dual-channel)

SMS payload includes patient name, risk level, stability score, and a shortened triage URL.

### 4. Wearable Integration

Continuous 5-second polling via `/apistatus/latest` (stitched endpoint) populates:
- HRV SDNN, RMSSD (from Apple Watch / Xiaomi Mi Fitness)
- Heart rate, steps (BLE sync)
- Environmental temperature (Visual Crossing Weather API, Lagos)

Wearable fields are **display-only** in the log form β€” the patient cannot edit them. `buildPayload()` reads directly from `$store.vitals`.

### 5. Dashboard β€” Mission Control

Real-time dashboard with 5-second auto-refresh:
- **Risk banner** β€” pulses on CRITICAL
- **2Γ—2 score card grid** β€” stability score, risk level, HRV today, vs-baseline delta
- **7-day sparkline** β€” bar chart coloured by daily risk level
- **Triggered flags strip** β€” fadein on escalation
- **SMS status rows** β€” live delivery confirmation
- **QR button** β€” visible only on MEDIUM/CRITICAL, pulses on CRITICAL
- **Demo state switcher** β€” Ada (stable) / Chidi (weather) / Ngozi (critical VOC)

---

## πŸ‘₯ Demo Patients

Three seeded patients covering all three clinical states:

| State | Patient | Genotype | UUID (first 8) |
|---|---|---|---|
| `stable` | Ada Okonkwo | HbSC | `12cac1f5` |
| `weather_triggered` | Chidi Eze | HbSS | `6f2b4d3b` |
| `critical_voc` | Ngozi Adeyemi | HbSS + Asthma | `7df78b03` |

**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."

Access demo states at `/dashboard?patient=stable`, `/dashboard?patient=weather_triggered`, `/dashboard?patient=critical_voc`.

---

## πŸ› οΈ Tech Stack

| Layer | Technology | Why |
|---|---|---|
| Backend | FastAPI (Python 3.12+) | Async event loop, auto OpenAPI docs, speed |
| ORM | SQLModel (SQLAlchemy + Pydantic) | Type-safe models, single source of truth |
| Database | PostgreSQL | JSONB for flags/metadata, relational integrity |
| ML | Isolation Forest (scikit-learn, joblib) | Unsupervised β€” no labelled crisis data needed |
| Cache | diskcache | Zero-config, filesystem-backed poll cache |
| Frontend | Alpine.js 3.14 + Tailwind CSS CDN | Low-JS, fast on low-end Nigerian mobile devices |
| Fonts | IBM Plex Sans + IBM Plex Mono | Medical/clinical aesthetic, Google Fonts |
| Templates | Jinja2 | Thin shell renders β€” all data fetched by JS |
| SMS | Twilio | Programmable SMS, Nigerian numbers supported |
| QR | qrcode[pil] | Python-native QR generation, ERROR_CORRECT_M |
| PDF | html2canvas + jsPDF (CDN) | Auto-sized to content β€” zero clipping |
| Weather | Visual Crossing API | Lagos ambient temperature enrichment |
| Deploy | Render | Free tier, PostgreSQL add-on |

---

## πŸ“ Project Structure

```
sickleshield/
β”œβ”€β”€ src/
β”‚ β”œβ”€β”€ main.py # FastAPI app, lifespan, CORS, router registration
β”‚ β”œβ”€β”€ config.py # Pydantic settings (DATABASE_URL, API keys)
β”‚ β”œβ”€β”€ database.py # Engine, session factory, SessionDep
β”‚ β”œβ”€β”€ logger.py # Shared logging config
β”‚ β”œβ”€β”€ cache.py # diskcache setup
β”‚ β”œβ”€β”€ ml_model.py # Isolation Forest load/unload + feature map
β”‚ β”œβ”€β”€ models/
β”‚ β”‚ └── models.py # Patient, DailyLog, AnalysisResult, BaselineSnapshot
β”‚ β”œβ”€β”€ schemas/
β”‚ β”‚ └── schema.py # LoginRequest, LoginResponse, Pydantic payloads
β”‚ β”œβ”€β”€ routers/
β”‚ β”‚ β”œβ”€β”€ api.py # /api/submit-log/, /apistatus/latest, /demo/*
β”‚ β”‚ β”œβ”€β”€ auth.py # /api/auth/login, /api/auth/get-patient
β”‚ β”‚ β”œβ”€β”€ dashboard_router.py # /api/dashboard/sparkline, /api/dashboard/sms-status
β”‚ β”‚ └── qr_router.py # /api/qr/data, /api/qr/wallet-card
β”‚ β”œβ”€β”€ templates.py # Thin Jinja2 template routes
β”‚ └── services.py # ML inference, heuristic engine, SMS dispatch
β”œβ”€β”€ templates/
β”‚ β”œβ”€β”€ base.html # Alpine.js load order, Tailwind config, script slots
β”‚ β”œβ”€β”€ _header.html # Shared app header partial (drawer + stats strip)
β”‚ β”œβ”€β”€ login.html # Auth page
β”‚ β”œβ”€β”€ log.html # 3-step daily log form (urine β†’ hydration β†’ pain)
β”‚ β”œβ”€β”€ dashboard.html # Mission control dashboard
β”‚ β”œβ”€β”€ qr.html # Emergency QR + wallet card PDF download
β”‚ └── triage.html # Offline ER triage (zero JS, zero CDN)
β”œβ”€β”€ static/
β”‚ β”œβ”€β”€ script/
β”‚ β”‚ β”œβ”€β”€ components/
β”‚ β”‚ β”‚ β”œβ”€β”€ config.js # BASE_URL (localhost vs production)
β”‚ β”‚ β”‚ β”œβ”€β”€ vitals.js # $store.vitals + vitalsPoller (5s polling)
β”‚ β”‚ β”‚ β”œβ”€β”€ log.js # logForm Alpine component
β”‚ β”‚ β”‚ β”œβ”€β”€ dashboard.js # dashboardApp Alpine component
β”‚ β”‚ β”‚ β”œβ”€β”€ login.js # loginForm Alpine component
β”‚ β”‚ β”‚ β”œβ”€β”€ urine-wheel.js # urineWheel Alpine component
β”‚ β”‚ β”‚ └── hydration-log.js # hydrationLog Alpine component
β”‚ β”‚ β”œβ”€β”€ api.js # API singleton (all endpoints, getPatient())
β”‚ β”‚ └── auth.js # Auth utilities (getKey, require, logout)
β”‚ └── style/
β”‚ └── main.css # Custom CSS (app-header, card styles, etc.)
β”œβ”€β”€ models/
β”‚ └── isolation.pkl # Trained Isolation Forest model
β”œβ”€β”€ seed.py # Seeds 3 demo patients + 30-day logs
β”œβ”€β”€ requirements.txt
└── .env
```

---

## πŸ”§ Installation & Setup

### Prerequisites

- Python 3.12+
- PostgreSQL 14+
- Node.js (optional β€” only if building Tailwind locally)

### 1. Clone the repository

```bash
git clone https://github.com/yourusername/sickleshield.git
cd sickleshield
```

### 2. Set up environment variables

Create a `.env` file in the project root:

```env
DATABASE_URL=postgresql://user:password@localhost:5432/sickleshield
VISUAL_CROSSING_WEATHER_API_KEY=your_key_here
APP_URL=https://your-domain.com
```

> **Render note:** If your `DATABASE_URL` starts with `postgres://`, the app automatically rewrites it to `postgresql://` for SQLAlchemy compatibility.

### 3. Install dependencies

```bash
pip install -r requirements.txt
```

Key dependencies:
```
fastapi[standard]
sqlmodel
psycopg2-binary
uvicorn
twilio
qrcode[pil]
joblib
scikit-learn
diskcache
python-dotenv
pydantic-settings
httpx
```

### 4. Initialise the database

```bash
# Tables are auto-created on first startup via SQLModel.metadata.create_all()
uvicorn src.main:app --reload
```

### 5. Seed demo patients

```bash
python seed.py
```

This creates Ada, Chidi, and Ngozi with 30 days of synthetic logs and analysis results.

### 6. Run the server

```bash
# Development
uvicorn src.main:app --reload --port 8000

# Production (Render uses this automatically)
uvicorn src.main:app --host 0.0.0.0 --port $PORT
```

The app is available at `http://127.0.0.1:8000`. API docs at `/docs`.

---

## πŸ“‘ API Reference

### Authentication

| Method | Endpoint | Description |
|---|---|---|
| `POST` | `/api/auth/login` | Email + password β†’ `{ patient_id, name, status }` |
| `GET` | `/api/auth/get-patient?id=` | Fetch patient by UUID |

All protected endpoints require `X-Patient-ID: ` header.

### Core

| Method | Endpoint | Description |
|---|---|---|
| `POST` | `/api/submit-log/` | Submit daily log β†’ triggers ML inference |
| `GET` | `/apistatus/latest` | Stitched patient status (vitals + analysis) |
| `GET` | `/api/poll-status/` | Legacy cache endpoint |
| `GET` | `/api/hrv/latest` | Live HRV stub (wearable integration) |

### Dashboard

| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/api/dashboard/sparkline` | 7-day HRV bars for sparkline |
| `GET` | `/api/dashboard/sms-status` | Latest SMS delivery state |

### QR

| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/api/qr/data` | QR image (base64) + risk state |
| `GET` | `/api/qr/wallet-card?patient_id=` | Printable PDF card (stub β†’ 501) |

### Demo

| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/demo/stable` | Ada's stable state |
| `GET` | `/demo/weather_triggered` | Chidi's weather state |
| `GET` | `/demo/critical_voc` | Ngozi's critical VOC state |

### Template Routes (Jinja2)

| Route | Page |
|---|---|
| `GET /` | Index |
| `GET /login` | Login |
| `GET /log?patient={state}` | Daily log form |
| `GET /dashboard?patient={state}` | Dashboard |
| `GET /qr?patient={state}` | Emergency QR |
| `GET /triage/{patient_id}` | Offline ER triage |
| `GET /triage/demo/{state}` | Demo triage (no QR scan needed) |

### `/apistatus/latest` Response Shape

```json
{
"patient_id": "12cac1f5-...",
"patient_info": { "name": "Ada Okonkwo", "genotype": "HbSC" },
"vitals": { "hr": 65.2, "hrv": 75.0, "temp": 0, "steps": 0 },
"manual": { "hydration": 1, "pain": 0, "urine": "pale_yellow" },
"analysis": {
"stability_score": 0.64,
"risk_level": "low",
"is_crisis": false,
"flags": [],
"last_updated": "2026-04-11T09:41:00Z"
}
}
```

> **Score convention:** `stability_score` where `1.0 = LOW risk` and `0.0 = CRITICAL`. The frontend inverts colour logic accordingly.

---

## πŸ—ƒοΈ Data Models

```python
class Patient(SQLModel, table=True):
id: uuid.UUID # Primary key
email: EmailStr
password: str
name: str
genotype: str # "HbSS" | "HbSC"
has_asthma: bool
phone_number: str
clinic_phone: str
created_at: datetime

class DailyLog(SQLModel, table=True):
id: int
patient_id: uuid.UUID
steps: Optional[int]
rmssd: Optional[float]
hrv_sdnn: Optional[float]
hr_mean: Optional[float]
body_temp: Optional[float] # Internal patient temp
environment_temp: Optional[float] # Lagos ambient temp
hydration_score: Optional[float] # 0.0–1.0 (8 glasses = 1.0)
urine_color: Optional[str] # very_pale | pale_yellow | yellow | dark_yellow | dark_amber
pain_level: Optional[int] # 0 | 3 | 6 | 9
logged_at: datetime

class AnalysisResult(SQLModel, table=True):
id: int
log_id: int # FK β†’ daily_logs
anomaly_score: float
raw_anomaly_score: float
risk_level: str # low | medium | critical
is_crisis: bool
method_used: str # isolation_forest | heuristic_override
triggered_flags: List[str] # JSONB
ml_metadata: dict # JSONB

class BaselineSnapshot(SQLModel, table=True):
patient_id: uuid.UUID # PK β€” one snapshot per patient
hr_mean_avg: float
hrv_sdnn_avg: float
hydration_avg: float
updated_at: datetime
```

---

## πŸ€– ML Model β€” Isolation Forest

### Why Isolation Forest?

Sickle 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.

### Feature Map (10 dimensions)

```
1. hr_mean β€” Cardiac load (tachycardia = stress signal)
2. steps β€” Activity context (lethargy at high HR = crisis signal)
3. rmssd β€” High-frequency HRV (vagus nerve withdrawal)
4. hrv_sdnn β€” Total autonomic resilience
5. environment_temp β€” Lagos ambient temperature (cold = viscosity spike)
6. hydration_score β€” Primary VOC trigger
7. HR_deviation β€” Current HR minus 24h mean (spike detection)
8. steps_deviation β€” Sudden lethargy vs mean (prodromal signal)
9. HRV_deviation β€” Delta-HRV (loss of physiological flexibility)
10. rmssd_deviation β€” Parasympathetic withdrawal delta
```

### Heuristic Safety Net

The ML model can be bypassed by clinical rules that are deterministic:

```python
if urine_color == "dark_amber":
risk_level = "CRITICAL"
method_used = "heuristic_override"
# Dark amber urine = haemolysis β€” cannot wait for model confidence
```

Pain-level β‰₯ 6 (Moderate/Severe) excludes the log from baseline recalculation, preventing crisis-phase readings from polluting the patient's healthy distribution.

### Model File

```
models/isolation.pkl # Trained Isolation Forest, joblib format
```

If the model file is missing at startup, the system falls back to the heuristic-only engine and logs a warning.

---

## πŸ–₯️ Frontend Architecture

### Philosophy

"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.

### Script Load Order (critical)

```html



{% block scripts %}{% endblock %}

```

### Authentication Flow

```
User submits email + password
β†’ POST /api/auth/login
β†’ { patient_id: "uuid" }
β†’ localStorage.setItem('X-Patient-ID', uuid)
β†’ redirect to /log

Every subsequent API call:
β†’ API.get(path) adds header: X-Patient-ID: uuid
β†’ API.getPatient() fetches /api/auth/get-patient?id=uuid
β†’ 401/404 β†’ Auth.logout() β†’ redirect to /login?next=...
```

### Polling Architecture

```
vitalsPoller (mounted on every page via base.html):
β”œβ”€β”€ GET /apistatus/latest every 5s β†’ $store.vitals.* + dispatches vitals:analysis event
β”œβ”€β”€ GET /api/api/temperature every 60s β†’ $store.vitals.temp
└── GET /api/hrv/latest every 10s β†’ $store.vitals.hrv (wearable stub)

dashboardApp (dashboard page only):
β”œβ”€β”€ listens to vitals:analysis events
β”œβ”€β”€ GET /api/dashboard/sparkline every 30s β†’ sparklineBars[]
└── GET /api/dashboard/sms-status every 10s β†’ smsStatus{patient, clinic}
```

### Pain Scale Design Decision

The log form uses 4 clinical categories instead of a 0–10 slider:

| Button | Value sent | Baseline exclusion |
|---|---|---|
| None 😌 | 0 | No |
| Mild 😐 | 3 | No |
| Moderate 😣 | 6 | Yes (`pain_level β‰₯ 4`) |
| Severe πŸ˜– | 9 | Yes |

Reason: 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.

---

## πŸ”© Technical Debt & Hackathon Bypasses

To ensure 100% demo reliability during HSIL 2026, the following engineering trade-offs were made deliberately:

| Bypass | Detail | Production fix |
|---|---|---|
| **Weather fallback** | If Visual Crossing times out or hits rate limit, defaults to `31.0Β°C` (Lagos constant) | Implement retry + circuit breaker |
| **X-Patient-ID auth** | Custom header used instead of OAuth2/JWT | Replace with JWT (RS256) + refresh tokens |
| **Postgres prefix** | `postgres://` β†’ `postgresql://` auto-rewrite for Render | Standardise in ENV |
| **Plain text passwords** | Passwords stored as-is for demo speed | bcrypt hashing + salting |
| **In-process ML** | Model loaded into main FastAPI process | Move to dedicated inference service or Celery worker |
| **Wallet card PDF** | `GET /api/qr/wallet-card` returns 501 | Implement ReportLab or WeasyPrint server-side PDF |
| **No migrations** | Tables created via `SQLModel.metadata.create_all()` | Alembic versioned migrations |
| **Demo patients hardcoded** | UUIDs baked into `DEMO_PATIENT_MAP` | Dynamic patient discovery |

---

## πŸ“ˆ Future Roadmap

**Backend**
- [ ] Alembic migrations β€” versioned schema management
- [ ] SMART on FHIR integration β€” international interoperability standards
- [ ] Celery + Redis β€” async SMS and PDF generation workers
- [ ] JWT hardening β€” rotatable token-based auth (RS256)
- [ ] bcrypt password hashing
- [ ] Rate limiting on `/api/submit-log/` (prevent duplicate submissions)

**ML**
- [ ] Per-patient model fine-tuning as baseline data accumulates
- [ ] Gradient Boosting ensemble alongside Isolation Forest
- [ ] Federated learning β€” train on aggregated patient cohort without exposing individual data

**Wearable**
- [ ] Native Apple HealthKit integration (iOS HealthKit API)
- [ ] Xiaomi Mi Fitness BLE direct sync
- [ ] RMSSD real-time stream via WebSocket

**Clinical**
- [ ] Clinician dashboard (separate role + auth)
- [ ] Medication tracking integration
- [ ] Hospital EHR API (NHIS Nigeria)

---

## πŸ‘₯ Team

Built at **HSIL Hackathon 2026 β€” Lagos Hub** in 24 hours.

| Role | Contribution |
|---|---|
| **Backend Lead** | FastAPI architecture, rules engine, template routes, frontend integration |
| **ML Engineer** | Isolation Forest training, feature map, joblib export |
| **AI Engineer** | LLM prompt engineering, Twilio SMS integration |
| **PM / Designer** | Pitch deck, UI/UX, business model, demo choreography |

**Developed by:** Gbenga Opeyemi β€” Computer Science, University of Lagos.

---

## βš–οΈ Legal Disclaimer

**FOR EDUCATIONAL PURPOSES ONLY.**

This 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.

Any clinical decisions should be made by qualified medical professionals. The anomaly scores produced by this system are statistical indicators, not medical diagnoses.

---

## πŸ“„ Licence

MIT β€” see `LICENSE` for details.

---

*SickleShield Β· HSIL 2026 Β· Built for Nigeria Β· HbSS Β· HbSC*