Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 184 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,157 @@
# Diabetic Patient Priority System
# Diacify

**For clinicians:** A smart dashboard that automatically ranks your diabetic patients by clinical urgency — so your team always knows who needs attention first.
A clinical triage tool that ranks diabetic patients by urgency using an
ML risk score, longitudinal visit tracking, and appointment management.

**For developers:** A full-stack system built with React, Node.js, MySQL, and a Python Random Forest classifier that scores patients across 13 clinical indicators on a 0–100 risk scale.
Stack: React · Node.js/Express · MySQL · Python/FastAPI · Random Forest · Clerk

---

## Screenshots

### Priority Dashboard
![Priority Dashboard](./assets/dashboard.png)

### Patient Detail
![Patient Detail](./assets/patient-detail.png)

### Analytics
![Analytics](./assets/analytics.png)

### Book Appointment
![Book Appointment](./assets/book-appointment.png)

---

## Architecture

```mermaid
graph TD
Clinician["🧑‍⚕️ Clinician"]

subgraph Frontend["Frontend — React + Vite (Vercel)"]
UI["Dashboard / Patient Detail / Analytics"]
Clerk["Clerk Auth"]
end

subgraph Backend["Backend — Node.js + Express (Render)"]
API["REST API"]
AuthMW["requireClerkAuth middleware"]
PatientCtrl["patientController"]
ApptCtrl["appointmentController"]
AnalyticsCtrl["analyticsController"]
RateLimit["Rate limiting + Helmet"]
end

subgraph MLService["ML Service — Python + FastAPI (Render)"]
Predict["POST /predict"]
RF["Random Forest Classifier"]
SecretCheck["X-Internal-Secret validation"]
end

subgraph Database["Database — MySQL 8 (Railway)"]
Patients["patients table"]
Visits["visits table"]
Appointments["appointments table"]
AuditLog["audit_log table"]
end

Clinician -->|"HTTPS"| UI
UI -->|"Clerk session token"| Clerk
UI -->|"Bearer token + request"| API
API --> AuthMW
AuthMW --> PatientCtrl
AuthMW --> ApptCtrl
AuthMW --> AnalyticsCtrl
API --> RateLimit
PatientCtrl -->|"INSERT visit first"| Visits
PatientCtrl -->|"POST /predict + X-Internal-Secret"| SecretCheck
SecretCheck --> Predict
Predict --> RF
RF -->|"score + category + top_factors"| PatientCtrl
PatientCtrl -->|"UPDATE visit with ML result"| Visits
PatientCtrl --- Patients
ApptCtrl --- Appointments
PatientCtrl --- AuditLog
```

## Preview
![Diabetic Risk Classification Dashboard](./assets/dashboard.png)
![Diabetic Risk Classification Analytics](./assets/analytics.png)
---

## Tech Stack

**Frontend**
- React — component-based UI
- React 18 — component-based UI
- Vite — build tool and development server
- Clerk — authentication and session management
- react-chartjs-2 — trajectory and analytics charts
- axios — HTTP client

**Backend**
- Node.js + Express.js — RESTful API
- MySQL — patient data storage
- MySQL 8 — relational data storage
- Zod — server-side input validation
- Clerk SDK — backend session token verification
- helmet — security headers
- express-rate-limit — rate limiting
- winston — structured logging

**Machine Learning**
- Python + FastAPI — ML service
- Python + FastAPI — ML microservice
- scikit-learn — Random Forest classifier
- pandas / numpy — data processing
- pandas / numpy — data preprocessing

---

## Features

### Dashboard
- Summary cards showing High, Medium, and Low risk patient counts
- Priority patient list sorted by risk score (highest first)
- Search by Patient ID (e.g. `p8` or `8`)
- Filter patients by risk level
- Priority patient list sorted by risk score, HbA1c, then patient ID
- Search by Patient ID
- Filter by risk level
- This week's appointments widget

### Patient Detail
- Current risk score with semicircular gauge
- Top contributing factors with relative importance bars
- HbA1c trajectory chart with ADA reference lines at 5.7% and 6.5%
- Risk score trajectory chart with colour-coded bands
- Sparklines for BMI, Systolic BP, RBS, and Triglycerides
- Full visit history table with expandable rows
- Appointment booking and history

### Patient Management
- Add, edit, and delete patient records
- Visit history — one row per clinical visit, full longitudinal record
- Client and server-side validation with clinical range checking
- Risk score and category automatically recalculated on every save
- Risk score and category automatically recalculated on every new visit

