Crawls MedlinePlus, builds a symptom→disease knowledge base, optionally maps symptoms to OTC ingredients (openFDA / FDA monographs), and serves a small Flask API. For research/education only — not medical advice.
I wanted a lightweight, reproducible pipeline for:
- pulling symptom lists from reputable public docs (MedlinePlus),
- turning them into a consolidated KB with simple weights,
- doing quick semantic lookup with sentence embeddings,
- and, if needed, suggesting OTC ingredient families with links to DailyMed labels.
- Crawl MedlinePlus A–Z encyclopedia pages and extract symptom sections per article.
- Merge per‑article items into per‑disease dictionaries with rough cue weights (e.g. “most common”, “may”, etc.).
- Build a KB (
kb_symptoms.json) and sentence embeddings (kb_symptom_embeddings.npz). - Given a free‑text input, rank likely diseases and show which KB symptoms matched.
- (Optional) Build a symptom→OTC‑ingredient map from openFDA and FDA monographs; surface a few DailyMed cards.
- Serve a Flask API with two endpoints:
/diagnoseand/assist.
services/
access_data.py # MedlinePlus crawler
build_kb.py # Build KB + embeddings
diagnose_disease.py # SymptomDiagnoser (semantic matching)
disease_symptom_expander.py # Utility for disease→symptom lists
openfda_client.py # Minimal openFDA label client
build_symptom_map_from_openfda.py# Symptom→OTC ingredient map
otc_service_dynamic.py # Runtime OTC recommender
app.py # Flask app (serves /diagnose, /assist)
Python 3.10+ recommended.
python -m venv .venv && source .venv/bin/activate # or use conda
pip install aiohttp async-timeout beautifulsoup4 lxml requests flask numpy torch sentence-transformers scikit-imageThis is the minimal end‑to‑end path. Some steps can take a while.
- Crawl MedlinePlus (writes
ency_symptoms.jsonanddisease_symptoms_merged.json):
python services/access_data.py- Build the KB and embeddings:
python services/build_kb.py
# → kb_symptoms.json, kb_symptom_embeddings.npz- Run the API server:
python app.py
# visit http://127.0.0.1:8000/health- Try a request:
curl -X POST http://127.0.0.1:8000/diagnose \
-H 'Content-Type: application/json' \
-d '{"text":"fever and dry cough with body aches"}'(Optional) Build the OTC map and hit /assist:
python services/build_symptom_map_from_openfda.py --auto --max-ings 200 --max-labels 25
curl -X POST http://127.0.0.1:8000/assist \
-H 'Content-Type: application/json' \
-d '{"text":"sore throat and nasal congestion"}'Crawler (services/access_data.py)
CONCURRENCY(default 4) — drop if you see throttling.limit_letters,limit_articles_per_letter— useful for dev slices.- Cue weights live in
CUE_WEIGHTS.
KB / embeddings (services/build_kb.py)
EMB_MODELdefaults toall-MiniLM-L6-v2. Swap to a domain model if you like.- Normalization is intentionally simple; tighten if you see duplicates.
Diagnosis (services/diagnose_disease.py)
- Scoring knobs:
TOPK_SYMPTOMS,MIN_SIM,ALPHA,BETA. - Model/device defaults to CPU; set CUDA if available.
openFDA
- Without an API key you’ll hit rate limits quickly. Keep page sizes small.
Body:
{ "text": "free‑text symptoms" }Response (example):
[
{ "disease": "influenza", "score": 2.73, "matched_symptoms": ["fever", "dry cough"] },
{ "disease": "common cold", "score": 2.11, "matched_symptoms": ["runny nose"] }
]Combines diagnosis with OTC ingredient candidates and a few DailyMed links.
Body:
{ "text": "sore throat and nasal congestion" }Abridged response:
{
"diagnosis": [ { "disease": "common cold", "score": 2.31 } ],
"top_disease": "common cold",
"selected_symptoms": ["sore throat", "nasal congestion"],
"otc_recommendations": { "results": [ { "symptom": "sore throat", "ingredients": [ {"ingredient":"benzocaine","score":3.4} ], "candidates": [ {"title":"...","label_url":"https://dailymed..."} ] } ] }
}- Keep parsing within section boundaries (avoid
find_all_nextover‑reach). - Add TF‑IDF‑like downweighting for very generic symptoms.
- Better negation handling (medspaCy/pyConTextNLP).
- Batch encode embeddings and add simple caching.
- A few unit tests for link extraction and section parsing wouldn’t hurt.
MIT. MedlinePlus®, DailyMed and openFDA content is subject to their own terms. This repo is not a medical device and does not provide medical advice.