Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

indiser/Rxcheck

Repository files navigation

πŸ’Š RxCheck β€” Clinical Drug Interaction Safety Dashboard

RxCheck is a production-grade drug interaction checker with PostgreSQL-backed clinical rules, automated FDA data ingestion, multi-provider LLM router, and intelligent prescription image analysis. Built for healthcare professionals and patients who need reliable medication safety screening with complete privacy.

Python Flask PostgreSQL License


πŸ“Έ Screenshots

🎨 Application Interface

Home Screen

Home Screen
Clean interface with dual input modes

Drug Input

Drug Input
Manual entry with autocomplete

Prescription Upload

Prescription Upload
Gemini Vision-powered OCR

Interaction Results

Interaction Results
Risk categorization dashboard

Generic Alternatives

Generic Alternatives
Cost savings with PMBJP options

Emergency Warning

Emergency Warning
High-risk interaction alerts


✨ Key Features

πŸ” Enterprise-Grade Security

  • Server-side interaction rules evaluation (never exposed to client)
  • PostgreSQL connection pooling with thread-safe operations
  • High-precision trigram search prevents false-positive matches
  • RxNorm API proxy prevents direct client exposure

🧬 Clinical Intelligence

  • PostgreSQL-backed rules engine with curated interaction patterns
  • openFDA SPL data ingestion with automated ETL pipeline
  • Multi-provider LLM router (Groq, Cerebras, Gemini, OpenRouter, HuggingFace) with automatic failover
  • Trigram similarity matching (threshold 0.85) eliminates dangerous substring matches
  • Levenshtein fuzzy matching for Indian brand name normalization
  • RxNorm integration for standardized drug nomenclature

πŸ“Έ Smart Prescription Processing

  • Client-side image upload with Gemini 2.5 Flash vision analysis
  • Automatic image preprocessing and clinical OCR
  • Indian prescription shorthand extraction (OD, BD, TDS, etc.)
  • Multi-line prescription text grouping for broken OCR output
  • Support for single-letter vitamins (A, C, D, E, K) and numeric compounds (Omega 3, D3)

πŸ’° Cost Savings Intelligence

  • 34 common Indian brand-to-generic mappings
  • PMBJP (Jan Aushadhi) generic alternatives highlighted
  • Approximate MRP pricing with regional disclaimers
  • Automatic prescription matching and savings calculation

🎯 User Experience

  • Medical disclaimer modal with localStorage persistence
  • Emergency escalation warnings for high-risk interactions
  • WhatsApp sharing and clipboard export
  • Fully responsive mobile-first design
  • Dual input modes: manual entry and prescription text parsing

πŸ—οΈ Architecture

Backend (Flask REST API)

app.py
β”œβ”€β”€ GET / β†’ Render SPA shell
β”œβ”€β”€ GET /api/rxnorm/<path> β†’ Transparent RxNorm proxy
β”œβ”€β”€ GET /api/local-data β†’ Serve safe catalog data (brand_hints.json + price_catalog.json)
β”œβ”€β”€ POST /api/normalize-drug β†’ Server-side brandβ†’generic matching
β”œβ”€β”€ POST /api/check-interactions β†’ PostgreSQL interaction query
└── POST /api/extract-vision β†’ Gemini Vision image analysis endpoint

Catalog Builder (Data Processing)

build_catgory.py
β”œβ”€β”€ INPUT β†’ updated_indian_medicine_data.csv
β”œβ”€β”€ CLEAN β†’ Strip dosage forms, normalize salts
β”œβ”€β”€ EXTRACT β†’ Brand families (e.g., "Ascoril LS" β†’ "ascoril ls" + "ascoril")
β”œβ”€β”€ OUTPUT β†’ brand_hints.json (brandβ†’generic mappings)
└── OUTPUT β†’ price_catalog.json (genericβ†’pricing + alternatives)

ETL Pipeline (openFDA Ingestion)

etl.py
β”œβ”€β”€ SEED β†’ Bootstrap from hardcoded INTERACTION_RULES
β”œβ”€β”€ CHECKPOINT β†’ Resume from last successful batch
β”œβ”€β”€ FETCH β†’ Download FDA drug labels (openFDA API)
β”œβ”€β”€ PARSE β†’ Extract drug names and interaction text
β”œβ”€β”€ LLM β†’ Structure free-text with multi-provider router
β”œβ”€β”€ LOAD β†’ Upsert drugs and interactions to PostgreSQL
└── COMMIT β†’ Atomic checkpoint update