### Machine Learning
- Random Forest model trained on historical patient data
- Random Forest classifier trained on the Erbil Diabetes Dataset (662 patients)
- Labels derived from ADA 2025 diagnostic thresholds — HbA1c primary driver
- Secondary upgrade rule using five clinical flags (BP, BMI, RBS, TG/HDL ratio, LDL/HDL ratio)
- 14 features including four engineered features: TG/HDL ratio, LDL/HDL ratio, hypertension flag, age-BMI interaction
- Risk scored on a 0–100 continuous scale
- Three risk categories: Low (0–39), Medium (40–69), High (70–100)
- 7 features used for prediction: HbA1c, Age, Sex, BP Systolic, BP Diastolic, BMI, RBS
- 13 clinical indicators collected via the form: Age, Sex, Social Life, BP Systolic, BP Diastolic, Cholesterol, Triglycerides, HDL, LDL, VLDL, HbA1c, BMI, RBS
- Confidence percentages returned per class
- Low confidence flag when max class probability < 0.40

### Analytics
- Age distribution chart split by risk category
- Risk score histogram across 10-point bands
### Security
- Clerk session token verified on every backend route
- ML service protected by shared internal secret header
- helmet.js security headers
- Rate limiting on all API routes
- Audit log on all patient data actions

---

## Prerequisites

- Git
- Node.js (v14 or higher)
- Node.js v20 or higher
- npm
- MySQL (v8.0 or higher)
- Python (v3.8 or higher)
- MySQL 8.0 or higher
- Python 3.11 or higher

---

Expand All @@ -83,7 +174,7 @@ npm install
Create a `.env` file in the `frontend/` directory:

```env
VITE_API_URL=http://localhost:3000
VITE_API_URL=http://localhost:3300
VITE_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key_here
```

Expand All @@ -97,19 +188,18 @@ npm install
Create a `.env` file in the `backend/` directory:

```env
# Server
PORT=3000
PORT=3300
NODE_ENV=development

# Database
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_mysql_password
DB_NAME=diabetic_db
DB_NAME=diacify_db
DB_PORT=3306

# ML Service
ML_SERVICE_URL=http://localhost:8001
CLERK_SECRET_KEY=your_clerk_secret_key_here
ML_INTERNAL_SECRET=your_shared_secret_here
```

### 4. Database Setup
Expand All @@ -119,18 +209,19 @@ mysql -u root -p
```

```sql
CREATE DATABASE diabetic_db;
USE diabetic_db;
```

```bash
mysql -u root -p diabetic_db < backend/database/schema.sql
CREATE DATABASE diacify_db;
exit
```

Optionally load sample data:
Run migrations in order:

```bash
mysql -u root -p diabetic_db < backend/database/seed.sql
cd backend/database/migrations
mysql -u root -p diacify_db < 001_create_patients.sql
mysql -u root -p diacify_db < 002_create_visits.sql
mysql -u root -p diacify_db < 003_create_appointments.sql
mysql -u root -p diacify_db < 004_create_audit_log.sql
mysql -u root -p diacify_db < 005_add_indices.sql
```

### 5. Machine Learning Setup
Expand All @@ -140,7 +231,14 @@ cd machine-learning
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
python train_model.py # trains and saves the model
python train_model.py
```

Create a `.env` file in the `machine-learning/` directory:

```env
ML_INTERNAL_SECRET=your_shared_secret_here
PORT=8001
```

---
Expand All @@ -152,14 +250,14 @@ Open three terminal windows:
**Terminal 1 — Backend**
```bash
cd backend
node server.js
npm run dev
```
Runs at `http://localhost:3000`
Runs at `http://localhost:3300`

**Terminal 2 — ML Service**
```bash
cd machine-learning
source venv/bin/activate # Windows: venv\Scripts\activate
source venv/bin/activate
uvicorn app:app --reload --port 8001
```
Runs at `http://localhost:8001`
Expand All @@ -175,69 +273,75 @@ Runs at `http://localhost:5173`

## API Endpoints

Base URL: `http://localhost:3000`

### Authentication
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/auth/signup` | Create clinician account |
| POST | `/api/auth/login` | Clinician login |
Base URL: `http://localhost:3300`

