Hwy send me all the important thing in reasoning manner for rs cases... as a flowcharts as all in one

Asking for Preferences
Asking for Preferences
Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Running Command

ls /home/daytona/workspace/ 2>/dev/null && echo "---" && which python3 && python…

Running Command

python3 -c "import PIL; print('PIL OK')" 2>/dev/null && python3 -c "from PIL imp…

Running Command

mkdir -p /home/daytona/workspace/rs-flowchart && ls /home/daytona/workspace/rs-f…

Writing File

~/rs-flowchart/gen_flowchart.py

from PIL import Image, ImageDraw, ImageFont
import textwrap, os

# ── Canvas ──────────────────────────────────────────────────────────────────
W, H = 2400, 5600
bg       = (10, 12, 30)          # deep navy
canvas   = Image.new("RGB", (W, H), bg)
draw     = ImageDraw.Draw(canvas)

# ── Fonts ────────────────────────────────────────────────────────────────────
def font(size, bold=False):
    candidates = [
        "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" if bold else
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
        "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf" if bold else
        "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
    ]
    for c in candidates:
        if os.path.exists(c):
            return ImageFont.truetype(c, size)
    return ImageFont.load_default()

F_TITLE   = font(62, True)
F_TOPIC   = font(46, True)
F_SECTION = font(34, True)
F_BODY    = font(27)
F_LABEL   = font(25)
F_SMALL   = font(22)

# ── Palette ──────────────────────────────────────────────────────────────────
COL = {
    "pleural":  {"hdr": (0, 168, 232),  "box": (0, 60, 90),    "arrow": (0, 168, 232)},
    "bronch":   {"hdr": (255, 140, 0),  "box": (80, 40, 0),    "arrow": (255, 165, 0)},
    "fibrosis": {"hdr": (120, 200, 80), "box": (30, 60, 10),   "arrow": (120, 200, 80)},
    "pneumo":   {"hdr": (220, 80, 160), "box": (70, 15, 50),   "arrow": (220, 80, 160)},
}
WHITE   = (240, 245, 255)
YELLOW  = (255, 220, 60)
LGREY   = (170, 180, 200)
DIMGREY = (80, 90, 110)

# ── Helpers ───────────────────────────────────────────────────────────────────
def draw_rounded_rect(draw, xy, fill, radius=18, outline=None, outline_w=2):
    x1,y1,x2,y2 = xy
    draw.rounded_rectangle([x1,y1,x2,y2], radius=radius, fill=fill,
                            outline=outline, width=outline_w)

def draw_diamond(draw, cx, cy, w, h, fill, outline, outline_w=2):
    pts = [(cx, cy-h//2),(cx+w//2, cy),(cx, cy+h//2),(cx-w//2, cy)]
    draw.polygon(pts, fill=fill, outline=outline)

def draw_arrow(draw, x1,y1,x2,y2, color, width=3):
    draw.line([(x1,y1),(x2,y2)], fill=color, width=width)
    # arrowhead (pointing down)
    ah = 14
    aw = 8
    draw.polygon([(x2,y2),(x2-aw,y2-ah),(x2+aw,y2-ah)], fill=color)

def wrapped_text(draw, text, x, y, w, fnt, color, align="left", line_spacing=6):
    max_chars = max(int(w / (fnt.size * 0.6)), 10)
    lines = []
    for raw in text.split("\n"):
        if raw.strip() == "":
            lines.append("")
        else:
            lines.extend(textwrap.wrap(raw, width=max_chars) or [""])
    lh = fnt.size + line_spacing
    for i, line in enumerate(lines):
        if align == "center":
            bbox = draw.textbbox((0,0), line, font=fnt)
            lw = bbox[2] - bbox[0]
            draw.text((x + (w - lw)//2, y + i*lh), line, font=fnt, fill=color)
        else:
            draw.text((x, y + i*lh), line, font=fnt, fill=color)
    return y + len(lines) * lh

def section_box(draw, title, lines, x, y, box_w, pal, icon=""):
    # Header bar
    draw_rounded_rect(draw, [x, y, x+box_w, y+46], fill=pal["hdr"], radius=12)
    draw.text((x+14, y+6), f"{icon}  {title}" if icon else title, font=F_SECTION, fill=(10,12,30))
    y += 54
    # Body
    body_h = len(lines)*32 + 16
    draw_rounded_rect(draw, [x, y, x+box_w, y+body_h], fill=pal["box"], radius=10,
                      outline=pal["hdr"], outline_w=2)
    ty = y + 10
    for line in lines:
        bullet = "  • " if not line.startswith("   ") else ""
        draw.text((x+12, ty), bullet + line, font=F_BODY, fill=WHITE)
        ty += 32
    return y + body_h + 8

def flow_diamond(draw, text, cx, cy, fill, outline):
    draw_diamond(draw, cx, cy, 320, 80, fill, outline, 2)
    bbox = draw.textbbox((0,0), text, font=F_LABEL)
    tw = bbox[2]-bbox[0]
    draw.text((cx - tw//2, cy - F_LABEL.size//2), text, font=F_LABEL, fill=WHITE)
    return cy

# ═══════════════════════════════════════════════════════════════════════════════
#  MASTER TITLE
# ═══════════════════════════════════════════════════════════════════════════════
y = 24
draw_rounded_rect(draw, [30, y, W-30, y+90], fill=(25, 30, 70), radius=20,
                  outline=(80,120,200), outline_w=3)
draw.text((W//2, y+18), "RESPIRATORY SYSTEM — Clinical Reasoning Flowcharts", 
          font=F_TITLE, fill=YELLOW, anchor="mt")
draw.text((W//2, y+72), "Pleural Effusion  |  Bronchiectasis  |  Pulmonary Fibrosis  |  Pneumonia",
          font=F_LABEL, fill=LGREY, anchor="mt")
y += 110

# ── divider
draw.line([(30, y), (W-30, y)], fill=DIMGREY, width=2)
y += 14

# ═══════════════════════════════════════════════════════════════════════════════
#  SECTION 1 — PLEURAL EFFUSION
# ═══════════════════════════════════════════════════════════════════════════════
PAL = COL["pleural"]
x0, col_w = 40, W-80

# Topic header
draw_rounded_rect(draw, [x0, y, x0+col_w, y+58], fill=PAL["hdr"], radius=14)
draw.text((x0+20, y+8), "① PLEURAL EFFUSION", font=F_TOPIC, fill=(10,12,30))
y += 68

# ---- Row 1: Definition + Epidemiology
cx = x0
col2 = (col_w - 20) // 2
y = section_box(draw, "DEFINITION", [
    "Accumulation of excess fluid in the pleural space",
    ">1 million cases/year in the US",
    "Most common causes (US): CHF, malignancy,",
    "   bacterial pneumonia, pulmonary embolism",
], cx, y, col2, PAL)

cx2 = x0 + col2 + 20
y_save = y - section_box(draw, "DEFINITION", [], cx2, y, col2, PAL) + y   # align
section_box(draw, "CLASSIFICATION", [
    "TRANSUDATES — systemic causes (↓oncotic / ↑hydrostatic P)",
    "   CHF, cirrhosis, nephrotic syndrome, hypoalbuminaemia",
    "EXUDATES — pleural/lung pathology",
    "   Infections, malignancy, PE, RA, SLE, TB, drugs",
], cx2, y_save - 8, col2, PAL)
y = max(y, y_save) + 8

# ---- Light's Criteria diamond
y += 10
draw.text((W//2, y), "▼  Apply LIGHT'S CRITERIA to pleural fluid", font=F_SECTION, fill=YELLOW, anchor="mt")
y += 38

# criteria box
draw_rounded_rect(draw, [x0+80, y, x0+col_w-80, y+160], fill=(0,40,70), radius=14,
                  outline=PAL["hdr"], outline_w=3)
draw.text((W//2, y+10), "LIGHT'S CRITERIA — ANY ONE = Exudate:", font=F_SECTION, fill=YELLOW, anchor="mt")
criteria = [
    "1. Pleural fluid protein / serum protein  >  0.5",
    "2. Pleural fluid LDH / serum LDH  >  0.6",
    "3. Pleural fluid LDH  >  2/3 upper limit of normal serum LDH",
]
for i, c in enumerate(criteria):
    draw.text((x0+110, y+56+i*32), c, font=F_BODY, fill=WHITE)
y += 170

# Arrow + two outcomes
CX = W//2
draw_arrow(draw, CX, y, CX-400, y+60, PAL["arrow"], 3)
draw_arrow(draw, CX, y, CX+400, y+60, PAL["arrow"], 3)
draw.text((CX-550, y+20), "TRANSUDATE", font=F_SECTION, fill=(100,200,255))
draw.text((CX+260, y+20), "EXUDATE", font=F_SECTION, fill=(255,120,80))
y += 70

# Transudate box
draw_rounded_rect(draw, [x0, y, x0+col2-10, y+100], fill=(0,40,70), radius=10,
                  outline=(100,200,255), outline_w=2)
draw.text((x0+10, y+10), "Treat underlying cause:", font=F_SECTION, fill=(100,200,255))
for i, t in enumerate(["Diuretics for CHF", "Treat cirrhosis / hypoalbuminaemia", "Drain if symptomatic"]):
    draw.text((x0+20, y+46+i*18), f"• {t}", font=F_SMALL, fill=WHITE)
# Exudate box
draw_rounded_rect(draw, [x0+col2+10, y, x0+col_w, y+100], fill=(70,20,10), radius=10,
                  outline=(255,120,80), outline_w=2)
draw.text((x0+col2+20, y+10), "Further workup:", font=F_SECTION, fill=(255,140,80))
for i, t in enumerate(["Culture, cytology, ADA (TB)", "CT chest / bronchoscopy", "Drain + treat (empyema → antibiotics + tube)"]):
    draw.text((x0+col2+30, y+46+i*18), f"• {t}", font=F_SMALL, fill=WHITE)
y += 110

# Investigations row
y = section_box(draw, "INVESTIGATIONS", [
    "Imaging: CXR (blunting costophrenic angle ≥200 mL), US (5 mL detectable), CT",
    "Thoracocentesis: protein, LDH, glucose, pH, cell count, culture, cytology, ADA",
    "Empyema markers: pH <7.2, glucose <60, LDH >1000 → needs drainage",
    "Malignant: cytology +ve, pleural biopsy; TB: ADA >40, lymphocytic",
], x0, y, col_w, PAL)

y = section_box(draw, "MANAGEMENT SUMMARY", [
    "Transudates → treat cause | Small exudates → observe + treat",
    "Parapneumonic → antibiotics ± drainage if complicated",
    "Empyema → chest tube + antibiotics (VATS if loculated)",
    "Malignant → pleurodesis (talc), indwelling catheter",
    "TB → anti-TB therapy (HRZE × 2 months, then HR × 4 months)",
], x0, y, col_w, PAL)

y += 10
draw.line([(30, y), (W-30, y)], fill=DIMGREY, width=2)
y += 16

# ═══════════════════════════════════════════════════════════════════════════════
#  SECTION 2 — BRONCHIECTASIS
# ═══════════════════════════════════════════════════════════════════════════════
PAL = COL["bronch"]
draw_rounded_rect(draw, [x0, y, x0+col_w, y+58], fill=PAL["hdr"], radius=14)
draw.text((x0+20, y+8), "② BRONCHIECTASIS", font=F_TOPIC, fill=(10,12,30))
y += 68

y = section_box(draw, "DEFINITION & PATHOGENESIS — Vicious Cycle Hypothesis", [
    "Permanent, abnormal dilation of bronchi (≥1.5× diameter of adjacent pulmonary artery)",
    "Infection → inflammation → airway wall damage → impaired clearance → more infection",
    "Key mediators: neutrophil elastase, reactive oxygen species, inflammatory cytokines",
    "Organisms: Pseudomonas aeruginosa (most virulent), H. influenzae, S. aureus, NTM",
], x0, y, col_w, PAL)

# Two-column: Causes + Features
cx = x0
y_a = section_box(draw, "CAUSES (Mnemonic: ABCDEF)", [
    "A — Allergic bronchopulmonary aspergillosis (ABPA)",
    "B — Bronchial obstruction (foreign body, tumour)",
    "C — Cystic fibrosis / Ciliary dyskinesia (PCD)",
    "D — Deficiency (immunoglobulin, α1-antitrypsin)",
    "E — Erosion by TB / NTM / post-pertussis",
    "F — Fibrosis (post-infective), GORD/aspiration",
    "Idiopathic: 25–50% of cases",
], cx, y, col2, PAL)
y_b = section_box(draw, "CLINICAL FEATURES", [
    "Chronic productive cough (large volumes, 3-layered sputum)",
    "Recurrent chest infections / haemoptysis",
    "Dyspnoea, wheeze, pleuritic chest pain",
    "Signs: coarse crackles, wheeze, finger clubbing",
    "Worsening with Pseudomonas colonisation",
], cx2, y, col2, PAL)
y = max(y_a, y_b)

y = section_box(draw, "INVESTIGATIONS", [
    "Gold standard: HRCT chest — airway dilation, tram-track sign, signet-ring sign",
    "Sputum culture (especially for Pseudomonas, NTM, Aspergillus)",
    "PFTs: obstructive pattern (FEV1/FVC ↓); may have mixed pattern",
    "Screen for cause: serum Ig, CFTR mutation, ciliary biopsy, ABPA (IgE, Aspergillus precipitins)",
    "Bronchoscopy if focal bronchiectasis (exclude obstruction)",
], x0, y, col_w, PAL)

# Exacerbation decision diamond
y += 6
draw.text((W//2, y), "▼  Is there an ACUTE EXACERBATION?", font=F_SECTION, fill=YELLOW, anchor="mt")
y += 38
draw_arrow(draw, CX, y, CX-350, y+55, PAL["arrow"], 3)
draw_arrow(draw, CX, y, CX+350, y+55, PAL["arrow"], 3)
draw.text((CX-600, y+18), "YES — Acute management", font=F_SECTION, fill=(255,180,0))
draw.text((CX+220, y+18), "NO — Stable management", font=F_SECTION, fill=(180,220,100))
y += 65

draw_rounded_rect(draw, [x0, y, x0+col2-10, y+120], fill=PAL["box"], radius=10,
                  outline=PAL["hdr"], outline_w=2)
draw.text((x0+10, y+8), "Acute:", font=F_SECTION, fill=PAL["hdr"])
for i, t in enumerate(["Oral/IV antibiotics (guided by culture)",
                        "Pseudomonas: ciprofloxacin PO or ceftazidime IV",
                        "Airway clearance: physio, hypertonic saline",
                        "Bronchodilators if wheeze"]):
    draw.text((x0+18, y+44+i*18), f"• {t}", font=F_SMALL, fill=WHITE)

draw_rounded_rect(draw, [x0+col2+10, y, x0+col_w, y+120], fill=PAL["box"], radius=10,
                  outline=PAL["hdr"], outline_w=2)
draw.text((x0+col2+20, y+8), "Stable / Long-term:", font=F_SECTION, fill=PAL["hdr"])
for i, t in enumerate(["Airway clearance techniques (daily physiotherapy)",
                        "Macrolide prophylaxis (azithromycin 3×/week)",
                        "Inhaled antibiotics (tobramycin, colistin) if Pseudomonas",
                        "Vaccinations: influenza, pneumococcal; treat cause"]):
    draw.text((x0+col2+30, y+44+i*18), f"• {t}", font=F_SMALL, fill=WHITE)
y += 130

y += 10
draw.line([(30, y), (W-30, y)], fill=DIMGREY, width=2)
y += 16

# ═══════════════════════════════════════════════════════════════════════════════
#  SECTION 3 — PULMONARY FIBROSIS (IPF)
# ═══════════════════════════════════════════════════════════════════════════════
PAL = COL["fibrosis"]
draw_rounded_rect(draw, [x0, y, x0+col_w, y+58], fill=PAL["hdr"], radius=14)
draw.text((x0+20, y+8), "③ PULMONARY FIBROSIS (IPF)", font=F_TOPIC, fill=(10,12,30))
y += 68

y = section_box(draw, "DEFINITION & EPIDEMIOLOGY", [
    "IPF = chronic, progressive fibrosing interstitial pneumonia, unknown cause (UIP pattern)",
    "50–60% of all idiopathic interstitial pneumonias",
    "Mean age of onset: 62 years; more common in men; associated with cigarette smoking",
    "Median survival: 3–5 years from diagnosis; progressive with acute exacerbations",
], x0, y, col_w, PAL)

# Two-column
y_a = section_box(draw, "RISK FACTORS", [
    "Cigarette smoking (OR ~5.4)",
    "Diabetes (OR ~4.1)",
    "Familial (MUC5B gene, telomere genes)",
    "Occupational dust (metal, wood, stone)",
    "Micro-aspiration / GORD",
], cx, y, col2, PAL)
y_b = section_box(draw, "CLINICAL FEATURES", [
    "Gradual onset exertional dyspnoea",
    "Chronic non-productive cough",
    "Bilateral fine end-inspiratory crackles (Velcro)",
    "Clubbing (25–50%)",
    "No fever/wheeze (unless AE-IPF)",
], cx2, y, col2, PAL)
y = max(y_a, y_b)

y = section_box(draw, "INVESTIGATIONS — HRCT IS KEY", [
    "CXR: bibasilar reticular opacities, low lung volumes",
    "HRCT (gold standard): UIP pattern — subpleural honeycombing + traction bronchiectasis",
    "   Lower lobe predominant; absence of GGO, micronodules, pleural effusion",
    "PFTs: RESTRICTIVE (↓FVC, ↓TLC, ↓DLCO); FEV1/FVC normal or raised",
    "BAL: excess neutrophils ± eosinophils (non-specific)",
    "Surgical lung biopsy: UIP histology if HRCT non-diagnostic",
], x0, y, col_w, PAL)

# IPF management flow
y += 6
draw.text((W//2, y), "▼  MANAGEMENT DECISION", font=F_SECTION, fill=YELLOW, anchor="mt")
y += 36

draw_rounded_rect(draw, [x0, y, x0+col_w, y+170], fill=PAL["box"], radius=12,
                  outline=PAL["hdr"], outline_w=2)
headers = ["Antifibrotic Drugs", "Supportive Care", "Acute Exacerbation (AE-IPF)", "End-Stage"]
contents = [
    ["Nintedanib 150 mg BD", "Pirfenidone 2403 mg/day", "Slow FVC decline by ~50%", "Do NOT use steroids+azathioprine+NAC (harm)"],
    ["O2 therapy for hypoxaemia", "Pulmonary rehab", "Treat GORD (PPIs)", "Vaccinations"],
    ["High-dose IV corticosteroids", "Rule out infection", "Mechanical ventilation rarely beneficial", "Consider palliative care"],
    ["Lung transplant (single/bilateral)", "Palliative/hospice care", "DNR discussion", "Opiates for dyspnoea"],
]
col4 = (col_w - 30) // 4
for j, (hdr, items) in enumerate(zip(headers, contents)):
    bx = x0 + j*(col4+10)
    draw.text((bx+10, y+8), hdr, font=F_SMALL, fill=PAL["hdr"])
    for i, item in enumerate(items):
        draw.text((bx+6, y+34+i*30), f"• {item}", font=F_SMALL, fill=WHITE)
y += 180

y += 10
draw.line([(30, y), (W-30, y)], fill=DIMGREY, width=2)
y += 16

# ═══════════════════════════════════════════════════════════════════════════════
#  SECTION 4 — PNEUMONIA
# ═══════════════════════════════════════════════════════════════════════════════
PAL = COL["pneumo"]
draw_rounded_rect(draw, [x0, y, x0+col_w, y+58], fill=PAL["hdr"], radius=14)
draw.text((x0+20, y+8), "④ PNEUMONIA", font=F_TOPIC, fill=(10,12,30))
y += 68

y = section_box(draw, "CLASSIFICATION", [
    "CAP — Community-Acquired Pneumonia (onset outside hospital or <48h of admission)",
    "HAP — Hospital-Acquired Pneumonia (≥48h after admission, not incubating on admission)",
    "VAP — Ventilator-Associated Pneumonia (≥48–72h after ET intubation)",
    "HCAP — Healthcare-Associated (nursing home, haemodialysis, recent hospitalisation)",
    "Aspiration pneumonia — gastric contents/oropharyngeal flora",
], x0, y, col_w, PAL)

# Organisms
y_a = section_box(draw, "KEY ORGANISMS — CAP", [
    "Typical: S. pneumoniae (most common), H. influenzae, K. pneumoniae",
    "Atypical: Mycoplasma, Legionella, Chlamydophila, viruses",
    "Immunocompromised: PCP (Pneumocystis), fungi, CMV",
], cx, y, col2, PAL)
y_b = section_box(draw, "KEY ORGANISMS — HAP/VAP", [
    "MRSA, Pseudomonas aeruginosa, Acinetobacter",
    "Klebsiella pneumoniae (lobar consolidation)",
    "Enterobacterales, polymicrobial",
], cx2, y, col2, PAL)
y = max(y_a, y_b)

# CURB-65
y += 8
draw.text((W//2, y), "▼  SEVERITY ASSESSMENT — CURB-65", font=F_SECTION, fill=YELLOW, anchor="mt")
y += 38
draw_rounded_rect(draw, [x0+60, y, x0+col_w-60, y+160], fill=(60,10,40), radius=14,
                  outline=PAL["hdr"], outline_w=3)
draw.text((W//2, y+10), "CURB-65 Score (1 point each):", font=F_SECTION, fill=YELLOW, anchor="mt")
curb = [
    ("C", "Confusion (new onset)"),
    ("U", "Urea  >  7 mmol/L"),
    ("R", "Respiratory rate  ≥  30/min"),
    ("B", "Blood pressure — systolic <90 or diastolic ≤60"),
    ("65","Age ≥ 65 years"),
]
for i, (letter, desc) in enumerate(curb):
    draw.text((x0+90, y+50+i*20), letter, font=F_SECTION, fill=PAL["hdr"])
    draw.text((x0+130, y+50+i*20), f"—  {desc}", font=F_BODY, fill=WHITE)

scores = [
    ("Score 0–1", "Low risk → Outpatient oral antibiotics (amoxicillin 500mg TDS × 5d)", (80,200,80)),
    ("Score 2",   "Moderate → Hospital admission, oral/IV antibiotics",                   (255,200,0)),
    ("Score 3–5", "Severe → ICU consideration, IV antibiotics, supportive care",           (255,80,80)),
]
for i, (sc, rec, col) in enumerate(scores):
    bx = x0 + 60 + i*(( col_w-120)//3)
    draw_rounded_rect(draw, [bx, y+155, bx+(col_w-120)//3-8, y+155+50], fill=(30,0,20), radius=8,
                      outline=col, outline_w=2)
    draw.text((bx+6, y+157), sc, font=F_SMALL, fill=col)
    wrapped_text(draw, rec, bx+6, y+172, (col_w-120)//3-12, F_SMALL, WHITE)
y += 215

y = section_box(draw, "INVESTIGATIONS", [
    "Bloods: FBC (↑WCC), CRP, U&E (urea for CURB), LFTs, blood cultures (2 sets before Abx)",
    "Sputum MC&S (before antibiotics if possible); urinary antigen (Strep, Legionella)",
    "CXR: lobar/segmental consolidation (typical); diffuse patchy (atypical/viral)",
    "ABG if SpO2 <94% or severe; HIV test if PCP suspected",
    "Procalcitonin — helps guide antibiotic duration",
], x0, y, col_w, PAL)

y = section_box(draw, "ANTIBIOTIC TREATMENT — CAP", [
    "Mild CAP (outpatient): Amoxicillin 500mg TDS × 5 days  OR  Doxycycline 200mg day 1 → 100mg OD",
    "Moderate CAP (inpatient): Amoxicillin IV + Clarithromycin IV (dual for atypicals)",
    "Severe CAP / ICU: Co-amoxiclav IV + Clarithromycin IV  OR  Piperacillin-tazobactam + azithromycin",
    "Penicillin allergy: Doxycycline (mild); Levofloxacin/Moxifloxacin (moderate-severe)",
    "Aspiration: Add metronidazole for anaerobic cover",
    "Duration: minimum 5 days once afebrile; 7–10d if severe or bacteraemic",
], x0, y, col_w, PAL)

y = section_box(draw, "COMPLICATIONS & RESOLUTION", [
    "Parapneumonic effusion → if complicated (pH<7.2) → drain",
    "Empyema → chest tube + antibiotics; lung abscess → prolonged antibiotics ± drainage",
    "Sepsis / septic shock → fluids, vasopressors, early Abx within 1h",
    "ARDS → lung protective ventilation (low tidal volume 6 mL/kg IBW)",
    "CXR resolution: 2–4 weeks (young); 6–12 weeks (elderly / severe) — repeat at 6 weeks",
], x0, y, col_w, PAL)

y += 10
draw.line([(30, y), (W-30, y)], fill=DIMGREY, width=2)
y += 16

# ═══════════════════════════════════════════════════════════════════════════════
#  FOOTER
# ═══════════════════════════════════════════════════════════════════════════════
draw_rounded_rect(draw, [30, y, W-30, y+60], fill=(20,25,55), radius=14,
                  outline=(80,100,180), outline_w=2)
draw.text((W//2, y+10), 
          "Sources: Harrison's IM 22E (2025) | Goldman-Cecil Medicine | Murray & Nadel's Respiratory | Washington Manual",
          font=F_SMALL, fill=LGREY, anchor="mt")
draw.text((W//2, y+36), "For educational use — MBBS/MD clinical reasoning aid",
          font=F_SMALL, fill=DIMGREY, anchor="mt")
y += 70

# ── Crop to actual content ────────────────────────────────────────────────────
canvas = canvas.crop((0, 0, W, min(y + 20, H)))
canvas.save("/home/daytona/workspace/rs-flowchart/RS_Flowchart.png", dpi=(150,150))
print(f"Saved! Size: {canvas.size}, y_end={y}")
Running Command

cd /home/daytona/workspace/rs-flowchart && python3 gen_flowchart.py 2>&1

Running Command

ls -lh /home/daytona/workspace/rs-flowchart/RS_Flowchart.png

Loading Image
Here's your all-in-one RS Clinical Reasoning Flowchart!

Download Your Flowchart

RS Flowchart

Image · PNG

Size: 2400 × 5010 px, 1.1 MB - clear enough to zoom in on your phone or print on A3.

What's inside the flowchart (summary of reasoning logic):


① PLEURAL EFFUSION (Blue)

Reasoning flow:
  1. Detect - CXR blunting ≥200 mL, US confirms even 5 mL
  2. Tap it - thoracocentesis: protein, LDH, glucose, pH, culture, cytology, ADA
  3. Apply Light's Criteria - ANY ONE = Exudate:
    • PF protein/serum protein >0.5
    • PF LDH/serum LDH >0.6
    • PF LDH >2/3 upper normal serum LDH
  4. Transudate - treat cause (diuretics for CHF, albumin for cirrhosis)
  5. Exudate - investigate further: TB (ADA >40), malignancy (cytology), empyema (pH <7.2 → drain)

② BRONCHIECTASIS (Orange)

Reasoning flow (Vicious Cycle): Infection → inflammation → airway wall destruction → impaired mucociliary clearance → re-infection
Causes mnemonic ABCDEF: ABPA, Bronchial obstruction, CF/Ciliary dyskinesia, Deficiency (Ig, α1-AT), Erosion (TB/NTM), Fibrosis/aspiration
Key investigations: HRCT (signet ring sign, tram tracks) + sputum culture
Exacerbation?
  • YES: antibiotics (ciprofloxacin for Pseudomonas) + airway clearance
  • NO: long-term azithromycin prophylaxis + daily physiotherapy

③ PULMONARY FIBROSIS - IPF (Green)

Reasoning flow:
  1. Suspect - older male smoker, gradual exertional dyspnoea, Velcro crackles, clubbing
  2. HRCT - UIP pattern: honeycombing + traction bronchiectasis, lower lobe, subpleural
  3. PFTs - restrictive (↓FVC, ↓TLC, ↓DLCO)
  4. Manage:
    • Nintedanib or Pirfenidone (antifibrotics - slow progression)
    • NO steroids + azathioprine + NAC (harmful)
    • Lung transplant for end-stage

④ PNEUMONIA (Pink)

Reasoning flow:
  1. Classify - CAP / HAP / VAP / aspiration
  2. CURB-65 severity:
    • 0-1 = outpatient amoxicillin 5 days
    • 2 = admit, dual oral/IV
    • 3-5 = ICU, IV antibiotics (piperacillin-tazobactam + azithromycin)
  3. Investigate - blood cultures before antibiotics, CXR, urine antigens
  4. Complications - empyema (drain), ARDS (protective ventilation), sepsis (early antibiotics within 1h)
  5. Repeat CXR at 6 weeks to confirm resolution

Sources: Harrison's IM 22E (2025), Goldman-Cecil Medicine, Murray & Nadel's Textbook of Respiratory Medicine, The Washington Manual

Lets go to pnemonia alone right now ... i want a perfect flowcgart for case sheet on oneumia what to ask and y to ask like that as a flowchart

Finding Sources
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Reading File
Writing File

~/rs-flowchart/pneumonia_casesheet.py

from PIL import Image, ImageDraw, ImageFont
import os, textwrap

# ── Canvas ─────────────────────────────────────────────────────────────────
W = 2600
H = 7800
BG = (8, 10, 28)
canvas = Image.new("RGB", (W, H), BG)
draw = ImageDraw.Draw(canvas)

# ── Fonts ──────────────────────────────────────────────────────────────────
def fnt(size, bold=False):
    paths = [
        "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" if bold else
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
        "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf" if bold else
        "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
    ]
    for p in paths:
        if os.path.exists(p):
            return ImageFont.truetype(p, size)
    return ImageFont.load_default()

F_MEGA   = fnt(72, True)
F_BIG    = fnt(52, True)
F_HEAD   = fnt(40, True)
F_SUB    = fnt(33, True)
F_BODY   = fnt(27)
F_SMALL  = fnt(23)
F_TINY   = fnt(20)

# ── Palette ────────────────────────────────────────────────────────────────
# Each step has a colour
C = {
    "step":    (255, 200, 0),    # yellow - step labels
    "why":     (100, 210, 255),  # cyan - "why ask" text
    "box_hdr": (30, 35, 80),     # dark box header
    "arrow":   (200, 200, 200),
    "green":   (80, 220, 120),
    "orange":  (255, 160, 60),
    "red":     (255, 80, 80),
    "white":   (240, 245, 255),
    "dimgrey": (90, 100, 130),
    "lgrey":   (160, 175, 200),
}

STEPS = [
    (255, 200, 0),    # 1 CHIEF COMPLAINT
    (255, 120, 50),   # 2 HISTORY OF PRESENTING ILLNESS
    (180, 120, 255),  # 3 PAST HISTORY
    (80, 200, 255),   # 4 PERSONAL / SOCIAL HISTORY
    (80, 230, 120),   # 5 SYSTEMIC REVIEW
    (255, 100, 150),  # 6 GENERAL EXAM
    (255, 210, 80),   # 7 RESPIRATORY EXAM
    (140, 220, 255),  # 8 INVESTIGATIONS
    (255, 140, 80),   # 9 SEVERITY SCORE
    (140, 255, 160),  # 10 DIAGNOSIS
    (255, 100, 120),  # 11 MANAGEMENT
    (200, 180, 255),  # 12 COMPLICATIONS / FOLLOW UP
]

# ── Helpers ────────────────────────────────────────────────────────────────
def rr(xy, fill, r=16, outline=None, ow=2):
    draw.rounded_rectangle(xy, radius=r, fill=fill, outline=outline, width=ow)

def text_c(text, cx, y, fnt, color):
    bb = draw.textbbox((0,0), text, font=fnt)
    w = bb[2]-bb[0]
    draw.text((cx - w//2, y), text, font=fnt, fill=color)

def wrapped(text, x, y, max_w, fnt, color, lsp=6, indent=0):
    cw = max(int(max_w / (fnt.size * 0.58)), 12)
    lines = []
    for raw in text.split("\n"):
        if raw == "":
            lines.append("")
        else:
            lines += textwrap.wrap(raw, width=cw) or [""]
    lh = fnt.size + lsp
    for i, ln in enumerate(lines):
        draw.text((x + indent, y + i*lh), ln, font=fnt, fill=color)
    return y + len(lines)*lh + 4

def arrow_down(x, y1, y2, color=(180,180,200), w=4):
    draw.line([(x, y1),(x, y2)], fill=color, width=w)
    draw.polygon([(x,y2),(x-10,y2-18),(x+10,y2-18)], fill=color)

def section_divider(y):
    draw.line([(40, y), (W-40, y)], fill=(40,45,90), width=2)
    return y + 8

# ── BIG step block ─────────────────────────────────────────────────────────
def step_block(step_num, step_title, step_color, content_rows, y,
               ask_col=(240,245,255), why_col=(100,210,255)):
    """
    content_rows: list of (question_text, why_text)
    """
    PAD = 40
    BW = W - 2*PAD
    x = PAD

    # Step number badge
    badge_w = 130
    rr([x, y, x+badge_w, y+50], fill=step_color, r=10)
    text_c(f"STEP {step_num}", x + badge_w//2, y+8, F_SUB, (10,12,30))

    # Step title
    draw.text((x + badge_w + 18, y+6), step_title, font=F_HEAD, fill=step_color)
    y += 60

    # Column headers
    col_q = int(BW * 0.44)
    col_w_w = BW - col_q - 12
    hdr_h = 38
    rr([x, y, x+col_q, y+hdr_h], fill=(30,35,75), r=8)
    rr([x+col_q+12, y, x+BW, y+hdr_h], fill=(10,30,50), r=8)
    draw.text((x+10, y+6), "WHAT TO ASK / EXAMINE / ORDER", font=F_SMALL, fill=(220,220,80))
    draw.text((x+col_q+22, y+6), "WHY  —  Clinical Reasoning", font=F_SMALL, fill=(100,210,255))
    y += hdr_h + 4

    # Rows
    for i, (q, w) in enumerate(content_rows):
        row_bg = (18, 20, 50) if i%2==0 else (22, 25, 58)
        # Estimate row height
        cw_q = max(int(col_q / (F_BODY.size * 0.6)), 12)
        cw_w = max(int(col_w_w / (F_BODY.size * 0.6)), 12)
        lines_q = []
        for r2 in q.split("\n"):
            lines_q += textwrap.wrap(r2, width=cw_q) or [""]
        lines_w = []
        for r2 in w.split("\n"):
            lines_w += textwrap.wrap(r2, width=cw_w) or [""]
        row_h = max(len(lines_q), len(lines_w)) * (F_BODY.size + 5) + 14

        rr([x, y, x+BW, y+row_h], fill=row_bg, r=6)
        # bullet circle
        draw.ellipse([x+6, y+row_h//2-6, x+18, y+row_h//2+6], fill=step_color)

        # Q text
        ty = y+8
        for ln in lines_q:
            draw.text((x+26, ty), ln, font=F_BODY, fill=ask_col)
            ty += F_BODY.size + 5

        # W text
        ty = y+8
        for ln in lines_w:
            draw.text((x+col_q+22, ty), ln, font=F_BODY, fill=why_col)
            ty += F_BODY.size + 5

        y += row_h + 3

    y += 12
    return y


# ════════════════════════════════════════════════════════════════════════════
#  MASTER TITLE
# ════════════════════════════════════════════════════════════════════════════
y = 20
rr([20, y, W-20, y+100], fill=(18,22,60), r=20, outline=(80,120,240), ow=3)
text_c("PNEUMONIA — Complete Clinical Case Sheet Flowchart", W//2, y+10, F_BIG, (255,220,60))
text_c("What to Ask  |  What to Examine  |  Why it Matters  |  Investigations  |  Management",
       W//2, y+68, F_SMALL, (140,160,220))
y += 115

# Column legend
rr([40, y, 600, y+34], fill=(30,35,75), r=8)
rr([620, y, W-40, y+34], fill=(10,30,50), r=8)
draw.text((52, y+6), "WHAT TO ASK / EXAMINE / ORDER", font=F_SMALL, fill=(220,220,80))
draw.text((632, y+6), "WHY — Clinical Reasoning", font=F_SMALL, fill=(100,210,255))
y += 44

y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 1 — CHIEF COMPLAINT
# ════════════════════════════════════════════════════════════════════════════
y = step_block(1, "CHIEF COMPLAINT", STEPS[0], [
    ("Patient's main presenting symptom in their own words",
     "Sets the clinical frame — respiratory vs systemic vs cardiac"),
    ("Duration of the complaint",
     "Acute (<1 wk) = likely infection; Chronic = consider TB, malignancy"),
    ("Onset — sudden vs gradual",
     "Sudden = lobar pneumonia (S. pneumoniae); gradual = atypical (Mycoplasma)"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 2 — HISTORY OF PRESENTING ILLNESS
# ════════════════════════════════════════════════════════════════════════════
y = step_block(2, "HISTORY OF PRESENTING ILLNESS (HOPI)", STEPS[1], [
    ("Cough — present or absent?",
     "Most sensitive symptom; absent cough does NOT exclude pneumonia"),
    ("Cough character — dry vs productive",
     "Productive = bacterial (typical); Dry = atypical/viral/Mycoplasma/Legionella"),
    ("Sputum — colour, amount, blood",
     "Rusty = pneumococcal; Green/yellow = bacterial; Frank haemoptysis → TB, abscess, cancer"),
    ("Fever — onset, pattern, chills/rigors",
     "High fever + single rigor = S. pneumoniae; Relative bradycardia with fever = Legionella/Typhoid"),
    ("Dyspnoea — at rest or exertional, severity",
     "RR ≥30 is part of CURB-65 (severe marker); hypoxia drives confusion and cyanosis"),
    ("Pleuritic chest pain — sharp, worsens with breathing",
     "Indicates pleural involvement = consolidation reaching pleura (typical bacterial)"),
    ("Haemoptysis",
     "Blood-streaked = bronchitis/pneumonia; Massive = TB, lung abscess, cancer — must exclude"),
    ("Associated symptoms: sweats, weight loss",
     "Night sweats + weight loss + haemoptysis → TB until proven otherwise"),
    ("Myalgia, headache, sore throat preceding illness",
     "Prodromal viral illness → secondary bacterial pneumonia (influenza → S. aureus/S. pneumoniae)"),
    ("Any vomiting, aspiration, altered consciousness",
     "Aspiration risk → anaerobic organisms, Klebsiella; aspiration pneumonia = lower lobe"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 3 — PAST MEDICAL HISTORY
# ════════════════════════════════════════════════════════════════════════════
y = step_block(3, "PAST MEDICAL HISTORY (PMH)", STEPS[2], [
    ("Previous pneumonia / chest infections",
     "Recurrent = consider bronchiectasis, immunodeficiency, structural lung disease"),
    ("COPD / Asthma / Bronchiectasis",
     "Pre-existing lung disease worsens severity; CURB-65 may underestimate severity"),
    ("Diabetes mellitus",
     "Immunosuppression → risk of Klebsiella, S. aureus, fungi; poor glucose control → worse outcome"),
    ("HIV / Immunosuppression",
     "PCP (P. jirovecii) if CD4 <200; CMV, fungi (Aspergillus, Cryptococcus) if severely immunocompromised"),
    ("Cardiac disease (CHF, valvular)",
     "Heart failure can mimic pneumonia; CHF + pneumonia → worse prognosis; right-sided endocarditis → septic emboli"),
    ("Malignancy / chemotherapy",
     "Chemotherapy → neutropenia → atypical organisms; malignancy can cause post-obstructive pneumonia"),
    ("Renal failure / dialysis",
     "Immunosuppressed + HCAP risk organisms (MRSA, Pseudomonas); may affect drug dosing"),
    ("Recent surgery / hospitalisation (last 90 days)",
     "If within 90 days → HAP/HCAP organisms (MRSA, resistant GNBs, Pseudomonas)"),
    ("Swallowing problems / neurological disease",
     "Stroke, Parkinson's, dementia → aspiration risk → lower lobe patchy pneumonia, anaerobes"),
    ("Previous TB / contact with TB",
     "Reactivation TB in immunosuppressed; atypical cavitating picture"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 4 — PERSONAL / SOCIAL / TRAVEL HISTORY
# ════════════════════════════════════════════════════════════════════════════
y = step_block(4, "PERSONAL / SOCIAL / TRAVEL / DRUG HISTORY", STEPS[3], [
    ("Smoking history — pack years",
     "Damages mucociliary clearance; increases risk of S. pneumoniae, H. influenzae, S. aureus"),
    ("Alcohol use",
     "Aspiration risk when intoxicated; impairs immunity → S. pneumoniae, Klebsiella, anaerobes"),
    ("IV drug use",
     "Right-sided endocarditis → septic pulmonary emboli mimicking pneumonia (MRSA)"),
    ("Occupation — healthcare worker, farmer, abattoir",
     "Healthcare → MRSA, MDR organisms; farmers → Q fever (Coxiella), psittacosis (birds)"),
    ("Travel history — where? when?",
     "Southeast Asia/Middle East → MERS-CoV; US southwest → Coccidioidomycosis; Ohio valley → Histoplasmosis; Sub-Saharan Africa → TB"),
    ("Animal / bird exposure",
     "Birds → Chlamydophila psittaci (psittacosis); Bats/caves → Histoplasma; Sheep/cattle → Q fever"),
    ("Hostel / prison / close-contact living",
     "TB risk; Legionella if hotel/spa/AC exposure"),
    ("Current medications — steroids, immunosuppressants, ACE inhibitors",
     "ACE inhibitor → cough (mimics symptom); steroids/immunosuppressants → opportunistic infections"),
    ("Vaccination status — influenza, pneumococcal",
     "Vaccinated → less likely typical pneumococcal; unvaccinated elderly → high risk"),
    ("HIV risk factors — sexual history, transfusions",
     "Determines need for PCP prophylaxis and HIV testing"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 5 — SYSTEMIC REVIEW (targeted)
# ════════════════════════════════════════════════════════════════════════════
y = step_block(5, "SYSTEMIC REVIEW (Targeted)", STEPS[4], [
    ("Confusion / altered mental status",
     "C in CURB-65 — confusion is a SEVERE sign; also consider meningitis"),
    ("Abdominal pain, diarrhoea",
     "Lower lobe pneumonia can refer pain to abdomen; Legionella causes GI symptoms"),
    ("Rash, joint pain",
     "Erythema multiforme → Mycoplasma; Erythema nodosum → TB/fungi; SLE → lupus pneumonitis"),
    ("Ear pain, sore throat",
     "Preceding URTI → viral then secondary bacterial pneumonia"),
    ("Urinary symptoms",
     "Elderly often present with incontinence/UTI-like symptoms as atypical pneumonia presentation"),
    ("Leg swelling / calf pain",
     "DVT → pulmonary embolism mimics or complicates pneumonia"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 6 — GENERAL EXAMINATION
# ════════════════════════════════════════════════════════════════════════════
y = step_block(6, "GENERAL PHYSICAL EXAMINATION", STEPS[5], [
    ("Temperature — fever or hypothermia",
     "Fever = active infection; Hypothermia in elderly/immunocompromised = sepsis (WORSE sign)"),
    ("Pulse rate — tachycardia?",
     "HR >100 = systemic response to infection; relative bradycardia + fever → Legionella/typhoid"),
    ("Blood pressure — hypotension?",
     "Systolic <90 or diastolic ≤60 = B in CURB-65 = SEVERE; suggests sepsis → need vasopressors"),
    ("Respiratory rate — count for full minute",
     "RR ≥30 = R in CURB-65 = SEVERE; most sensitive early sign of respiratory compromise"),
    ("SpO2 on room air",
     "SpO2 <92% → hospitalization; <88% → likely need supplemental O2; <90% → may need ICU"),
    ("Consciousness — GCS or AVPU",
     "New confusion = C in CURB-65; low GCS → risk of aspiration, ICU needed"),
    ("Cyanosis — peripheral vs central",
     "Central cyanosis = hypoxaemia; indicates O2 Sat usually <85%"),
    ("Clubbing of fingers",
     "Not seen in acute pneumonia; if present → chronic lung disease (bronchiectasis, ILD, lung cancer)"),
    ("Lymphadenopathy",
     "Hilar adenopathy → TB, sarcoidosis, lymphoma masquerading as pneumonia"),
    ("Jaundice",
     "Hepatitis with viral pneumonia (influenza); leptospirosis if exposure; haemolysis (Mycoplasma)"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 7 — RESPIRATORY SYSTEM EXAMINATION
# ════════════════════════════════════════════════════════════════════════════
y = step_block(7, "RESPIRATORY SYSTEM EXAMINATION", STEPS[6], [
    ("INSPECTION — chest shape, movement, accessory muscles",
     "Asymmetric chest movement → ipsilateral consolidation/effusion/collapse; accessory muscle use → severe dyspnoea"),
    ("INSPECTION — trachea position",
     "Trachea central in pneumonia; deviated AWAY from lesion = effusion/tension pneumothorax; TOWARD = collapse"),
    ("PALPATION — tactile vocal fremitus (TVF)",
     "INCREASED TVF over consolidation (solid transmits better); DECREASED over effusion or collapse"),
    ("PALPATION — expansion",
     "REDUCED on affected side in consolidation, effusion, or collapse"),
    ("PERCUSSION — note over affected area",
     "DULL = consolidation or pleural effusion; STONY DULL = effusion (different from dull)"),
    ("AUSCULTATION — breath sounds",
     "BRONCHIAL breathing over consolidation (high-pitched, loud, gap between I and E); Absent = effusion"),
    ("AUSCULTATION — crackles",
     "Fine late-inspiratory crackles over consolidation; do not clear with coughing (vs bronchitis)"),
    ("AUSCULTATION — vocal resonance (VR)",
     "INCREASED VR / whispering pectoriloquy = consolidation; DECREASED = effusion"),
    ("AUSCULTATION — aegophony (E to A change)",
     "Say 'E' → heard as 'A' over consolidation — highly specific for lobar pneumonia"),
    ("PLEURAL RUB",
     "Creaking sound (like walking on fresh snow) = inflamed pleura = pleuritis; pleural effusion silences it"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 8 — INVESTIGATIONS
# ════════════════════════════════════════════════════════════════════════════
y = step_block(8, "INVESTIGATIONS — ORDER & REASON", STEPS[7], [
    ("CXR PA (Chest X-Ray) — MANDATORY",
     "Diagnosis requires NEW opacity on imaging; lobar = S. pneumoniae; patchy bilateral = viral/atypical; cavitation = S. aureus/TB; RLL = aspiration"),
    ("FBC (Full Blood Count)",
     "WBC >15k = bacterial; Neutrophilia = bacterial; Lymphocytosis = viral; Thrombocytopenia = sepsis (severe)"),
    ("CRP (C-reactive protein)",
     "Elevated in infection; used for monitoring response and antibiotic stewardship; normal CRP makes pneumonia less likely"),
    ("Procalcitonin (PCT)",
     "More specific for bacterial infection than CRP; serial PCT guides stopping antibiotics; NOT for initial Dx alone"),
    ("Urea and Electrolytes (U&E)",
     "Urea >7 mmol/L = U in CURB-65; hyponatraemia → Legionella; renal function for drug dosing"),
    ("Liver function tests (LFTs)",
     "Legionella causes raised ALT; severe sepsis → hepatic dysfunction; baseline for antibiotics"),
    ("Blood cultures × 2 (before antibiotics)",
     "Bacteraemia in 10-30% of CAP; positive = escalate Abx; take BEFORE first antibiotic dose"),
    ("Sputum MC&S (Microscopy, Culture, Sensitivity)",
     "Identify organism; guide de-escalation; most useful in moderate-severe CAP; ensure taken BEFORE Abx"),
    ("Urinary Legionella antigen",
     "Detects L. pneumophila serogroup 1; quick, non-invasive; positive → add macrolide/fluoroquinolone"),
    ("Urinary pneumococcal antigen",
     "Positive confirms S. pneumoniae even after antibiotics started; not affected by prior Abx treatment"),
    ("ABG (Arterial Blood Gas) if SpO2 <94%",
     "PaO2, PaCO2, pH; PaO2 <60 = type 1 RF; rising PaCO2 = type 2 RF = ventilatory failure → ICU"),
    ("HIV test (if risk factors)",
     "PCP presents as bilateral diffuse pneumonia in HIV (CD4 <200); requires co-trimoxazole NOT standard Abx"),
    ("CT Chest if diagnosis uncertain / poor response",
     "Higher sensitivity than CXR; detects cavitation, effusion, adenopathy; excludes PE, malignancy"),
    ("Nasopharyngeal swab PCR (influenza/COVID/RSV)",
     "Viral pneumonia → antivirals (oseltamivir for flu); no/de-escalate antibiotics; cohort isolation"),
    ("Thoracocentesis if pleural effusion present",
     "pH <7.2 or glucose <60 or LDH >1000 = COMPLICATED → needs chest drain; send for culture/cytology/Light's"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 9 — SEVERITY SCORING
# ════════════════════════════════════════════════════════════════════════════
y_start = y
# CURB-65 box
BW = W - 80
rr([40, y, 40+BW, y+58], fill=STEPS[8], r=14)
text_c("STEP 9 — SEVERITY SCORING: CURB-65", W//2, y+10, F_HEAD, (10,12,30))
y += 66

# Table header
col5 = (BW - 10) // 5
headers5 = ["Variable", "C  Confusion", "U  Urea >7", "R  RR ≥30", "B  BP sys<90", "65  Age ≥65"]
# 6 cols but 5 variables + label col
col6 = (BW - 10) // 6
h_labels = ["Variable", "C — Confusion\n(new onset)", "U — Urea\n>7 mmol/L",
            "R — RR\n≥30/min", "B — BP sys<90\nor dia≤60", "65 — Age\n≥65 years"]
sub_labels = ["Why it matters", "= C in CURB-65;\nICU risk, aspiration",
              "Severity marker,\nrenal impairment",
              "Respiratory\nfailure marker",
              "Sepsis /\ncirculatory shock",
              "Reduced reserve,\nhigh mortality"]
for j, (hl, sl) in enumerate(zip(h_labels, sub_labels)):
    bx = 40 + j*col6
    bg_c = (30,35,80) if j==0 else (18,20,55)
    rr([bx, y, bx+col6-4, y+100], fill=bg_c, r=8, outline=STEPS[8], ow=1)
    draw.text((bx+6, y+6),  hl, font=F_SMALL, fill=STEPS[8])
    draw.text((bx+6, y+54), sl, font=F_TINY,  fill=(160,185,220))
y += 108

# Score guide
score_rows = [
    ("0",   "Low risk (30-day mortality ~1.5%)", "Outpatient, oral amoxicillin 500mg TDS × 5d", C["green"]),
    ("1–2", "Moderate risk",                     "Admit if any concern; oral/IV dual therapy", C["orange"]),
    ("3–5", "Severe (mortality ≥22%)",            "ICU consideration; IV piperacillin-tazobactam + azithromycin; vasopressors/MV if needed", C["red"]),
]
for sc, risk, rec, col in score_rows:
    rr([40, y, 40+BW, y+52], fill=(15,18,45), r=8, outline=col, ow=2)
    draw.text((50, y+8),   f"Score {sc}", font=F_SUB,  fill=col)
    draw.text((300, y+10), risk,          font=F_BODY, fill=(200,210,230))
    draw.text((900, y+10), f"→  {rec}",  font=F_BODY, fill=C["white"])
    y += 56

# ICU criteria
y += 8
rr([40, y, 40+BW, y+50], fill=(50,10,10), r=10, outline=C["red"], ow=2)
draw.text((52, y+8), "DIRECT ICU CRITERIA (regardless of CURB-65):", font=F_SUB, fill=C["red"])
draw.text((740, y+8), "Septic shock (vasopressors needed)  OR  Acute respiratory failure (intubation/MV needed)",
          font=F_BODY, fill=C["white"])
y += 58

y = section_divider(y + 4)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 10 — DIFFERENTIALS / CONFIRM DIAGNOSIS
# ════════════════════════════════════════════════════════════════════════════
y = step_block(10, "DIFFERENTIAL DIAGNOSIS & CONFIRM", STEPS[9], [
    ("Pulmonary oedema / CHF",
     "Bilateral basal crackles + raised JVP + orthopnoea; CXR: cardiomegaly + bat-wing shadowing; BNP elevated"),
    ("Pulmonary embolism (PE)",
     "Pleuritic chest pain + dyspnoea + normal CXR + hypoxia; Wells score + D-dimer + CTPA"),
    ("Lung cancer with post-obstructive pneumonia",
     "Recurrent pneumonia in same lobe; smoker; CXR mass; needs bronchoscopy"),
    ("Tuberculosis",
     "Upper lobe cavitation; night sweats, weight loss, chronic cough; AFB smear + culture; contact history"),
    ("Lung abscess",
     "Fever, putrid sputum, cavitation with fluid level on CXR; anaerobes/S. aureus; prolonged Abx"),
    ("Pneumocystis pneumonia (PCP)",
     "HIV + bilateral diffuse GGO; no bacteria on culture; responds to co-trimoxazole NOT standard Abx"),
    ("COVID-19 / Viral pneumonia",
     "Bilateral patchy GGO on CT; PCR positive; antivirals if appropriate; no routine Abx"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 11 — MANAGEMENT
# ════════════════════════════════════════════════════════════════════════════
y = step_block(11, "MANAGEMENT — ANTIBIOTICS + SUPPORTIVE", STEPS[10], [
    ("Administer O2 to keep SpO2 ≥94% (or 88–92% in COPD)",
     "Prevents hypoxaemic organ damage; titrate to avoid hypercapnia in COPD patients"),
    ("IV access + IV fluids if hypotensive / dehydrated",
     "Sepsis resuscitation: 30 mL/kg crystalloid bolus; monitor lactate; target MAP ≥65 mmHg"),
    ("MILD CAP (outpatient, CURB-65 = 0-1):\nAmoxicillin 500mg TDS × 5 days",
     "S. pneumoniae is most common; amoxicillin has excellent coverage; use doxycycline if penicillin allergy"),
    ("MODERATE CAP (ward, CURB-65 = 2):\nAmoxicillin IV + Clarithromycin IV",
     "Dual covers typical (amoxicillin) AND atypical (Legionella, Mycoplasma) organisms"),
    ("SEVERE CAP (ICU, CURB-65 ≥3):\nPiperacillin-tazobactam IV + Azithromycin IV\nOR Co-amoxiclav + Clarithromycin",
     "Broader spectrum for resistant organisms; macrolide covers Legionella, Mycoplasma; consider MRSA cover (vancomycin) if risk factors"),
    ("ASPIRATION pneumonia:\nAdd Metronidazole 400mg TDS",
     "Anaerobic cover for oral flora (Bacteroides, Fusobacterium); lower lobe involved typically"),
    ("HAP / VAP:\nPiperacillin-tazobactam ± Vancomycin/Linezolid (for MRSA)",
     "MRSA risk in HAP; Pseudomonas cover essential; use local antibiogram to guide choice"),
    ("PCP (HIV):\nCo-trimoxazole (Trimethoprim-sulfamethoxazole) high dose",
     "First-line for PCP; add prednisolone if PaO2 <70 mmHg; standard Abx will FAIL PCP"),
    ("Antibiotic DURATION:\n5 days if afebrile; 7–10d if severe / bacteraemic",
     "PCT-guided stopping reduces duration; ensure clinical stability first: afebrile, HR/RR normal, tolerating oral"),
    ("Analgesia for pleuritic pain",
     "Paracetamol/NSAIDs; adequate pain relief improves breathing effort and sputum clearance"),
    ("VTE prophylaxis (LMWH + TED stockings)",
     "Hospitalised patients at VTE risk; pneumonia itself increases clotting tendency"),
    ("Physiotherapy / positioning",
     "Upright posture improves VQ matching; physio helps sputum clearance"),
], y)
y = section_divider(y)

# ════════════════════════════════════════════════════════════════════════════
#  STEP 12 — COMPLICATIONS + FOLLOW UP
# ════════════════════════════════════════════════════════════════════════════
y = step_block(12, "COMPLICATIONS & FOLLOW UP", STEPS[11], [
    ("Parapneumonic effusion / Empyema",
     "If effusion develops → thoracocentesis; pH <7.2 / glucose <60 / turbid = complicated → chest drain + prolonged Abx"),
    ("Lung abscess",
     "Cavity with fluid level; prolonged IV/oral Abx (6–8 weeks); percutaneous drainage if not responding"),
    ("Respiratory failure (type 1 / type 2)",
     "Type 1 (↓PaO2, normal CO2) → high-flow O2; Type 2 (↓PaO2, ↑CO2) → NIV/BiPAP; intubation if failing"),
    ("ARDS (Acute Respiratory Distress Syndrome)",
     "Bilateral infiltrates + PaO2/FiO2 <300 + no heart failure; lung protective ventilation 6 mL/kg; prone positioning"),
    ("Sepsis / Septic Shock",
     "Sepsis-3 criteria: suspected infection + SOFA ≥2; septic shock: vasopressors to maintain MAP ≥65; early Abx within 1h"),
    ("Metastatic infection (meningitis, endocarditis)",
     "Bacteraemia → seeding; persistent fever + new focal signs → investigate; LP if meningism"),
    ("Hepatitis / Multi-organ dysfunction",
     "Legionella, severe sepsis → LFT derangement; monitor renal, liver, coag in severe disease"),
    ("FOLLOW UP: Repeat CXR at 6 weeks",
     "CXR should clear by 6 wks (young) or 12 wks (elderly); failure to clear → exclude malignancy, TB"),
    ("FOLLOW UP: Offer pneumococcal + influenza vaccine",
     "Reduces future episodes; give before discharge or at follow-up; part of prevention strategy"),
], y)
y = section_divider(y)

# ── Footer ─────────────────────────────────────────────────────────────────
y += 6
rr([20, y, W-20, y+65], fill=(16,20,50), r=14, outline=(60,80,180), ow=2)
text_c("Sources: Harrison's Principles IM 22E (2025)  |  Goldman-Cecil Medicine  |  Murray & Nadel's Respiratory Medicine",
       W//2, y+10, F_SMALL, C["lgrey"])
text_c("For clinical education — MBBS/MD Pneumonia Case Sheet  |  CURB-65 based on British Thoracic Society guidelines",
       W//2, y+38, F_TINY, C["dimgrey"])
y += 75

# ── Crop & Save ──────────────────────────────────────────────────────────
final = canvas.crop((0, 0, W, min(y, H)))
out_path = "/home/daytona/workspace/rs-flowchart/Pneumonia_CaseSheet_Flowchart.png"
final.save(out_path, dpi=(150, 150))
print(f"Saved: {out_path}")
print(f"Canvas size: {final.size}")
Running Command

cd /home/daytona/workspace/rs-flowchart && python3 pneumonia_casesheet.py 2>&1

Running Command

ls -lh /home/daytona/workspace/rs-flowchart/Pneumonia_CaseSheet_Flowchart.png

Your Pneumonia Case Sheet Flowchart is ready! 🎯

Download

Pneumonia CaseSheet Flowchart

Image · PNG

Size: 2600 × 7800 px | 1.8 MB | Print-ready (150 DPI)

What's inside — 12 Steps with WHY column:

StepSectionWhat it covers
1Chief ComplaintOnset, duration, sudden vs gradual - frames the whole case
2HOPICough type, sputum colour, fever pattern, haemoptysis, pleuritic pain, myalgia - each with its clinical reason
3Past Medical HistoryCOPD, DM, HIV, CHF, malignancy, prior TB, recent hospitalisation
4Personal/Social/TravelSmoking, alcohol, IVDU, occupation, travel (specific organisms per region), animal exposure, drugs
5Systemic ReviewConfusion (CURB), abdo pain (Legionella), rash (Mycoplasma), DVT
6General ExamTemp, pulse, BP, RR, SpO2, GCS, cyanosis, clubbing, lymph nodes
7Respiratory ExamFull IPTPA — inspection, palpation (TVF, expansion), percussion, auscultation (bronchial breathing, crackles, aegophony, pleural rub)
8Investigations15 tests — CXR, FBC, CRP, PCT, blood cultures, sputum, urinary Legionella/pneumococcal Ag, ABG, HIV, CTPA — each with exact reason
9CURB-65 ScoringFull table + score = 0/1-2/3-5 actions + direct ICU criteria
10DifferentialsCHF, PE, TB, lung abscess, PCP, lung cancer, COVID
11ManagementMild/Moderate/Severe CAP, aspiration, HAP, PCP - doses + durations + reasoning
12Complications & Follow-upEmpyema, ARDS, septic shock, lung abscess + repeat CXR at 6 weeks rule

Pro tip: The left column tells you WHAT to ask/do, the right (cyan) column tells you WHY - that's how you answer examiners when they ask "why are you asking that?"
This is a shared conversation. Sign in to Orris to start your own chat.