LLM Router (Multi-Provider Failover)

llm_router.py
β”œβ”€β”€ Groq (llama-3.3-70b-versatile) β†’ Primary
β”œβ”€β”€ Cerebras (gpt-oss-120b) β†’ Fallback 1
β”œβ”€β”€ Gemini (gemini-2.5-flash) β†’ Fallback 2
β”œβ”€β”€ OpenRouter (free tier) β†’ Fallback 3
└── HuggingFace (Meta-Llama-3-8B) β†’ Fallback 4

Database (PostgreSQL)

-- High-precision drug resolution
CREATE TABLE drugs (
 id BIGSERIAL PRIMARY KEY,
 generic_name CITEXT UNIQUE NOT NULL,
 brand_names JSONB DEFAULT '[]'::jsonb
);
CREATE INDEX idx_drugs_name_trgm ON drugs USING gin (generic_name gin_trgm_ops);
-- Interaction rules storage
CREATE TABLE interactions (
 drug_a_id BIGINT REFERENCES drugs(id),
 drug_b_id BIGINT REFERENCES drugs(id),
 severity TEXT CHECK (severity IN ('high', 'medium', 'low')),
 description TEXT,
 action_text TEXT,
 source TEXT,
 PRIMARY KEY (drug_a_id, drug_b_id),
 CHECK (drug_a_id < drug_b_id)
);
-- ETL checkpoint for fault-tolerance
CREATE TABLE etl_sync_state (
 id INT PRIMARY KEY DEFAULT 1 CHECK (id = 1),
 last_successful_skip INT DEFAULT 0,
 updated_at TIMESTAMPTZ DEFAULT NOW()
);

Frontend (Vanilla JS SPA)

  • No framework dependencies β€” pure JavaScript
  • Gemini Vision API for prescription image analysis
  • RxNorm proxy calls β€” all API traffic routed through Flask
  • LocalStorage for disclaimer acceptance tracking
  • Dual input modes β€” manual entry and prescription text parsing

πŸ“ System Design Diagrams

High-Level System Architecture

graph TB
 subgraph Client["πŸ–₯️ Client Browser"]
 UI["SPA Interface<br/>(Vanilla JS)"]
 IMG["Image Upload<br/>Module"]
 end
 
 subgraph Flask["βš™οΈ Flask Backend (app.py)"]
 API["REST API<br/>Endpoints"]
 NORM["Drug Normalizer<br/>(Levenshtein)"]
 VISION["Vision Proxy<br/>(Gemini)"]
 end
 
 subgraph Database["πŸ—„οΈ PostgreSQL"]
 DRUGS[("drugs<br/>table")]
 INTER[("interactions<br/>table")]
 ETL_STATE[("etl_sync_state<br/>table")]
 end
 
 subgraph External["🌐 External APIs"]
 RXNORM["RxNorm API<br/>(NLM)"]
 GEMINI["Gemini Vision<br/>(Google)"]
 FDA["openFDA API<br/>(Drug Labels)"]
 end
 
 subgraph ETL["πŸ”„ ETL Pipeline (etl.py)"]
 FETCH["FDA Fetcher"]
 LLM["LLM Router<br/>(Multi-Provider)"]
 LOADER["DB Loader"]
 end
 
 UI -->|"1. Drug Names"| API
 IMG -->|"2. Prescription Image"| VISION
 API -->|"3. Normalize"| NORM
 API -->|"4. Query Interactions"| INTER
 NORM -->|"5. Fuzzy Match"| DRUGS
 VISION -->|"6. OCR Request"| GEMINI
 API -->|"7. Proxy Request"| RXNORM
 
 FETCH -->|"8. Fetch Labels"| FDA
 FETCH -->|"9. Extract Text"| LLM
 LLM -->|"10. Structured Data"| LOADER
 LOADER -->|"11. Upsert"| DRUGS
 LOADER -->|"12. Upsert"| INTER
 LOADER -->|"13. Checkpoint"| ETL_STATE
 
 style Client fill:#e1f5ff
 style Flask fill:#fff4e1
 style Database fill:#e8f5e9
 style External fill:#fce4ec
 style ETL fill:#f3e5f5
Loading

Drug Interaction Check Flow