### Patients
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/patients` | Get all patients (supports `clerk_id`, `riskLevel`, `sortBy` query params) |
| GET | `/api/patients/:id` | Get patient by ID |
| POST | `/api/patients` | Add new patient |
| GET | `/api/patients` | Get all patients for the authenticated clinician |
| GET | `/api/patients/:id` | Get patient by ID including all visits |
| POST | `/api/patients` | Add new patient and trigger ML scoring |
| PUT | `/api/patients/:id` | Update patient record |
| DELETE | `/api/patients/:id` | Delete patient |

### Appointments
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/appointments` | Book a new appointment |
| GET | `/api/appointments/:patientId` | Get all appointments for a patient |

### Analytics
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/analytics` | Get age distribution and risk score histogram data |
| GET | `/api/analytics` | Get cohort analytics data |

### Health
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/health` | Service health check including DB and ML status |

### ML Service
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/` | Health check |
| POST | `/predict` | Predict risk score for a patient |
| GET | `/` | ML service health check |
| POST | `/predict` | Score a patient (internal use only) |

---

## Production Build
## Environment Variables Reference

### Backend
| Variable | Description |
|----------|-------------|
| `PORT` | Server port (default 3300) |
| `DB_HOST` | MySQL host |
| `DB_USER` | MySQL user |
| `DB_PASSWORD` | MySQL password |
| `DB_NAME` | Database name (diacify_db) |
| `DB_PORT` | MySQL port (default 3306) |
| `ML_SERVICE_URL` | URL of the ML FastAPI service |
| `CLERK_SECRET_KEY` | Clerk backend secret key |
| `ML_INTERNAL_SECRET` | Shared secret for ML service authentication |

Comment on lines +313 to +325

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add BACKEND_ORIGIN to backend env docs to prevent CORS setup failures.

The backend env reference omits BACKEND_ORIGIN, but the frontend runs on http://localhost:5173 and the ML service default origin is still http://localhost:3000. Fresh local setups can fail CORS unless this is explicitly configured.

Suggested doc patch
 ### Backend
 | Variable | Description |
 |----------|-------------|
 | `PORT` | Server port (default 3300) |
 | `DB_HOST` | MySQL host |
 | `DB_USER` | MySQL user |
 | `DB_PASSWORD` | MySQL password |
 | `DB_NAME` | Database name (diacify_db) |
 | `DB_PORT` | MySQL port (default 3306) |
 | `ML_SERVICE_URL` | URL of the ML FastAPI service |
+| `BACKEND_ORIGIN` | Allowed frontend origin for ML CORS (e.g. `http://localhost:5173`) |
 | `CLERK_SECRET_KEY` | Clerk backend secret key |
 | `ML_INTERNAL_SECRET` | Shared secret for ML service authentication |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 314-314: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 313 - 325, The Backend env table is missing
BACKEND_ORIGIN which causes CORS mismatches during local setup; add a new row
for `BACKEND_ORIGIN` in the Backend environment variables section alongside
`ML_SERVICE_URL` and the DB vars, specifying its purpose (backend origin used
for CORS configuration) and a sensible default such as `http://localhost:3000`
(note frontend runs at `http://localhost:5173`) so developers can set the
correct origin to avoid CORS failures.

### Frontend
| Variable | Description |
|----------|-------------|
| `VITE_API_URL` | Backend API base URL |
| `VITE_CLERK_PUBLISHABLE_KEY` | Clerk publishable key |

```bash
cd frontend
npm run build
```

Output will be in the `dist/` directory.
### ML Service
| Variable | Description |
|----------|-------------|
| `ML_INTERNAL_SECRET` | Must match backend value |
| `PORT` | ML service port (default 8001) |

---

## Future Enhancements

- Integration with hospital Electronic Medical Record (EMR) systems
- Mobile application
- Automated notifications and reminders for follow-up appointments
- Automated notifications for follow-up appointments
- Patient outcome tracking and model retraining on real clinical labels
- Export functionality for reports and analytics

---

## Note

This is a prototype system built for educational purposes.
For deployment in clinical settings, additional regulatory compliance,
security audits, and clinical validation are required.
## Running the project locally

Terminal 1 — Frontend:
cd frontend && npm run dev

Terminal 2 — Backend:
cd backend && npm run dev

Terminal 3 — ML service:
cd machine-learning && uvicorn app:app --reload --port 8001
- Google Calendar integration for appointment management
Binary file removed assets/analytics.png
Binary file not shown.
Binary file modified assets/dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading