notes for common opd condition with symptoms investigation and medicine
all of the above
mkdir -p /home/daytona/workspace/opd-notes && echo "dir ready"
~/opd-notes/generate_opd_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table,
TableStyle, HRFlowable, PageBreak, KeepTogether)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate
from reportlab.lib.colors import HexColor
import datetime
# ── Color palette ─────────────────────────────────────────────────────────────
C_NAVY = HexColor("#1A3557")
C_BLUE = HexColor("#2C6FAC")
C_LBLUE = HexColor("#D6E8F7")
C_TEAL = HexColor("#1D7874")
C_LTEAL = HexColor("#D0EFED")
C_RED = HexColor("#C0392B")
C_LRED = HexColor("#FAD7D3")
C_ORANGE = HexColor("#E67E22")
C_LORANGE = HexColor("#FDEBD0")
C_GREEN = HexColor("#1E8449")
C_LGREEN = HexColor("#D5F5E3")
C_PURPLE = HexColor("#6C3483")
C_LPURPLE = HexColor("#E8DAEF")
C_GREY = HexColor("#5D6D7E")
C_LGREY = HexColor("#F0F3F4")
C_WHITE = colors.white
C_BLACK = colors.black
OUTPUT = "/home/daytona/workspace/opd-notes/Common_OPD_Conditions_Notes.pdf"
PAGE_W, PAGE_H = A4
# ── Document setup ─────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=1.8*cm, rightMargin=1.8*cm,
topMargin=2.5*cm, bottomMargin=2.2*cm,
title="Common OPD Conditions – Medical Student Notes",
author="Orris Medical Notes"
)
styles = getSampleStyleSheet()
W = PAGE_W - 3.6*cm # usable width
# ── Custom styles ──────────────────────────────────────────────────────────────
def S(name, **kw):
return ParagraphStyle(name, **kw)
sTitle = S("sTitle", fontSize=26, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER, spaceAfter=4)
sSubtitle = S("sSubtitle", fontSize=13, fontName="Helvetica",
textColor=C_LBLUE, alignment=TA_CENTER, spaceAfter=2)
sDate = S("sDate", fontSize=9, fontName="Helvetica",
textColor=C_LBLUE, alignment=TA_CENTER)
sSectionHead = S("sSH", fontSize=15, fontName="Helvetica-Bold",
textColor=C_WHITE, spaceBefore=14, spaceAfter=6,
leftIndent=0, leading=20)
sCondHead = S("sCH", fontSize=12, fontName="Helvetica-Bold",
textColor=C_NAVY, spaceBefore=8, spaceAfter=4)
sLabel = S("sLbl", fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE)
sBody = S("sBody", fontSize=9, fontName="Helvetica",
textColor=C_BLACK, leading=13, spaceAfter=2)
sBullet = S("sBul", fontSize=9, fontName="Helvetica",
textColor=C_BLACK, leading=13, leftIndent=10, spaceAfter=1,
bulletIndent=2)
sSmall = S("sSmall", fontSize=8, fontName="Helvetica",
textColor=C_GREY, leading=11)
sWarn = S("sWarn", fontSize=8.5, fontName="Helvetica-Bold",
textColor=C_RED, leading=12, spaceAfter=3)
sNote = S("sNote", fontSize=8, fontName="Helvetica-Oblique",
textColor=C_GREY, leading=11, spaceAfter=4)
sFooter = S("sFooter", fontSize=7.5, fontName="Helvetica",
textColor=C_GREY, alignment=TA_CENTER)
story = []
# ══════════════════════════════════════════════════════════════════════════════
# HELPER FUNCTIONS
# ══════════════════════════════════════════════════════════════════════════════
def colored_banner(text, bg=C_NAVY, fg=C_WHITE, fs=15):
"""Full-width colored banner paragraph."""
st = S(f"banner_{text[:8]}", fontSize=fs, fontName="Helvetica-Bold",
textColor=fg, backColor=bg, alignment=TA_LEFT,
leftIndent=6, rightIndent=6, spaceBefore=10, spaceAfter=6,
leading=fs+6, borderPad=4)
return Paragraph(text, st)
def tag(text, bg=C_BLUE, fg=C_WHITE):
st = S(f"tag_{text[:6]}", fontSize=8, fontName="Helvetica-Bold",
textColor=fg, backColor=bg, borderPad=2,
leftIndent=3, rightIndent=3, leading=12)
return Paragraph(f" {text} ", st)
def pill_table(items, bg=C_LBLUE, fg=C_NAVY):
"""Render a list of short items as colored 'pills' in a wrapped table."""
cells = [[Paragraph(f"• {i}", S(f"pill_{i[:4]}", fontSize=8.5,
fontName="Helvetica", textColor=fg, leading=12)) for i in items]]
t = Table([cells[0]], colWidths=[W/len(items)]*len(items))
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("ROWBACKGROUNDS", (0,0), (-1,-1), [bg]),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
return t
def condition_table(rows, col_headers, col_widths, header_bg=C_BLUE):
"""Build a styled data table for a condition."""
header_style = [
ParagraphStyle(f"th{i}", fontSize=8.5, fontName="Helvetica-Bold",
textColor=C_WHITE, leading=11)
for i in range(len(col_headers))
]
cell_style = ParagraphStyle("td", fontSize=8.5, fontName="Helvetica",
textColor=C_BLACK, leading=12)
table_data = [[Paragraph(h, header_style[i]) for i, h in enumerate(col_headers)]]
for row in rows:
table_data.append([Paragraph(str(c), cell_style) for c in row])
t = Table(table_data, colWidths=col_widths, repeatRows=1)
style = TableStyle([
("BACKGROUND", (0,0), (-1,0), header_bg),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_LGREY]),
("GRID", (0,0), (-1,-1), 0.4, HexColor("#BFC9CA")),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
])
t.setStyle(style)
return t
def section_divider(title, icon="", bg=C_NAVY):
return colored_banner(f"{icon} {title}", bg=bg, fs=13)
def hr():
return HRFlowable(width="100%", thickness=0.5, color=C_GREY, spaceAfter=4, spaceBefore=4)
def sp(h=4):
return Spacer(1, h)
# ══════════════════════════════════════════════════════════════════════════════
# COVER PAGE
# ══════════════════════════════════════════════════════════════════════════════
cover_bg = Table(
[[Paragraph("<b>COMMON OPD CONDITIONS</b>", sTitle)],
[Paragraph("Medical Student Quick-Reference Notes", sSubtitle)],
[Paragraph("General Medicine · Pediatrics · Gynecology", sSubtitle)],
[Spacer(1, 6)],
[Paragraph(f"Compiled: {datetime.date.today().strftime('%B %Y')}", sDate)],
],
colWidths=[W]
)
cover_bg.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 14),
("RIGHTPADDING", (0,0), (-1,-1), 14),
("ROUNDEDCORNERS", [8]),
]))
story += [sp(30), cover_bg, sp(20)]
# Scope note
scope = Table([[
Paragraph(
"These notes cover <b>15 common OPD presentations</b> across General Medicine, "
"Pediatrics, and Gynecology. Each condition includes: <b>Definition → Symptoms → "
"Investigations → Treatment / Medicines → Key Points</b>. "
"Doses are standard adult/pediatric guidelines; always verify locally.",
S("scope", fontSize=9, fontName="Helvetica", textColor=C_NAVY,
leading=13, alignment=TA_JUSTIFY))
]], colWidths=[W])
scope.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_LBLUE),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("BOX", (0,0), (-1,-1), 1, C_BLUE),
]))
story += [scope, PageBreak()]
# ══════════════════════════════════════════════════════════════════════════════
# INDEX / TOC (manual)
# ══════════════════════════════════════════════════════════════════════════════
story += [section_divider("TABLE OF CONTENTS", "📋", C_TEAL), sp(8)]
toc_data = [
["#", "Condition", "Section"],
["1", "Upper Respiratory Tract Infection (URTI)", "General Medicine"],
["2", "Acute Gastroenteritis / Diarrhea", "General Medicine"],
["3", "Essential Hypertension", "General Medicine"],
["4", "Type 2 Diabetes Mellitus", "General Medicine"],
["5", "Community-Acquired Pneumonia (CAP)", "General Medicine"],
["6", "Urinary Tract Infection (UTI)", "General Medicine"],
["7", "Peptic Ulcer Disease / GERD", "General Medicine"],
["8", "Anemia (Iron-Deficiency)", "General Medicine"],
["9", "Allergic Rhinitis", "General Medicine"],
["10", "Acute Febrile Illness / Fever", "General Medicine"],
["11", "Childhood Fever & Common Cold", "Pediatrics"],
["12", "Acute Otitis Media (AOM)", "Pediatrics"],
["13", "Bronchial Asthma (Pediatric)", "Pediatrics"],
["14", "Dysmenorrhea", "Gynecology"],
["15", "Vaginitis / Vaginal Discharge", "Gynecology"],
]
toc_style_h = ParagraphStyle("toc_h", fontSize=9, fontName="Helvetica-Bold", textColor=C_WHITE, leading=12)
toc_style_n = ParagraphStyle("toc_n", fontSize=9, fontName="Helvetica", textColor=C_BLACK, leading=13)
toc_style_s = ParagraphStyle("toc_s", fontSize=9, fontName="Helvetica-Oblique", textColor=C_GREY, leading=13)
toc_rows = [[Paragraph(toc_data[0][0], toc_style_h),
Paragraph(toc_data[0][1], toc_style_h),
Paragraph(toc_data[0][2], toc_style_h)]]
for r in toc_data[1:]:
toc_rows.append([Paragraph(r[0], toc_style_n),
Paragraph(r[1], toc_style_n),
Paragraph(r[2], toc_style_s)])
toc_t = Table(toc_rows, colWidths=[0.8*cm, 11.5*cm, 4*cm])
toc_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_TEAL),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_LGREY]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#BFC9CA")),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story += [toc_t, PageBreak()]
# ══════════════════════════════════════════════════════════════════════════════
# CONDITION CARD BUILDER
# ══════════════════════════════════════════════════════════════════════════════
def build_condition(num, title, section_color,
definition, symptoms, investigations, treatment, key_points,
red_flags=None):
"""Build a full condition card."""
block = []
# Title bar
title_bar = Table([[
Paragraph(f"<b>{num}. {title}</b>",
S(f"ct{num}", fontSize=13, fontName="Helvetica-Bold",
textColor=C_WHITE, leading=16)),
]], colWidths=[W])
title_bar.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), section_color),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
block.append(title_bar)
block.append(sp(4))
# Definition
block.append(Paragraph(f"<b>Definition:</b> {definition}", sBody))
block.append(sp(3))
# Symptoms | Investigations (two-column layout)
sym_items = "".join(f"• {s}<br/>" for s in symptoms)
inv_items = "".join(f"• {i}<br/>" for i in investigations)
col_head = ParagraphStyle("ch", fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, leading=12)
col_body = ParagraphStyle("cb", fontSize=8.5, fontName="Helvetica",
textColor=C_BLACK, leading=13)
si_table = Table([
[Paragraph("SYMPTOMS", col_head), Paragraph("INVESTIGATIONS", col_head)],
[Paragraph(sym_items, col_body), Paragraph(inv_items, col_body)],
], colWidths=[W/2 - 2*mm, W/2 - 2*mm])
si_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), C_TEAL),
("BACKGROUND", (1,0), (1,0), C_BLUE),
("BACKGROUND", (0,1), (0,1), C_LTEAL),
("BACKGROUND", (1,1), (1,1), C_LBLUE),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "TOP"),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#BFC9CA")),
]))
block.append(si_table)
block.append(sp(4))
# Treatment table
block.append(Paragraph("TREATMENT & MEDICINES", col_head.__class__(
"treat_h", fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, backColor=C_ORANGE, leading=14,
leftIndent=6, spaceBefore=2, spaceAfter=0, borderPad=3)))
treat_header_st = ParagraphStyle("tth", fontSize=8.5, fontName="Helvetica-Bold",
textColor=C_WHITE, leading=11)
treat_cell_st = ParagraphStyle("ttc", fontSize=8.5, fontName="Helvetica",
textColor=C_BLACK, leading=12)
treat_rows = [[Paragraph("Category", treat_header_st),
Paragraph("Drug / Measure", treat_header_st),
Paragraph("Dose / Note", treat_header_st)]]
for cat, drug, dose in treatment:
treat_rows.append([Paragraph(cat, treat_cell_st),
Paragraph(f"<b>{drug}</b>", treat_cell_st),
Paragraph(dose, treat_cell_st)])
treat_t = Table(treat_rows, colWidths=[3.5*cm, 5.5*cm, W - 9*cm])
treat_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_ORANGE),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_LORANGE]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#BFC9CA")),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
block.append(treat_t)
block.append(sp(4))
# Red flags
if red_flags:
rf_text = " 🚨 <b>RED FLAGS (Refer/Admit):</b> " + " | ".join(red_flags)
rf_table = Table([[Paragraph(rf_text,
S("rf", fontSize=8.5, fontName="Helvetica-Bold",
textColor=C_RED, leading=12))]], colWidths=[W])
rf_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_LRED),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("BOX", (0,0), (-1,-1), 0.8, C_RED),
]))
block.append(rf_table)
block.append(sp(4))
# Key points
kp_items = "".join(f"✔ {k}<br/>" for k in key_points)
kp_table = Table([[Paragraph(
f"<b>Key Points</b><br/>{kp_items}",
S("kp", fontSize=8.5, fontName="Helvetica", textColor=C_PURPLE,
leading=13, leftIndent=4))]], colWidths=[W])
kp_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_LPURPLE),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("BOX", (0,0), (-1,-1), 0.6, C_PURPLE),
]))
block.append(kp_table)
block.append(sp(10))
return KeepTogether(block) if len(block) < 20 else block
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1: GENERAL MEDICINE
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_divider("SECTION 1 — GENERAL MEDICINE OPD", "🩺", C_NAVY))
story.append(sp(6))
# ─── 1. URTI ──────────────────────────────────────────────────────────────────
story += build_condition(
1, "Upper Respiratory Tract Infection (URTI)", C_BLUE,
definition="Self-limiting viral infection of the nose, pharynx, larynx, and sinuses. "
"Commonest cause: Rhinovirus.",
symptoms=[
"Nasal congestion / rhinorrhea",
"Sore throat, hoarseness",
"Low-grade fever (< 38.5 °C)",
"Sneezing, watery eyes",
"Mild headache, malaise",
"Cough (dry or productive)",
],
investigations=[
"Usually CLINICAL diagnosis",
"CBC: mild leukocytosis (viral) or normal",
"Throat swab C/S: if bacterial suspected",
"Rapid Strep test (if Group A Strep suspected)",
"X-ray sinuses: if sinusitis suspected",
],
treatment=[
("Symptomatic", "Paracetamol", "500–1000 mg PO Q6H PRN (max 4 g/day)"),
("Symptomatic", "Ibuprofen", "400 mg PO TDS with food (anti-inflammatory)"),
("Decongestant", "Pseudoephedrine", "60 mg PO BD–TDS (short term ≤ 5 days)"),
("Antihistamine","Cetirizine / Loratadine", "10 mg PO OD (for rhinorrhea/sneezing)"),
("Throat", "Benzydamine gargle", "15 mL gargle Q3H for sore throat"),
("Antibiotic", "Amoxicillin (if bacterial)", "500 mg PO TDS × 5–7 days (Strep pharyngitis)"),
("Cough", "Honey + warm fluids", "1st line; Dextromethorphan 15 mg Q4H PRN"),
("General", "Rest, hydration, humidification", "Mainstay of viral URTI management"),
],
red_flags=["High fever > 39 °C", "Stridor / difficulty breathing", "Muffled voice (peritonsillar abscess)", "Neck stiffness"],
key_points=[
"Antibiotics NOT indicated for viral URTI — avoid overuse",
"Most URTIs resolve in 7–10 days without antibiotics",
"Amoxicillin given only if Strep throat confirmed or strongly suspected",
"Nasal saline irrigation is safe and effective",
]
)
# ─── 2. ACUTE GASTROENTERITIS ─────────────────────────────────────────────────
story += build_condition(
2, "Acute Gastroenteritis / Acute Diarrhea", C_TEAL,
definition="Inflammation of the GI tract causing diarrhea (≥3 loose stools/day) with or without vomiting. "
"Commonest causes: Rotavirus (children), E. coli, Salmonella, Norovirus.",
symptoms=[
"Diarrhea — watery / loose stools",
"Nausea and vomiting",
"Abdominal cramps / pain",
"Fever (mild to moderate)",
"Dehydration: dry mouth, decreased urine",
"Bloody stools (invasive organisms)",
],
investigations=[
"Clinical assessment for dehydration (severity)",
"Stool R/E + C/S (if bloody, prolonged > 3 days)",
"CBC: leukocytosis suggests bacterial",
"Electrolytes / RFT (if moderate-severe dehydration)",
"Stool for Ova & Parasites (chronic cases)",
"Blood culture (if systemic illness)",
],
treatment=[
("Rehydration", "ORS (Oral Rehydration Salts)", "200 mL after each loose stool; 2–4 L/day"),
("IV Fluids", "Ringer's Lactate / Normal Saline", "If unable to tolerate oral; severe dehydration"),
("Antiemetic", "Ondansetron", "4–8 mg PO/IV Q8H; Domperidone 10 mg TDS"),
("Antidiarrheal","Loperamide", "4 mg initial, then 2 mg after each stool (adults only, NOT in bloody diarrhea)"),
("Probiotic", "Lactobacillus / Saccharomyces", "Shorten duration by 1 day"),
("Antibiotic", "Ciprofloxacin", "500 mg BD × 3–5 days (bacterial, bloody stools)"),
("Antibiotic", "Metronidazole", "400 mg TDS × 7 days (Giardia / Amoebiasis)"),
("Zinc", "Zinc sulfate", "20 mg/day × 14 days (pediatric adjunct)"),
],
red_flags=["Severe dehydration (sunken eyes, no tears)", "Bloody diarrhea with high fever", "Signs of peritonitis", "Infants < 6 months dehydrated"],
key_points=[
"ORS is the cornerstone — start immediately",
"Antibiotics reserved for bacterial, bloody, or cholera-like diarrhea",
"Avoid loperamide in children < 2 years and bloody diarrhea",
"BRAT diet (Banana, Rice, Apple, Toast) for recovery",
]
)
# ─── 3. HYPERTENSION ──────────────────────────────────────────────────────────
story += build_condition(
3, "Essential Hypertension", C_RED,
definition="Persistently elevated BP ≥ 130/80 mmHg (ACC/AHA 2017) or ≥ 140/90 mmHg (JNC 7/WHO) on ≥ 2 separate visits. "
"Essential = no identifiable cause (>90% of cases).",
symptoms=[
"Usually ASYMPTOMATIC ('silent killer')",
"Headache (occipital, morning)",
"Dizziness / lightheadedness",
"Palpitations",
"Visual disturbances (hypertensive retinopathy)",
"Epistaxis (nosebleeds)",
"Tinnitus",
],
investigations=[
"BP measurement: both arms, 2 visits",
"ECG: LVH, arrhythmia",
"Echocardiogram: LV hypertrophy",
"CBC, RFT, electrolytes",
"Urine R/E: proteinuria (renal damage)",
"Lipid profile, FBS",
"Fundoscopy: Keith-Wagener grading",
"24-hr urine: if secondary HTN suspected",
],
treatment=[
("Lifestyle", "DASH diet, low Na+ diet", "Na < 2.3 g/day; weight loss, exercise"),
("1st line", "Amlodipine (CCB)", "5–10 mg PO OD"),
("1st line", "Enalapril / Ramipril (ACE-I)", "5–20 mg PO OD; preferred in DM+HTN"),
("1st line", "Losartan / Telmisartan (ARB)", "50–100 mg PO OD; use if ACE-I cough"),
("1st line", "Hydrochlorothiazide (Thiazide)", "12.5–25 mg PO OD"),
("2nd line", "Atenolol / Metoprolol (Beta-blocker)","25–100 mg PO OD (avoid in asthma)"),
("Combination", "ACE-I + CCB or ACE-I + Thiazide", "For stage 2 HTN or uncontrolled single agent"),
("Hypertensive urgency", "Amlodipine 10 mg PO", "BP 180/110 without end-organ damage"),
],
red_flags=["BP > 180/120 + headache/confusion (hypertensive emergency)", "Chest pain + high BP", "Focal neuro deficit", "Acute pulmonary edema"],
key_points=[
"First-line: ANY of ACE-I, ARB, CCB, or Thiazide (individualized)",
"ACE-I / ARB preferred in diabetics with proteinuria",
"Beta-blockers preferred in heart failure, post-MI, or angina",
"Target BP: < 130/80 mmHg (most patients); < 140/90 in elderly",
"Medication adherence + lifestyle is key — treat for LIFE",
]
)
# ─── 4. TYPE 2 DIABETES ───────────────────────────────────────────────────────
story += build_condition(
4, "Type 2 Diabetes Mellitus (T2DM)", C_ORANGE,
definition="Chronic metabolic disorder with relative insulin deficiency and insulin resistance. "
"Diagnosed when FBS ≥ 126 mg/dL, RBS ≥ 200 mg/dL + symptoms, or HbA1c ≥ 6.5%.",
symptoms=[
"Polyuria (excessive urination)",
"Polydipsia (excessive thirst)",
"Polyphagia (excessive hunger)",
"Weight loss (unintentional)",
"Fatigue, weakness",
"Blurred vision",
"Recurrent infections (skin, UTI)",
"Slow wound healing",
"Paresthesia (tingling in hands/feet)",
],
investigations=[
"FBS: ≥ 126 mg/dL (fasting ≥ 8 hrs)",
"RBS: ≥ 200 mg/dL with symptoms",
"HbA1c: ≥ 6.5% (diagnosis); target < 7%",
"OGTT: 2-hr glucose ≥ 200 mg/dL",
"Lipid profile, RFT, LFT",
"Urine Albumin-Creatinine Ratio",
"Fundoscopy (diabetic retinopathy)",
"ECG, foot examination",
],
treatment=[
("Lifestyle", "Diet + Exercise", "Low-GI diet; 150 min/week moderate exercise"),
("1st line", "Metformin", "500 mg BD → titrate up to 2000 mg/day with meals"),
("2nd line", "Glipizide / Glibenclamide (SU)","2.5–10 mg OD before breakfast (risk of hypoglycemia)"),
("2nd line", "Sitagliptin (DPP-4i)", "100 mg PO OD (weight neutral)"),
("Cardioprotect","Empagliflozin (SGLT-2i)", "10–25 mg OD (CV + renal benefit in T2DM)"),
("GLP-1", "Liraglutide / Dulaglutide", "SC injection; weight loss + CV benefit"),
("Insulin", "Basal insulin (Glargine)", "Start 10 U SC at bedtime; if HbA1c uncontrolled"),
("BP control", "ACE-I / ARB", "Mandatory if HTN + DM or proteinuria"),
],
red_flags=["Diabetic ketoacidosis (DKA): vomiting, fruity breath, altered consciousness", "Hypoglycemia: glucose < 70 mg/dL", "Hyperglycemic hyperosmolar state (HHS)"],
key_points=[
"Metformin is 1st line unless contraindicated (eGFR < 30)",
"HbA1c target: < 7% for most; < 8% in elderly / frail",
"SGLT-2 inhibitors and GLP-1 agonists reduce cardiovascular events",
"Monitor: HbA1c Q3 months (uncontrolled), Q6 months (stable)",
"Diabetic foot, retinal, and renal screening annually",
]
)
story.append(PageBreak())
# ─── 5. COMMUNITY-ACQUIRED PNEUMONIA ─────────────────────────────────────────
story += build_condition(
5, "Community-Acquired Pneumonia (CAP)", C_NAVY,
definition="Acute infection of the lung parenchyma acquired outside hospital. "
"Commonest cause: Streptococcus pneumoniae.",
symptoms=[
"Fever with chills and rigors",
"Productive cough (rusty / mucopurulent sputum)",
"Pleuritic chest pain",
"Dyspnea / tachypnea",
"Dullness on percussion",
"Bronchial breath sounds, crepitations",
"Fatigue, myalgia",
],
investigations=[
"CXR: lobar / segmental consolidation",
"CBC: leukocytosis, neutrophilia",
"CRP, ESR (elevated)",
"Sputum C/S + Gram stain",
"Blood culture × 2 (before antibiotics)",
"SpO2 / ABG (if dyspneic)",
"Urine Pneumococcal / Legionella antigen",
"CURB-65 score (severity assessment)",
],
treatment=[
("Mild CAP (outpatient)", "Amoxicillin", "500 mg PO TDS × 5–7 days"),
("Mild CAP (outpatient)", "Azithromycin", "500 mg PO OD × 5 days (atypical cover)"),
("Moderate CAP", "Co-Amoxiclav + Azithromycin", "875/125 mg BD + 500 mg OD × 7 days"),
("Severe CAP (hospital)","IV Ceftriaxone", "1–2 g IV OD + Azithromycin 500 mg IV/PO"),
("O2 therapy", "Supplemental O2", "Target SpO2 > 94%"),
("Antipyretic", "Paracetamol", "1 g QID"),
("Bronchodilator", "Salbutamol nebulization","If wheeze present"),
("Prevention", "Pneumococcal vaccine", "PCV13 + PPSV23 (elderly, immunocompromised)"),
],
red_flags=["CURB-65 ≥ 3 (confusion, urea >7, RR >30, BP <90/60, age ≥65)", "SpO2 < 90%", "Bilateral pneumonia", "Sepsis"],
key_points=[
"CURB-65 score guides outpatient vs inpatient decision",
"Always cover atypicals (Mycoplasma, Legionella) in CAP",
"Blood cultures BEFORE starting antibiotics",
"Duration: 5 days mild CAP; 7 days moderate; 10 days severe",
]
)
# ─── 6. UTI ───────────────────────────────────────────────────────────────────
story += build_condition(
6, "Urinary Tract Infection (UTI)", C_TEAL,
definition="Infection of any part of the urinary tract (bladder = cystitis; kidneys = pyelonephritis). "
"Commonest organism: E. coli (80%).",
symptoms=[
"Dysuria (burning on urination)",
"Frequency and urgency",
"Suprapubic pain / discomfort",
"Hematuria (visible or microscopic)",
"Cloudy / foul-smelling urine",
"Fever + loin pain (pyelonephritis)",
"Nausea, vomiting (upper UTI)",
],
investigations=[
"Urine dipstick: nitrites, leucocyte esterase",
"Urine R/E: pus cells > 5/HPF",
"Urine C/S (mid-stream specimen): gold standard",
"CBC: leukocytosis (pyelonephritis)",
"RFT (upper UTI / recurrent UTI)",
"USS KUB: if recurrent or upper UTI",
"Blood culture (if sepsis suspected)",
],
treatment=[
("Uncomplicated (F)", "Nitrofurantoin", "100 mg BD × 5–7 days (avoid in pyelonephritis)"),
("Uncomplicated (F)", "Trimethoprim", "200 mg BD × 7 days (if susceptible)"),
("Alternative", "Fosfomycin", "3 g PO single dose (uncomplicated cystitis)"),
("Complicated / male","Ciprofloxacin", "500 mg BD × 7–14 days"),
("Pyelonephritis", "Ceftriaxone", "1–2 g IV OD × 10–14 days"),
("Analgesic", "Phenazopyridine / Paracetamol","For dysuria relief (short-term)"),
("Alkalinizer", "Potassium citrate", "10 mL TDS in water (urine alkalization)"),
("Fluids", "Increase fluid intake", "2–3 L/day; void frequently"),
],
red_flags=["High fever + rigors + loin pain (pyelonephritis)", "Recurrent UTI (≥3 per year)", "UTI in men (always investigate further)", "Pregnancy with UTI"],
key_points=[
"Nitrofurantoin: NOT for pyelonephritis (poor tissue penetration)",
"Send urine C/S before antibiotics in complicated UTI",
"UTI in pregnancy always treated (risk of pyelonephritis)",
"Prophylactic low-dose Trimethoprim for recurrent UTI in women",
]
)
story.append(PageBreak())
# ─── 7. PEPTIC ULCER / GERD ───────────────────────────────────────────────────
story += build_condition(
7, "Peptic Ulcer Disease (PUD) / GERD", C_ORANGE,
definition="PUD: mucosal break in stomach/duodenum > 5 mm caused by H. pylori (70%) or NSAIDs. "
"GERD: retrograde acid into esophagus causing heartburn.",
symptoms=[
"Epigastric pain (burning / gnawing)",
"Heartburn, acid regurgitation (GERD)",
"Nausea, vomiting, bloating",
"Relief with food (DU) / worse with food (GU)",
"Night-time pain waking from sleep (DU)",
"Dysphagia (GERD / stricture)",
"Hematemesis / melena (bleeding ulcer)",
],
investigations=[
"Upper GI Endoscopy: gold standard",
"H. pylori: urea breath test, stool antigen, CLO test",
"CBC (if bleeding: anemia)",
"Barium meal swallow (if endoscopy not available)",
"Serum gastrin (if Zollinger-Ellison suspected)",
],
treatment=[
("PPI (mainstay)", "Omeprazole / Pantoprazole", "20–40 mg PO OD before breakfast × 4–8 weeks"),
("PPI", "Rabeprazole / Lansoprazole", "20 mg / 30 mg PO OD; equivalent efficacy"),
("H2 Blocker", "Ranitidine / Famotidine", "150 mg BD or 300 mg OD (alternative to PPI)"),
("H. pylori", "Triple Therapy (1st line)", "PPI + Amoxicillin 1g + Clarithromycin 500 mg — all BD × 14 days"),
("H. pylori (alt)", "Bismuth Quadruple", "PPI + Bismuth + Tetracycline + Metronidazole × 14 days"),
("Antacid", "Aluminium hydroxide", "30 mL PO TDS between meals (symptom relief)"),
("Mucosal protector","Sucralfate", "1 g QID 1 hr before meals (coating agent)"),
("Lifestyle", "Avoid NSAIDs, alcohol, smoking", "Elevate head of bed; small frequent meals"),
],
red_flags=["Hematemesis / melena", "Dysphagia", "Unintentional weight loss (malignancy)", "Vomiting after meals (obstruction)"],
key_points=[
"Test and treat for H. pylori in all peptic ulcer patients",
"PPI is superior to H2 blocker for healing and maintenance",
"NSAIDs should be stopped; if essential — add PPI prophylactically",
"Confirm H. pylori eradication 4 weeks after completing therapy",
]
)
# ─── 8. IRON-DEFICIENCY ANEMIA ────────────────────────────────────────────────
story += build_condition(
8, "Iron-Deficiency Anemia (IDA)", C_RED,
definition="Anemia caused by inadequate iron stores, resulting in microcytic hypochromic red cells. "
"Commonest cause of anemia worldwide. Hb < 12 g/dL (women) / < 13 g/dL (men).",
symptoms=[
"Fatigue, weakness, lethargy",
"Pallor (conjunctiva, palms, nails)",
"Palpitations, dyspnea on exertion",
"Headache, dizziness",
"Koilonychia (spoon-shaped nails)",
"Pica (craving non-food items)",
"Glossitis, angular stomatitis",
"Brittle hair and nails",
],
investigations=[
"CBC: low Hb, MCV < 80 fL, MCH < 27 pg",
"Peripheral smear: microcytic hypochromic",
"Serum ferritin: < 12 µg/L (most sensitive)",
"Serum iron: low; TIBC: elevated",
"Transferrin saturation: < 16%",
"Reticulocyte count (response to therapy)",
"Stool for occult blood (if male or post-menopausal)",
"Endoscopy (if GI blood loss suspected)",
],
treatment=[
("1st line oral", "Ferrous sulfate", "200 mg (65 mg elemental iron) TDS before meals × 3–6 months"),
("Alternative", "Ferrous gluconate / fumarate", "Less GI side-effects; same efficacy"),
("Absorption", "Vitamin C (ascorbic acid)", "500 mg with iron tablet to enhance absorption"),
("IV iron", "Iron sucrose / Ferric carboxymaltose", "If oral intolerant, severe anemia, malabsorption"),
("Dietary", "Iron-rich foods", "Red meat, leafy greens, fortified cereals"),
("Avoid", "Tea/coffee with iron tablets","Tannins reduce absorption — space 2 hrs"),
("Transfusion", "Packed Red Cells", "If Hb < 7 g/dL with symptoms or hemodynamic instability"),
("Treat cause", "Address underlying cause", "GI bleed, menorrhagia, dietary deficiency"),
],
red_flags=["Hb < 7 g/dL", "Severe dyspnea or chest pain", "Rapid fall in Hb", "Occult GI blood loss (colon cancer)"],
key_points=[
"Treat the underlying cause, not just the anemia",
"Continue iron for 3 months AFTER Hb normalizes to replenish stores",
"Ferritin is the best single test for iron stores",
"IV iron preferred in IBD, CKD, pregnancy (2nd trimester+)",
]
)
story.append(PageBreak())
# ─── 9. ALLERGIC RHINITIS ─────────────────────────────────────────────────────
story += build_condition(
9, "Allergic Rhinitis", C_GREEN,
definition="IgE-mediated inflammation of the nasal mucosa triggered by allergens (pollen, dust mites, pet dander). "
"Seasonal or perennial. Linked to asthma ('one airway' concept).",
symptoms=[
"Nasal congestion",
"Sneezing (especially in the morning)",
"Watery nasal discharge",
"Nasal pruritus",
"Itchy, watery, red eyes (allergic conjunctivitis)",
"Post-nasal drip, throat clearing",
"Reduced smell (anosmia in severe cases)",
"'Allergic salute' (upward rubbing of nose in children)",
],
investigations=[
"Primarily CLINICAL diagnosis",
"Nasal smear: eosinophils > 20%",
"Skin prick test (SPT): identifies specific allergens",
"Serum specific IgE (RAST/ELISA)",
"CBC: eosinophilia",
"Nasal endoscopy (if polyps suspected)",
],
treatment=[
("1st line", "Intranasal corticosteroid", "Fluticasone / Mometasone 2 sprays/nostril OD (most effective)"),
("Antihistamine", "Cetirizine / Loratadine", "10 mg PO OD (non-sedating; 2nd gen)"),
("Antihistamine", "Chlorpheniramine", "4 mg TDS (sedating; 1st gen — avoid driving)"),
("Decongestant", "Xylometazoline nasal spray", "< 3 days use only (rebound congestion)"),
("Leukotriene", "Montelukast", "10 mg PO OD (especially if asthma co-exists)"),
("Mast cell stab.", "Sodium Cromoglycate spray", "1 spray/nostril QID — prophylactic, less potent"),
("Immunotherapy", "SCIT / SLIT", "For persistent moderate-severe; allergen-specific"),
("Avoid allergen", "Environmental measures", "Dust covers, HEPA filters, pet avoidance"),
],
red_flags=["Associated asthma exacerbation", "Sinusitis / facial pain", "Nasal polyps", "Anosmia"],
key_points=[
"Intranasal steroids are the MOST effective treatment for allergic rhinitis",
"Antihistamines better for sneezing/itch; steroids better for congestion",
"Allergen avoidance is the foundation of management",
"Treat co-existing asthma (unified airway disease)",
]
)
# ─── 10. ACUTE FEBRILE ILLNESS ────────────────────────────────────────────────
story += build_condition(
10, "Acute Febrile Illness (Fever)", C_RED,
definition="Fever = oral temperature > 37.5 °C / rectal > 38 °C. A symptom, not a diagnosis. "
"Common causes: viral infections, malaria, typhoid, dengue, bacterial infections.",
symptoms=[
"Elevated temperature",
"Chills and rigors",
"Headache, myalgia",
"Tachycardia",
"Diaphoresis (sweating)",
"Loss of appetite, malaise",
"History-guided: rash (dengue), jaundice (malaria/typhoid), URI symptoms",
],
investigations=[
"CBC: WBC differential (viral vs bacterial)",
"Malaria RDT / peripheral smear (in endemic areas)",
"Dengue NS1 Ag, IgM/IgG (Day 1–5)",
"Typhoid: Widal test (> 1:160), Blood culture",
"CRP, ESR (inflammatory markers)",
"LFT, RFT (if systemic illness)",
"Blood culture (if sepsis suspected)",
"CXR (if respiratory symptoms)",
],
treatment=[
("Antipyretic", "Paracetamol", "500 mg–1 g PO Q6H (safest; use first-line)"),
("Antipyretic", "Ibuprofen", "400 mg TDS with food (avoid in dengue)"),
("Hydration", "Oral fluids / ORS", "2–3 L/day; IV fluids if unable to tolerate oral"),
("Tepid sponging","Physical cooling", "Lukewarm water sponging for high fever"),
("Malaria", "Artemether-Lumefantrine", "Coartem: 4 tablets BD × 3 days (falciparum)"),
("Typhoid", "Azithromycin", "500 mg OD × 7 days (uncomplicated, outpatient)"),
("Typhoid", "Ceftriaxone", "2 g IV OD × 7–10 days (complicated/hospitalized)"),
("Dengue", "Supportive only", "Paracetamol + fluids; AVOID NSAIDs/Aspirin"),
],
red_flags=["Fever > 41 °C", "Altered consciousness", "Stiff neck (meningitis)", "Petechial rash (dengue/meningococcemia)", "SpO2 < 95%"],
key_points=[
"Paracetamol is safest antipyretic — avoid aspirin in dengue",
"Always do malaria test in endemic areas",
"Dengue: platelet < 100,000 — admit; < 20,000 — platelet transfusion",
"Blood culture BEFORE antibiotics in suspected bacterial cause",
]
)
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2: PEDIATRICS OPD
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_divider("SECTION 2 — PEDIATRICS OPD", "👶", C_TEAL))
story.append(sp(6))
# ─── 11. CHILDHOOD FEVER & COMMON COLD ───────────────────────────────────────
story += build_condition(
11, "Childhood Fever & Common Cold", C_TEAL,
definition="Fever in children: rectal T > 38 °C; axillary > 37.5 °C. "
"Common cold: viral URTI most commonly RSV, Rhinovirus. Most self-limiting.",
symptoms=[
"Fever (high-grade in young children)",
"Runny nose (clear initially, then purulent)",
"Sneezing, nasal congestion",
"Mild cough",
"Decreased feeding / poor appetite",
"Irritability, fussiness",
"Mild throat redness",
"Febrile convulsions (6 months–5 years)",
],
investigations=[
"Usually CLINICAL diagnosis",
"CBC (if fever > 5 days or toxic appearance)",
"CRP, blood culture (if sepsis)",
"Urine R/E (exclude UTI in febrile children)",
"CXR (if lower respiratory signs)",
"Rapid Strep test (if tonsillar exudate)",
],
treatment=[
("Antipyretic", "Paracetamol", "15 mg/kg/dose PO/PR Q6H (max 60 mg/kg/day)"),
("Antipyretic", "Ibuprofen", "10 mg/kg/dose PO Q8H (> 6 months; NOT in dengue)"),
("Decongestant", "Normal saline nasal drops", "2–3 drops each nostril TDS (safe, preferred)"),
("Cough", "Honey", "< 1 year: AVOID; > 1 year: 2.5–5 mL PO PRN"),
("Hydration", "Increase oral fluids", "Warm water, diluted juice, ORS"),
("Fever sponging","Tepid sponging", "Lukewarm water — helps reduce fever"),
("Antibiotic", "Amoxicillin", "40–50 mg/kg/day BD–TDS × 5–7 days (only if bacterial, Strep throat)"),
("Antibiotic", "Azithromycin", "10 mg/kg OD × 5 days (Mycoplasma/atypical)"),
],
red_flags=["Infant < 3 months with any fever", "Fever > 5 days", "Febrile convulsion", "Petechial rash", "Bulging fontanelle", "Inability to feed"],
key_points=[
"DO NOT give aspirin to children < 12 years (Reye syndrome)",
"OTC cough/cold medicines NOT recommended < 6 years",
"Antibiotics only if bacterial cause confirmed/strongly suspected",
"Honey effective for cough but strictly AVOID in infants < 1 year (botulism risk)",
"Paracetamol and Ibuprofen can be alternated for refractory fever",
]
)
# ─── 12. ACUTE OTITIS MEDIA ───────────────────────────────────────────────────
story += build_condition(
12, "Acute Otitis Media (AOM)", C_BLUE,
definition="Acute infection of the middle ear. Peaks at 6–24 months of age. "
"Bacteria: S. pneumoniae (35%), H. influenzae (25%), M. catarrhalis (15%).",
symptoms=[
"Ear pain (otalgia) — tugging at ear in infants",
"Fever",
"Irritability, crying, sleep disturbance",
"Decreased hearing",
"Otorrhea (ear discharge — if TM perforated)",
"Diarrhea / vomiting (in young children)",
"Otoscopy: bulging, erythematous TM; reduced mobility",
],
investigations=[
"Otoscopy: gold standard (bulging, red TM)",
"Pneumatic otoscopy / tympanometry",
"CBC (if systemically unwell)",
"Hearing test (after resolution)",
],
treatment=[
("Pain relief", "Paracetamol", "15 mg/kg Q6H"),
("Antibiotic 1st line", "Amoxicillin", "80–90 mg/kg/day in 2 divided doses × 10 days (< 2 years) / 5–7 days (> 2 years)"),
("If allergy", "Azithromycin", "10 mg/kg OD × 5 days"),
("Resistant", "Amoxicillin-Clavulanate", "90 mg/kg/day (high-dose) BD × 10 days"),
("Ear drops", "Ciprofloxacin ear drops", "3 drops BD × 7 days (only if perforated TM)"),
("Nasal", "Normal saline nasal spray", "Decongest nasal passages"),
("Observation", "Watchful waiting", "Age > 2 yrs, mild symptoms, no TM perforation — 48–72 hrs before antibiotics"),
],
red_flags=["Mastoiditis (swelling behind ear)", "Meningitis (stiff neck, altered consciousness)", "Facial nerve palsy", "Labyrinthitis (vertigo)"],
key_points=[
"High-dose amoxicillin (80–90 mg/kg) is first-line — covers resistant pneumococcus",
"Watchful waiting acceptable for mild AOM in children > 2 years",
"Do NOT use decongestants/antihistamines — no proven benefit in AOM",
"Persistent effusion (Otitis media with effusion) — grommet insertion if > 3 months",
]
)
story.append(PageBreak())
# ─── 13. PEDIATRIC ASTHMA ─────────────────────────────────────────────────────
story += build_condition(
13, "Bronchial Asthma (Pediatric)", C_NAVY,
definition="Chronic inflammatory airway disease with reversible airflow obstruction. "
"Triggered by allergens, exercise, infections, cold air. Commonest chronic childhood disease.",
symptoms=[
"Recurrent wheeze (musical expiratory sound)",
"Episodic cough (worse at night / early morning)",
"Dyspnea, chest tightness",
"Prolonged expiration",
"Use of accessory muscles (severe attack)",
"Reduced SpO2 (severe attack)",
"Symptoms triggered by exercise, cold air, dust",
],
investigations=[
"Spirometry: FEV1/FVC < 0.7; reversibility > 12% after bronchodilator",
"Peak Expiratory Flow Rate (PEFR) monitoring",
"Chest X-Ray: hyperinflation, rule out pneumonia",
"Skin prick test / specific IgE (allergen identification)",
"CBC: eosinophilia",
"SpO2 (pulse oximetry during attack)",
"ABG (if severe attack / respiratory failure)",
],
treatment=[
("Acute — mild", "Salbutamol MDI + spacer", "2–4 puffs Q20 min × 3; or nebulize 2.5 mg"),
("Acute — severe", "Salbutamol + Ipratropium neb", "Salb 5 mg + Ipratro 250 µg nebulized Q20 min"),
("Acute", "Systemic corticosteroid", "Prednisolone 1–2 mg/kg/day × 3–5 days"),
("Acute — O2", "Supplemental O2", "Target SpO2 > 94%"),
("Maintenance", "Inhaled corticosteroid (ICS)", "Fluticasone 50–100 µg BD or Budesonide 100–200 µg BD (cornerstone)"),
("Step-up", "ICS + LABA", "Salmeterol + Fluticasone (Seretide) or Formoterol + Budesonide (> 5 yrs)"),
("Leukotriene", "Montelukast", "5 mg OD (4–14 years) — add-on or mild persistent"),
("Rescue", "Salbutamol MDI", "1–2 puffs PRN (SABA — blue inhaler)"),
],
red_flags=["SpO2 < 92%", "Silent chest (no air entry)", "Cyanosis", "Altered consciousness", "PEFR < 33% predicted"],
key_points=[
"ICS is the foundation of preventive treatment — NOT for acute relief",
"SABA (salbutamol) is the reliever; ICS is the preventer",
"Inhaler technique and spacer use must be checked at every visit",
"Stepwise approach: adjust treatment every 3 months based on control",
"Trigger avoidance (allergens, smoke) is fundamental",
]
)
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3: GYNECOLOGY OPD
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_divider("SECTION 3 — GYNECOLOGY OPD", "🩺", C_PURPLE))
story.append(sp(6))
# ─── 14. DYSMENORRHEA ─────────────────────────────────────────────────────────
story += build_condition(
14, "Dysmenorrhea", C_PURPLE,
definition="Painful menstruation. Primary: no underlying pathology (excess prostaglandins). "
"Secondary: due to pelvic pathology (endometriosis, fibroids, adenomyosis, PID).",
symptoms=[
"Cramping lower abdominal pain",
"Pain starting 1–2 days before and during menstruation",
"Lower back pain, inner thigh radiation",
"Nausea, vomiting, diarrhea",
"Headache, dizziness, fatigue",
"Primary: young women, onset with menarche",
"Secondary: later onset, progressively worsening, dyspareunia",
],
investigations=[
"History + clinical examination (primary DX = diagnosis of exclusion)",
"Pelvic USS: fibroids, ovarian cysts, adenomyosis",
"Laparoscopy: endometriosis (gold standard for diagnosis)",
"CBC (rule out anemia from heavy periods)",
"High vaginal swab / cervical swab (if PID suspected)",
"Endometrial biopsy (if irregular bleeding)",
],
treatment=[
("1st line", "NSAIDs — Ibuprofen", "400–600 mg PO TDS × 3–5 days (start 1 day before period)"),
("NSAIDs", "Mefenamic acid", "500 mg TDS × duration of pain"),
("NSAIDs", "Naproxen", "500 mg initial, then 250 mg Q6–8H"),
("Hormonal", "Combined OCP", "1 pill PO OD × 21 days — reduces endometrial prostaglandins"),
("Hormonal", "Levonorgestrel IUS (Mirena)", "Reduces menstrual flow and pain — secondary dysmenorrhea"),
("Hormonal", "Depo-Provera (DMPA)", "150 mg IM Q3 months — amenorrhea in many women"),
("Antispasmodic","Drotaverine / Hyoscine", "40 mg TDS — antispasmodic for cramps"),
("Heat", "Local heat application", "Heat pad to lower abdomen — proven effective"),
],
red_flags=["Severe pain not responding to NSAIDs (consider endometriosis)", "Deep dyspareunia", "Heavy bleeding + anemia", "Fever + pelvic pain (PID)"],
key_points=[
"NSAIDs are 1st line — prostaglandin inhibition is the mechanism",
"Combined OCP is effective for both contraception and dysmenorrhea",
"Secondary dysmenorrhea requires treating the underlying cause",
"Endometriosis: laparoscopy + hormonal suppression + surgery",
]
)
# ─── 15. VAGINITIS / VAGINAL DISCHARGE ────────────────────────────────────────
story += build_condition(
15, "Vaginitis / Abnormal Vaginal Discharge", C_RED,
definition="Inflammation of vagina causing abnormal discharge, odor, or discomfort. "
"Three main types: Bacterial Vaginosis (BV), Vulvovaginal Candidiasis (VVC), Trichomonas vaginalis (TV).",
symptoms=[
"Abnormal vaginal discharge (color, consistency, odor)",
"BV: thin grey-white discharge, fishy odor (worse after sex)",
"Candida: thick, white, cottage-cheese discharge; vulval itch/burning",
"Trichomonas: yellow-green frothy discharge, offensive odor",
"Dysuria, dyspareunia",
"Vulval redness, swelling (Candida)",
"Strawberry cervix (Trichomonas, on colposcopy)",
],
investigations=[
"High Vaginal Swab (HVS) microscopy, culture, sensitivity",
"Wet mount: clue cells (BV), hyphae/spores (Candida), motile trichomonads",
"Whiff test: fishy odor with 10% KOH (BV)",
"pH: > 4.5 (BV, TV); < 4.5 (Candida)",
"NAAT (PCR): for Trichomonas, C. trachomatis, N. gonorrhoeae",
"Pap smear (if cervical pathology suspected)",
],
treatment=[
("BV — 1st line", "Metronidazole", "400–500 mg PO BD × 7 days OR 2 g single dose"),
("BV — alternative", "Clindamycin cream 2%", "1 applicator intravaginally at bedtime × 7 days"),
("Candida — 1st line","Clotrimazole pessary", "200 mg intravaginally × 3 nights OR 500 mg single dose"),
("Candida — oral", "Fluconazole", "150 mg PO single dose (systemic)"),
("Candida — topical","Clotrimazole cream 1%", "Apply to vulva BD × 7 days (external symptoms)"),
("Trichomonas", "Metronidazole", "2 g PO single dose (treat partner simultaneously)"),
("Trichomonas — alt","Tinidazole", "2 g PO single dose"),
("General", "Avoid douching, scented products", "Maintain normal vaginal flora; cotton underwear"),
],
red_flags=["Upper tract infection (PID): fever + pelvic pain + adnexal tenderness", "Recurrent Candida (> 4 episodes/year — check DM, HIV)", "STI in pregnancy"],
key_points=[
"Differentiate by pH, odor, and microscopy — each type has distinct features",
"Treat sexual partner for Trichomonas (sexually transmitted)",
"Recurrent Candida: maintain fluconazole 150 mg weekly × 6 months",
"Avoid alcohol during and 24 hrs after metronidazole",
"Metronidazole in pregnancy: use in 2nd/3rd trimester (1st trimester — use clindamycin)",
]
)
story.append(PageBreak())
# ══════════════════════════════════════════════════════════════════════════════
# QUICK-REFERENCE SUMMARY TABLE
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_divider("QUICK-REFERENCE SUMMARY TABLE", "📊", C_NAVY))
story.append(sp(6))
summary_data = [
["#", "Condition", "Key Drug(s)", "First-Line Dose", "Duration"],
["1", "URTI", "Paracetamol ± Amoxicillin", "1g Q6H / 500mg TDS", "Viral: 7–10d; Strep: 7d"],
["2", "Gastroenteritis", "ORS + Ondansetron", "200 mL/loose stool / 4–8 mg Q8H", "Until resolved"],
["3", "Hypertension", "Amlodipine / ACE-I", "5–10 mg OD / 5–20 mg OD", "Lifelong"],
["4", "Type 2 Diabetes", "Metformin", "500 mg BD → 2000 mg/day", "Lifelong"],
["5", "CAP", "Amoxicillin / Ceftriaxone", "500 mg TDS / 1–2 g IV OD", "5–10 days"],
["6", "UTI", "Nitrofurantoin / Ciprofloxacin","100 mg BD / 500 mg BD", "5–7d / 7d"],
["7", "PUD / GERD", "PPI + Triple Therapy", "Omeprazole 40 mg OD", "4–8 wk / 14d (HP)"],
["8", "IDA", "Ferrous Sulfate + Vit C", "200 mg TDS", "3–6 months"],
["9", "Allergic Rhinitis", "Fluticasone nasal spray", "2 sprays/nostril OD", "Ongoing"],
["10", "Acute Fever", "Paracetamol", "1g Q6H", "PRN; treat cause"],
["11", "Childhood Fever/Cold", "Paracetamol (peds)", "15 mg/kg Q6H", "Symptomatic"],
["12", "Acute Otitis Media", "Amoxicillin (high dose)", "80–90 mg/kg/day BD", "5–10 days"],
["13", "Pediatric Asthma", "Salbutamol + ICS", "2–4 puffs PRN / Fluticasone 50–100 µg BD", "Ongoing"],
["14", "Dysmenorrhea", "Ibuprofen / Mefenamic acid", "400–600 mg TDS / 500 mg TDS", "3–5 days"],
["15", "Vaginitis", "Metronidazole / Fluconazole", "400 mg BD / 150 mg stat", "7 days / Single"],
]
sum_h_st = ParagraphStyle("sh", fontSize=8, fontName="Helvetica-Bold", textColor=C_WHITE, leading=11)
sum_c_st = ParagraphStyle("sc", fontSize=8, fontName="Helvetica", textColor=C_BLACK, leading=12)
sum_rows = [[Paragraph(c, sum_h_st) for c in summary_data[0]]]
row_colors = [C_LGREY, C_WHITE]
for idx, r in enumerate(summary_data[1:]):
sum_rows.append([Paragraph(c, sum_c_st) for c in r])
sum_t = Table(sum_rows, colWidths=[0.6*cm, 4.2*cm, 4*cm, 3.8*cm, 3.7*cm])
sum_t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), C_NAVY),
("ROWBACKGROUNDS", (0,1), (-1,-1), [C_WHITE, C_LGREY]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#BFC9CA")),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "TOP"),
("FONTSIZE", (0,0), (-1,-1), 8),
]))
story += [sum_t, sp(10)]
# ══════════════════════════════════════════════════════════════════════════════
# ABBREVIATIONS
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_divider("COMMON ABBREVIATIONS", "📖", C_GREY))
story.append(sp(5))
abbr = [
["ABG", "Arterial Blood Gas"], ["AOM", "Acute Otitis Media"],
["BV", "Bacterial Vaginosis"], ["CAP", "Community-Acquired Pneumonia"],
["CCB", "Calcium Channel Blocker"], ["CBC", "Complete Blood Count"],
["CRP", "C-Reactive Protein"], ["DM", "Diabetes Mellitus"],
["DU", "Duodenal Ulcer"], ["FBS", "Fasting Blood Sugar"],
["GERD","Gastroesophageal Reflux Disease"], ["GU", "Gastric Ulcer"],
["HbA1c","Glycated Hemoglobin"], ["HTN", "Hypertension"],
["ICS", "Inhaled Corticosteroid"], ["IDA", "Iron-Deficiency Anemia"],
["IgE", "Immunoglobulin E"], ["LABA","Long-Acting Beta-Agonist"],
["LVH", "Left Ventricular Hypertrophy"], ["MDI", "Metered Dose Inhaler"],
["NAAT","Nucleic Acid Amplification Test"], ["NSAIDs","Non-Steroidal Anti-Inflammatory Drugs"],
["OCP", "Oral Contraceptive Pill"], ["ORS", "Oral Rehydration Salts"],
["PEFR","Peak Expiratory Flow Rate"], ["PID", "Pelvic Inflammatory Disease"],
["PPI", "Proton Pump Inhibitor"], ["PUD", "Peptic Ulcer Disease"],
["RFT", "Renal Function Tests"], ["SABA","Short-Acting Beta-Agonist"],
["SGLT-2","Sodium-Glucose Cotransporter-2"], ["SU", "Sulfonylurea"],
["TDS", "Three Times Daily"], ["TM", "Tympanic Membrane"],
["TIBC","Total Iron Binding Capacity"], ["TV", "Trichomonas vaginalis"],
["USS", "Ultrasound Scan"], ["UTI", "Urinary Tract Infection"],
["VVC", "Vulvovaginal Candidiasis"], ["WBC", "White Blood Cell"],
]
abbr_key_st = ParagraphStyle("ak", fontSize=8, fontName="Helvetica-Bold", textColor=C_NAVY, leading=12)
abbr_val_st = ParagraphStyle("av", fontSize=8, fontName="Helvetica", textColor=C_BLACK, leading=12)
# display in 4 columns
num_rows = (len(abbr) + 3) // 4
abbr_table_data = []
for i in range(num_rows):
row = []
for j in range(4):
idx = i + j * num_rows
if idx < len(abbr):
row.extend([
Paragraph(abbr[idx][0], abbr_key_st),
Paragraph(abbr[idx][1], abbr_val_st),
])
else:
row.extend([Paragraph("", abbr_val_st), Paragraph("", abbr_val_st)])
abbr_table_data.append(row)
col_w = W / 8
abbr_t = Table(abbr_table_data, colWidths=[col_w * 0.8, col_w * 1.2] * 4)
abbr_t.setStyle(TableStyle([
("ROWBACKGROUNDS", (0,0), (-1,-1), [C_WHITE, C_LGREY]),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("GRID", (0,0), (-1,-1), 0.2, HexColor("#D5D8DC")),
]))
story += [abbr_t, sp(12)]
# ══════════════════════════════════════════════════════════════════════════════
# DISCLAIMER
# ══════════════════════════════════════════════════════════════════════════════
disclaimer = Table([[Paragraph(
"<b>Disclaimer:</b> These notes are compiled for medical student education and quick reference only. "
"Drug doses, treatment guidelines, and clinical decisions must always be verified against current local "
"formularies, institutional protocols, and senior clinical guidance. This document does not replace "
"clinical judgment. Always consider patient-specific factors including allergies, comorbidities, "
"renal/hepatic function, and pregnancy status before prescribing.",
S("disc", fontSize=8, fontName="Helvetica-Oblique", textColor=C_GREY,
leading=12, alignment=TA_JUSTIFY))]], colWidths=[W])
disclaimer.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_LGREY),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("BOX", (0,0), (-1,-1), 0.5, C_GREY),
]))
story.append(disclaimer)
# ══════════════════════════════════════════════════════════════════════════════
# PAGE NUMBERS
# ══════════════════════════════════════════════════════════════════════════════
def add_page_number(canvas, doc):
canvas.saveState()
canvas.setFont("Helvetica", 8)
canvas.setFillColor(C_GREY)
page_num = canvas.getPageNumber()
text = f"Common OPD Conditions — Medical Student Notes | Page {page_num}"
canvas.drawCentredString(PAGE_W / 2, 1.2*cm, text)
canvas.restoreState()
doc.build(story, onFirstPage=add_page_number, onLaterPages=add_page_number)
print(f"PDF generated: {OUTPUT}")
cd /home/daytona/workspace/opd-notes && python generate_opd_pdf.py 2>&1
~/opd-notes/generate_opd_pdf.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/opd-notes/generate_opd_pdf.py'
cd /home/daytona/workspace/opd-notes && python generate_opd_pdf.py 2>&1
ls -lh /home/daytona/workspace/opd-notes/Common_OPD_Conditions_Notes.pdf
Common OPD Conditions Notes
PDF Document · PDF
| # | Condition | Key Drug |
|---|---|---|
| 1 | URTI / Common Cold | Paracetamol ± Amoxicillin |
| 2 | Acute Gastroenteritis / Diarrhea | ORS + Ondansetron |
| 3 | Essential Hypertension | Amlodipine / ACE-I / ARB |
| 4 | Type 2 Diabetes Mellitus | Metformin + SGLT-2i |
| 5 | Community-Acquired Pneumonia | Amoxicillin / Ceftriaxone |
| 6 | Urinary Tract Infection | Nitrofurantoin / Ciprofloxacin |
| 7 | Peptic Ulcer Disease / GERD | PPI + H. pylori triple therapy |
| 8 | Iron-Deficiency Anemia | Ferrous Sulfate + Vit C |
| 9 | Allergic Rhinitis | Intranasal Fluticasone |
| 10 | Acute Febrile Illness | Paracetamol; disease-specific Rx |
| # | Condition | Key Points |
|---|---|---|
| 11 | Childhood Fever & Common Cold | Paracetamol 15 mg/kg; NO aspirin |
| 12 | Acute Otitis Media | High-dose Amoxicillin 80–90 mg/kg |
| 13 | Bronchial Asthma | Salbutamol (rescue) + ICS (preventer) |
| # | Condition | Key Points |
|---|---|---|
| 14 | Dysmenorrhea | NSAIDs first; OCP for recurrent |
| 15 | Vaginitis / Vaginal Discharge | Metronidazole (BV/TV); Fluconazole (Candida) |