sequenceDiagram
 participant User
 participant Browser
 participant Flask
 participant RxNorm
 participant PostgreSQL
 
 User->>Browser: Enter drug names
 Browser->>Flask: POST /api/normalize-drug
 Flask->>Flask: Levenshtein fuzzy match<br/>(Indian brands)
 Flask-->>Browser: Normalized generic names
 
 Browser->>Flask: GET /api/rxnorm/...
 Flask->>RxNorm: Proxy request
 RxNorm-->>Flask: RxCUI + ingredients
 Flask-->>Browser: Drug metadata
 
 Browser->>Flask: POST /api/check-interactions
 Flask->>PostgreSQL: SELECT with trigram search<br/>(threshold 0.85)
 PostgreSQL-->>Flask: Matching drug IDs
 
 Flask->>PostgreSQL: Query interactions table<br/>(Cartesian product)
 PostgreSQL-->>Flask: Interaction rules
 Flask-->>Browser: Risk categorization
 
 Browser->>User: Display results dashboard
Loading

ETL Pipeline Architecture

flowchart TD
 START(["Start ETL"]) --> SEED{"Seed Mode?"}
 SEED -->|Yes| HARDCODE["Load INTERACTION_RULES<br/>from data.py"]
 SEED -->|No| CHECKPOINT["Read etl_sync_state<br/>last_successful_skip"]
 
 HARDCODE --> UPSERT_SEED["Upsert seed data<br/>to PostgreSQL"]
 UPSERT_SEED --> END(["End"])
 
 CHECKPOINT --> FETCH["Fetch batch from openFDA<br/>(skip + limit)"]
 FETCH --> PARSE["Extract drug names<br/>and interaction text"]
 PARSE --> LLM_ROUTE["LLM Router<br/>(Multi-Provider)"]
 
 LLM_ROUTE --> GROQ{"Groq API"}
 GROQ -->|Success| STRUCTURE["Structured JSON"]
 GROQ -->|Fail| CEREBRAS{"Cerebras API"}
 CEREBRAS -->|Success| STRUCTURE
 CEREBRAS -->|Fail| GEMINI{"Gemini API"}
 GEMINI -->|Success| STRUCTURE
 GEMINI -->|Fail| OPENROUTER{"OpenRouter API"}
 OPENROUTER -->|Success| STRUCTURE
 OPENROUTER -->|Fail| HF{"HuggingFace API"}
 HF -->|Success| STRUCTURE
 HF -->|Fail| ERROR["Log error & skip batch"]
 
 STRUCTURE --> UPSERT["Upsert drugs & interactions<br/>(idempotent)"]
 UPSERT --> UPDATE_CHECKPOINT["Update etl_sync_state<br/>(atomic transaction)"]
 UPDATE_CHECKPOINT --> MORE{"More batches?"}
 MORE -->|Yes| FETCH
 MORE -->|No| END
 ERROR --> END
 
 style START fill:#4caf50,color:#fff
 style END fill:#f44336,color:#fff
 style STRUCTURE fill:#2196f3,color:#fff
 style ERROR fill:#ff9800,color:#fff
Loading

Prescription Image Analysis Flow

sequenceDiagram
 participant User
 participant Browser
 participant Flask
 participant Gemini
 
 User->>Browser: Upload prescription image
 Browser->>Browser: Client-side preprocessing<br/>(resize, compress)
 Browser->>Flask: POST /api/extract-vision<br/>(multipart/form-data)
 Flask->>Gemini: Vision API request<br/>(Gemini 2.5 Flash)
 Gemini->>Gemini: Clinical OCR<br/>(Indian shorthand aware)
 Gemini-->>Flask: Extracted text
 Flask-->>Browser: Drug names + dosages
 
 Browser->>Browser: Parse multi-line text<br/>(group broken OCR)
 Browser->>Browser: Extract vitamins<br/>(A, C, D, E, K)
 Browser->>Browser: Normalize compounds<br/>(Omega 3, D3)
 Browser->>User: Populate drug input fields
Loading

Database Entity Relationship Diagram

erDiagram
 DRUGS ||--o{ INTERACTIONS : "drug_a_id"
 DRUGS ||--o{ INTERACTIONS : "drug_b_id"
 
 DRUGS {
 bigserial id PK
 citext generic_name UK
 jsonb brand_names
 }
 
 INTERACTIONS {
 bigint drug_a_id FK
 bigint drug_b_id FK
 text severity
 text description
 text action_text
 text source
 }
 
 ETL_SYNC_STATE {
 int id PK
 int last_successful_skip
 timestamptz updated_at
 }
Loading

LLM Router Failover Strategy

flowchart LR
 Start([Start]) --> Groq["Groq<br/>llama-3.3-70b-versatile<br/>(Primary)"]
 Groq -->|200 OK| Success([Success])
 Groq -->|429/Quota| Cerebras["Cerebras<br/>gpt-oss-120b<br/>(Fallback 1)"]
 
 Cerebras -->|200 OK| Success
 Cerebras -->|429/Quota| Gemini["Gemini<br/>gemini-2.5-flash<br/>(Fallback 2)"]
 
 Gemini -->|200 OK| Success
 Gemini -->|429/Quota| OpenRouter["OpenRouter<br/>free tier<br/>(Fallback 3)"]
 
 OpenRouter -->|200 OK| Success
 OpenRouter -->|429/Quota| HuggingFace["HuggingFace<br/>Meta-Llama-3-8B<br/>(Fallback 4)"]
 
 HuggingFace -->|200 OK| Success
 HuggingFace -->|All exhausted| Failure([Failure])
 
 style Start fill:#4caf50,color:#fff
 style Success fill:#2196f3,color:#fff
 style Failure fill:#f44336,color:#fff
 style Groq fill:#e3f2fd
 style Cerebras fill:#f3e5f5
 style Gemini fill:#fff3e0
 style OpenRouter fill:#e8f5e9
 style HuggingFace fill:#fce4ec
Loading

Security & Data Flow

graph LR
 subgraph Public["🌍 Public Zone"]
 USER["User Browser"]
 end
 
 subgraph DMZ["πŸ›‘οΈ Application Zone"]
 FLASK["Flask API<br/>(Port 5000)"]
 PROXY["RxNorm Proxy<br/>(No API key exposure)"]
 end
 
 subgraph Secure["πŸ”’ Secure Zone"]
 DB[("PostgreSQL<br/>(Connection Pool)")]
 RULES["Interaction Rules<br/>(Server-side only)"]
 end
 
 subgraph External["☁️ External Services"]
 RXNORM_API["RxNorm API"]
 GEMINI_API["Gemini API"]
 FDA_API["openFDA API"]
 end
 
 USER -->|"HTTPS"| FLASK
 FLASK -->|"Proxy"| PROXY
 PROXY -->|"API Key Hidden"| RXNORM_API
 FLASK -->|"Encrypted"| DB
 FLASK -->|"Load Rules"| RULES
 FLASK -->|"API Key Hidden"| GEMINI_API
 FLASK -->|"Public API"| FDA_API
 
 USER -.->|"❌ Never Exposed"| RULES
 USER -.->|"❌ Never Exposed"| DB
 
 style Public fill:#ffebee
 style DMZ fill:#fff3e0
 style Secure fill:#e8f5e9
 style External fill:#e3f2fd
Loading

πŸš€ Quick Start

Prerequisites

  • Python 3.8+
  • PostgreSQL 12+ with pg_trgm and citext extensions
  • pip package manager
  • At least one LLM API key (Groq, Cerebras, Gemini, OpenRouter, or HuggingFace)
  • Google Gemini API key (for prescription image analysis)

Installation

  1. Clone the repository

    git clone https://github.com/indiser/Rxcheck.git
    cd Rxcheck
  2. Create virtual environment

    python -m venv env
    source env/bin/activate # Windows: env\Scripts\activate
  3. Install dependencies

    pip install -r requirements.txt

    Dependencies include:

    • Flask β€” Web framework
    • psycopg2-binary β€” PostgreSQL adapter
    • python-dotenv β€” Environment variables
    • requests β€” HTTP client
    • groq β€” Groq LLM API
    • google-genai β€” Gemini API (Vision + LLM)
    • openai β€” OpenAI-compatible clients
    • huggingface_hub β€” HuggingFace API
    • pillow β€” Image processing
    • gunicorn, uvicorn β€” Production servers
  4. Set up PostgreSQL database

    createdb rxcheck
    psql rxcheck -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
    psql rxcheck -c "CREATE EXTENSION IF NOT EXISTS citext;"
  5. Configure environment variables

    # Create .env file
    cat > .env << EOF
    DATABASE_URL=postgresql://postgres:password@localhost:5432/rxcheck
    DB_POOL_MIN=1
    DB_POOL_MAX=10
    
    # At least one LLM API key required for ETL
    GROQ_API_KEY=your_groq_key_here
    CEREBRAS_API_KEY=your_cerebras_key_here
    GEMINI_API_KEY=your_gemini_key_here
    OPENROUTER_API_KEY=your_openrouter_key_here
    HUGGING_FACE_API_KEY=your_hf_key_here
    EOF
  6. Build catalog from CSV dataset (optional)

    # Generates brand_hints.json and price_catalog.json from CSV
    python build_catgory.py

    Note: Pre-built JSON files are included in the repository. Only run this if you have an updated CSV dataset.

  7. Initialize database schema

    python etl.py --seed-only
  8. Run the ETL pipeline (optional)

    # Fetch 100 FDA records and populate database
    python etl.py --limit 100
    # The pipeline automatically resumes from checkpoint on subsequent runs
    python etl.py --limit 100 # Fetches next 100 records
  9. Start the Flask application

    python app.py
  10. Open in browser

    http://localhost:5000
    

πŸ“ Project Structure

rxcheck/
β”œβ”€β”€ app.py # Flask REST API with connection pooling
β”œβ”€β”€ data.py # Business data (interaction rules, savings lookup)
β”œβ”€β”€ etl.py # openFDA ingestion pipeline with checkpointing
β”œβ”€β”€ llm_router.py # Multi-provider LLM failover router + Gemini Vision
β”œβ”€β”€ schema.sql # PostgreSQL DDL (drugs, interactions, etl_sync_state)
β”œβ”€β”€ build_catgory.py # CSVβ†’JSON catalog builder (brand hints + pricing)
β”œβ”€β”€ brand_hints.json # Auto-generated brandβ†’generic mappings
β”œβ”€β”€ price_catalog.json # Auto-generated generic drug pricing catalog
β”œβ”€β”€ updated_indian_medicine_data.csv # Source dataset for Indian medicines
β”œβ”€β”€ .env # Environment configuration (not in repo)
β”œβ”€β”€ .gitignore # Git ignore patterns
β”œβ”€β”€ requirements.txt # Python dependencies
β”œβ”€β”€ images/ # Website screenshots for README
β”‚ β”œβ”€β”€ home-screen.png
β”‚ β”œβ”€β”€ drug-input.png
β”‚ β”œβ”€β”€ prescription-upload.png
β”‚ β”œβ”€β”€ interaction-results.png
β”‚ β”œβ”€β”€ generic-alternatives.png
β”‚ └── emergency-warning.png
β”œβ”€β”€ templates/
β”‚ └── index.html # SPA shell with disclaimer modal
β”œβ”€β”€ static/
β”‚ β”œβ”€β”€ app.js # Client-side logic (image analysis, UI, API calls)
β”‚ └── styles.css # Design system with emergency warning styles
└── README.md # This file

πŸ“Š Catalog Builder

The build_catgory.py script processes a CSV dataset of Indian medicines to generate optimized JSON catalogs for fast server-side lookups.

Input Dataset

File: updated_indian_medicine_data.csv

Columns used:

  • name β€” Brand name (e.g., "Ascoril LS Syrup 100ml")
  • short_composition1 β€” Primary active ingredient
  • short_composition2 β€” Secondary active ingredient (if combination drug)
  • price β€” MRP in rupees
  • pack_size_label β€” Packaging description

Processing Pipeline

  1. Salt Normalization

    • Combines composition1 + composition2
    • Strips dosage information in parentheses
    • Converts to lowercase for case-insensitive matching
  2. Brand Family Extraction

    • Removes dosage forms (tablet, syrup, injection, etc.)
    • Strips numeric dosages (500mg, 10ml, etc.)
    • Preserves letter suffixes (LS, AT, CV, SR, etc.)
    • Example: "Ascoril LS Syrup 100ml" β†’ "ascoril ls"
  3. Dual-Key Strategy

    • Full brand family: "ascoril ls" β†’ "ambroxol + levosalbutamol + guaifenesin"
    • Base word: "ascoril" β†’ "ambroxol + levosalbutamol + guaifenesin"
    • Prevents false-positive overwrites (base word only saved if not already mapped)

Output Files

brand_hints.json

{
 "ascoril ls": "ambroxol + levosalbutamol + guaifenesin",
 "ascoril": "ambroxol + levosalbutamol + guaifenesin",
 "glycomet": "metformin",
 "glycomet gp": "metformin + glimepiride"
}

price_catalog.json

{
 "metformin": {
 "display": "Metformin",
 "alternatives": ["Glycomet 500mg Tablet"],
 "range": "Rs. 18 (strip of 10 tablets)"
 }
}

Usage

# Generate catalogs from CSV
python build_catgory.py
# Output:
# Success! Built high-precision catalog.
# - brand_hints.json (thousands of brand→generic mappings)
# - price_catalog.json (generic drug pricing database)

Note: Pre-built JSON files are included in the repository. Only regenerate if you have an updated CSV dataset.


πŸ”¬ How It Works

1. Drug Name Resolution

User Input β†’ Server-side Levenshtein Matching β†’ RxNorm API Lookup β†’ PostgreSQL Trigram Search

Two-tier precision strategy:

  • Tier 1: Exact CITEXT match (zero false positives)
  • Tier 2: Trigram similarity with threshold 0.85 (blocks "folic acid" β‰  "valproic acid")

2. Interaction Detection

Resolved Drugs β†’ PostgreSQL Query β†’ Interaction Rules β†’ Risk Categorization

Server-side evaluation:

  • All interaction rules stored in data.py (never sent to client)
  • PostgreSQL query with canonical drug pair ordering
  • Cartesian product resolution for multi-ingredient drugs

3. ETL Pipeline (openFDA Ingestion)

Checkpoint Read β†’ FDA API Fetch β†’ Text Extraction β†’ LLM Parsing β†’ PostgreSQL Upsert β†’ Checkpoint Write

Fault-tolerance guarantees:

  • Atomic checkpoint updates (same transaction as data writes)
  • Idempotent upserts (replaying batches creates no duplicates)
  • Multi-provider LLM failover (automatic quota/rate-limit handling)
  • Crash recovery (resumes from last successful batch)

4. Prescription Image Analysis

Photo Upload β†’ Gemini Vision API β†’ Text Extraction β†’ Drug Name Parsing

Processing pipeline:

  • Client-side image upload to /api/extract-vision
  • Gemini 2.5 Flash vision model for clinical OCR
  • Automatic drug name extraction with confidence scoring
  • Indian prescription shorthand recognition

5. Results Dashboard

  • Risk Grid: Safe / Caution / Dangerous counters
  • Interaction Cards: Severity badges with clinical guidance
  • Generic Alternatives: RxNorm-normalized names with Indian pricing
  • Save Money Table: Brand-to-generic comparison with PMBJP options

πŸ› οΈ Technology Stack

Backend

  • Flask 3.1+ β€” Lightweight WSGI web framework
  • psycopg2-binary β€” PostgreSQL adapter with connection pooling
  • python-dotenv β€” Environment variable management
  • requests β€” HTTP library for RxNorm proxy and FDA API
  • gunicorn β€” Production WSGI server
  • uvicorn β€” ASGI server for async workloads

ETL & AI

  • Groq β€” Primary LLM provider (llama-3.3-70b-versatile)
  • Cerebras β€” Fallback LLM (gpt-oss-120b)
  • Google Gemini (google-genai) β€” Fallback LLM (gemini-2.5-flash) + Vision API
  • OpenRouter (openai) β€” Free tier fallback
  • HuggingFace (huggingface_hub) β€” Final fallback (Meta-Llama-3-8B)
  • Pillow β€” Image preprocessing for prescription uploads

Database

  • PostgreSQL 12+ β€” Relational database with advanced text search
  • pg_trgm extension β€” Trigram similarity matching
  • citext extension β€” Case-insensitive text type

Frontend

  • Vanilla JavaScript β€” No framework overhead
  • Gemini Vision API β€” Cloud-based prescription image analysis
  • Custom CSS β€” Design system with CSS variables
  • Google Fonts β€” Syne, IBM Plex Mono, IBM Plex Sans

Data Processing

  • pandas β€” CSV data processing (build_catgory.py)
  • JSON β€” Catalog storage format (brand_hints.json, price_catalog.json)
  • CSV β€” Indian medicine dataset (updated_indian_medicine_data.csv)

πŸ”Œ API Reference

GET /api/rxnorm/<path>

Transparent proxy to https://rxnav.nlm.nih.gov/REST/<path>.

Example:

curl http://localhost:5000/api/rxnorm/rxcui.json?name=metformin

GET /api/local-data

Serve safe catalog data (brand hints, price catalog, savings lookup).

Response:

{
 "brand_hints": {"glycomet": "metformin", ...},
 "price_catalog": {"metformin": {...}, ...},
 "brand_savings_lookup": [...]
}

POST /api/normalize-drug

Server-side brand→generic normalization with Levenshtein fuzzy matching.

Request:

{
 "name": "Glycomet 500"
}

Response:

{
 "normalized": "metformin",
 "catalog": {
 "display": "Metformin",
 "alternatives": ["Metformin immediate-release", "Metformin sustained-release"],
 "range": "Rs. 18-110 per strip of 10 tablets"
 }
}

POST /api/check-interactions

Query PostgreSQL for interactions between resolved drugs.

Request:

{
 "resolved": [
 {
 "rawName": "Warfarin",
 "genericName": "warfarin",
 "ingredients": ["warfarin"]
 },
 {
 "rawName": "Aspirin",
 "genericName": "aspirin",
 "ingredients": ["aspirin"]
 }
 ]
}

Response:

{
 "findings": [
 {
 "level": "high",
 "pair": ["Warfarin", "Aspirin"],
 "message": "Higher bleeding risk when warfarin is combined with antiplatelet drugs or NSAIDs.",
 "action": "Avoid casual co-use and check INR/bleeding plan with the prescriber.",
 "source": "Internal Clinical Rules Engine"
 }
 ]
}

POST /api/extract-vision

Send a prescription image to Gemini Vision for clinical OCR.

Request:

multipart/form-data
- image: <binary image file>

Response:

{
 "text": "Ecosprin 75mg\nCombiflam\nMetformin 500mg SR"
}

πŸ“Š Database Schema

drugs Table

CREATE TABLE drugs (
 id BIGSERIAL PRIMARY KEY,
 generic_name CITEXT UNIQUE NOT NULL,
 brand_names JSONB DEFAULT '[]'::jsonb
);
CREATE INDEX idx_drugs_name_trgm ON drugs USING gin (generic_name gin_trgm_ops);
CREATE INDEX idx_drugs_brands ON drugs USING gin (brand_names jsonb_path_ops);

interactions Table

CREATE TABLE interactions (
 drug_a_id BIGINT NOT NULL REFERENCES drugs(id) ON DELETE CASCADE,
 drug_b_id BIGINT NOT NULL REFERENCES drugs(id) ON DELETE CASCADE,
 severity TEXT NOT NULL CHECK (severity IN ('high', 'medium', 'low')),
 description TEXT NOT NULL DEFAULT '',
 action_text TEXT NOT NULL DEFAULT '',
 source TEXT NOT NULL DEFAULT 'openFDA SPL',
 PRIMARY KEY (drug_a_id, drug_b_id),
 CHECK (drug_a_id < drug_b_id)
);
CREATE INDEX idx_interactions_drug_a ON interactions (drug_a_id);
CREATE INDEX idx_interactions_drug_b ON interactions (drug_b_id);

etl_sync_state Table

CREATE TABLE etl_sync_state (
 id INT PRIMARY KEY DEFAULT 1 CHECK (id = 1),
 last_successful_skip INT NOT NULL DEFAULT 0,
 updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

πŸ”„ ETL Pipeline Usage

Basic Commands

# Seed database from hardcoded rules only
python etl.py --seed-only
# Fetch 100 FDA records (resumes from checkpoint automatically)
python etl.py --limit 100
# Dry-run (fetch + parse, no database writes)
python etl.py --dry-run --limit 10
# Reset checkpoint to 0 (use with caution!)
python etl.py --reset-checkpoint
# Manual skip override (only if checkpoint is 0)
python etl.py --skip 500 --limit 100

ETL Pipeline Stages

  1. SEED β€” Bootstrap from data.py INTERACTION_RULES
  2. CHECKPOINT β€” Read etl_sync_state.last_successful_skip
  3. FETCH β€” Download batch from openFDA API
  4. PARSE β€” Extract drug names and interaction text
  5. LLM β€” Structure free-text with multi-provider router
  6. LOAD β€” Upsert drugs and interactions to PostgreSQL
  7. COMMIT β€” Atomic checkpoint update

Fault-Tolerance Features

  • Idempotent upserts β€” Replaying batches creates no duplicates
  • Atomic checkpoints β€” Checkpoint and data committed in same transaction
  • Crash recovery β€” Resumes from last successful batch
  • Multi-provider failover β€” Automatic LLM quota/rate-limit handling

πŸ€– LLM Router Configuration

The ETL pipeline uses a multi-provider LLM router for fault-tolerance. Configure API keys in .env:

# Primary (fastest, best for JSON extraction)
GROQ_API_KEY=your_groq_key_here
# Fallbacks (tried in order when primary fails)
CEREBRAS_API_KEY=your_cerebras_key_here
GEMINI_API_KEY=your_gemini_key_here
OPENROUTER_API_KEY=your_openrouter_key_here
HUGGING_FACE_API_KEY=your_hf_key_here

Provider Selection Logic:

  1. Groq (llama-3.3-70b-versatile) β€” Primary
  2. Cerebras (gpt-oss-120b) β€” Fallback on quota/rate-limit
  3. Gemini (gemini-2.5-flash) β€” Fallback on Cerebras failure
  4. OpenRouter (free tier) β€” Fallback on Gemini failure
  5. HuggingFace (Meta-Llama-3-8B) β€” Final fallback

Automatic Failover Triggers:

  • 429 (rate limit exceeded)
  • Quota exhausted
  • Capacity/overload errors
  • Transient 502/504 errors (retried once)

⚠️ Medical Disclaimer

RxCheck is an educational screening tool, not a substitute for professional medical advice.

This application:

  • ❌ Cannot replace clinical judgment or pharmacist consultation
  • ❌ May miss rare interactions, allergies, or contraindications
  • ❌ Does not account for patient-specific factors (age, weight, renal function, pregnancy)
  • ❌ Cannot verify local brand formulations or bioequivalence

Always confirm medication decisions with a qualified healthcare professional.


🎨 Design Philosophy

Security-First

  • Interaction rules never leave the server
  • PostgreSQL connection pooling prevents race conditions
  • High-precision matching eliminates false positives
  • RxNorm proxy prevents client-side API key exposure

Clinical Accuracy

  • Trigram threshold tuned to block dangerous substring matches
  • Server-side Levenshtein matching for Indian brand names
  • Emergency escalation warnings for high-risk interactions
  • Medical disclaimer modal with persistent acceptance

Data Integrity

  • Atomic ETL checkpoints (crash-safe)
  • Idempotent upserts (replay-safe)
  • Multi-provider LLM failover (quota-safe)
  • Canonical drug pair ordering (duplicate-safe)

User Privacy

  • No user accounts or authentication
  • No prescription data storage
  • Client-side image upload (processed server-side)
  • LocalStorage only for disclaimer acceptance

🀝 Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/clinical-rule-update)
  3. Commit your changes (git commit -m 'Add new interaction rule for X+Y')
  4. Push to the branch (git push origin feature/clinical-rule-update)
  5. Open a Pull Request

Areas for Contribution

  • Additional interaction rules with clinical references
  • More Indian brand-to-generic mappings
  • Prescription image analysis accuracy improvements
  • Internationalization (Hindi, Tamil, Bengali)
  • Database migration scripts
  • Additional LLM providers in router

πŸ“ License

This project is licensed under the MIT License β€” see the LICENSE file for details.


πŸ™ Acknowledgments

  • National Library of Medicine β€” RxNorm and RxNav APIs
  • U.S. FDA β€” openFDA drug label database
  • Google Gemini β€” Vision API for prescription image analysis
  • PostgreSQL β€” Advanced open-source database
  • Google Fonts β€” Syne, IBM Plex Mono, IBM Plex Sans typefaces
  • PMBJP (Jan Aushadhi) β€” Generic medicine pricing data
  • Groq, Cerebras, Google, OpenRouter, HuggingFace β€” LLM API providers

πŸ“§ Contact

For questions, bug reports, or feature requests:


🌟 Star History

If you find RxCheck useful, please consider giving it a ⭐ on GitHub!


Built with ❀️ for safer medication management in India

About

πŸ’Š Clinical drug interaction checker with automated FDA data ingestion | PostgreSQL + openFDA ETL | Multi-provider LLM router | Prescription OCR | Indian generic alternatives | PMBJP pricing | Fault-tolerant checkpointing | Server-side security | Privacy-first | No signup required

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /