Simplify asthma from fishman textbook and Gina 2026 with easy understanding way and also explain clinician point of view what should we do in opd for asthma patients during diagnosis treatment
GINA 2026 asthma guidelines diagnosis treatment updates
asthma diagnosis management guidelines 2025 2026
https://www.guidelinecentral.com/insights/may-2026-gina-asth…
"Asthma is characterized by airway inflammation leading to mostly reversible airways obstruction. Chronic inflammation, angiogenesis, subepithelial fibrosis, and extracellular matrix deposition are often features of the disease."
- Murray & Nadel's Textbook of Respiratory Medicine
| Layer | What Happens | Simple Analogy |
|---|---|---|
| Airway wall | Inflammation, edema, subepithelial fibrosis | Wall of a pipe swelling inward |
| Smooth muscle | Contracts due to triggers | Pipe squeezing shut |
| Lumen | Mucus plugging | Pipe getting blocked with goo |
| Test | Positive If... | Meaning |
|---|---|---|
| FEV1/FVC ratio | < 0.70 (or below lower limit of normal) | Obstruction present |
| Bronchodilator reversibility | FEV1 increases ≥12% AND ≥200 mL after salbutamol 400 mcg | Confirms reversible obstruction |
| Peak flow variability | >10% diurnal variation on ≥2 days/week for 2 weeks | Supports asthma |
| Methacholine challenge (PC20) | PC20 ≤8 mg/mL | Confirms airway hyperresponsiveness |
| Exercise test | FEV1 falls ≥10% after exercise | Exercise-induced asthma |
OPD Tip: If spirometry is normal but history is strong, do methacholine challenge or give a trial of ICS for 4-6 weeks and reassess spirometry.
| Feature | Well Controlled | Partly Controlled | Uncontrolled |
|---|---|---|---|
| Daytime symptoms | ≤2 days/week | >2 days/week | ≥3 of these features |
| Night waking | None | Any | |
| Reliever use | ≤2 days/week | >2 days/week | |
| Activity limitation | None | Any |
SABA (salbutamol) alone is NO LONGER recommended at any step. Every asthma patient must receive ICS-containing therapy.
| Step | Preferred Controller | Preferred Reliever | Notes |
|---|---|---|---|
| Step 1 (mild, intermittent) | As-needed low-dose ICS-formoterol | ICS-formoterol | NEW: ICS-SABA (AIR) also option at Step 1 |
| Step 2 (mild, persistent) | Low-dose ICS daily + as-needed ICS-formoterol | ICS-formoterol | |
| Step 3 (moderate) | Low-dose ICS-formoterol (MART) | ICS-formoterol | |
| Step 4 (uncontrolled) | Medium-dose ICS-formoterol (MART) | ICS-formoterol | Consider LAMA add-on |
| Step 5 (severe) | High-dose ICS + LABA + specialist review | ICS-formoterol | Add LAMA (tiotropium), consider biologics |
| Drug | Target | Who Gets It |
|---|---|---|
| Omalizumab | Anti-IgE | Allergic asthma, high IgE, sensitized allergens |
| Mepolizumab / Benralizumab / Reslizumab | Anti-IL-5 / Anti-IL-5R | Eosinophilic asthma (blood eos ≥150-300) |
| Dupilumab | Anti-IL-4/IL-13 (IL-4Rα) | Type 2 asthma, also works in CRS/eczema |
| Tezepelumab | Anti-TSLP | Broadest - works in type 2 and non-type 2 |
| Depemokimab | Anti-IL-5 (long-acting) | NEW in GINA 2026 - dosing every 6 months |
GINA 2026 update: Depemokimab (ultra-long-acting anti-IL-5) and omalizumab biosimilar are now incorporated.
1. History (10 min)
├── Symptom pattern (wheeze/cough/breathlessness/chest tightness)
├── Triggers (allergens, exercise, cold, ASA, occupational)
├── Atopic history (eczema, rhinitis, family history)
├── Previous diagnoses, hospitalizations, ICU admissions
└── Current medications (beta-blockers? NSAIDs?)
2. Examination
├── Vitals: RR, SpO2, HR
├── Nose: polyps, rhinitis
└── Chest: wheeze, air entry
3. Investigations
├── Spirometry with bronchodilator reversibility test [MANDATORY]
├── Peak expiratory flow (PEF) diary for 2 weeks
├── Chest X-ray (to exclude other diagnoses)
├── CBC (eosinophilia?)
├── Total IgE, skin prick test or specific IgE (if allergic asthma suspected)
└── FeNO (fractional exhaled nitric oxide) - if available, >25 ppb suggests type 2 inflammation
4. Confirm Diagnosis
└── Objective evidence of variable airflow limitation (GINA requirement)
5. Assess Control + Risk
└── GINA control table (see above)
6. Start Treatment
├── Prescribe appropriate GINA step
├── Prescribe one inhaler device type (minimize switching)
├── Demonstrate inhaler technique (most common reason for failure!)
└── Give written Asthma Action Plan
7. Education
├── Explain triggers and how to avoid
├── When to go to hospital (SpO2 <92%, can't speak full sentences)
└── No SABA-only use
Ask every visit:
├── How many times have you used your reliever this week?
├── Any night waking?
├── Any activity limitation?
├── Any exacerbations / ED visits / oral steroids?
├── Any missed doses?
└── Show me how you use your inhaler
Do every visit:
├── Check SpO2, peak flow (if available)
├── Review compliance and technique
└── Step up or down based on control assessment
Step up if: Uncontrolled for ≥1 month
Step down if: Well controlled for ≥3 months (try reducing dose by 25-50%)
| Feature | Mild | Moderate | Severe | Life-Threatening |
|---|---|---|---|---|
| Speaks | Sentences | Phrases | Words only | Mute |
| Resp Rate | Normal | 20-30/min | >30/min | >30/min |
| SpO2 | ≥95% | 92-95% | <92% | <92% |
| PEF (% predicted) | >70% | 40-69% | <40% | <25% |
| Wheeze | Moderate | Loud | Loud | Silent (no air entry) |
"Inhaler technique is still the most common reason for treatment failure. Before escalating a patient, watch them use their device."
DIAGNOSIS CHECKLIST:
☐ Variable symptoms (wheeze, cough, breathlessness, chest tightness)
☐ Spirometry: FEV1/FVC < 0.70
☐ Bronchodilator test: FEV1 rise ≥12% AND ≥200 mL
☐ Exclude other causes (CXR, CBC)
EVERY OPD VISIT:
☐ Reliever use this week? (>2x/week = uncontrolled)
☐ Night symptoms?
☐ Activity limitation?
☐ Exacerbations?
☐ CHECK INHALER TECHNIQUE
TREATMENT RULES (GINA 2026):
☐ No SABA alone - ever
☐ All patients need ICS
☐ Prefer ICS-formoterol as both reliever + controller
☐ Step up if uncontrolled ≥1 month
☐ Step down if controlled ≥3 months
☐ O2 only if SpO2 <92%
REFER TO SPECIALIST IF:
☐ Diagnosis uncertain
☐ Poor control despite step 3-4
☐ Suspected severe asthma / biologics candidate
☐ Occupational asthma
☐ Frequent exacerbations (≥2 severe/year)
Generate a clinical decision flowchart PDF for OPD asthma diagnosis and management
mkdir -p /home/daytona/workspace/asthma-flowchart && ls /home/daytona/workspace/asthma-flowchart
~/asthma-flowchart/generate_flowchart.py
"""
OPD Asthma Clinical Decision Flowchart - GINA 2026
Generates a multi-page A4 PDF with colour-coded flowcharts
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import Paragraph
from reportlab.lib.enums import TA_CENTER, TA_LEFT
W, H = A4 # 595 x 842 pt
# ── Colour palette ────────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1a3a5c")
BLUE = colors.HexColor("#1e6fa8")
LTBLUE = colors.HexColor("#d6eaf8")
GREEN = colors.HexColor("#1a7a4a")
LTGREEN = colors.HexColor("#d5f5e3")
AMBER = colors.HexColor("#c07000")
LTAMB = colors.HexColor("#fef3cd")
RED = colors.HexColor("#b22222")
LTRED = colors.HexColor("#fde8e8")
PURPLE = colors.HexColor("#6a0dad")
LTPUR = colors.HexColor("#f0e6ff")
GREY = colors.HexColor("#555555")
LTGREY = colors.HexColor("#f4f6f7")
WHITE = colors.white
ORANGE = colors.HexColor("#e07b00")
LTORA = colors.HexColor("#fff3e0")
# ── Helper drawing functions ──────────────────────────────────────────────────
def page_header(c, title, subtitle="", page_num=1, total_pages=4):
"""Draw a professional header bar."""
c.setFillColor(NAVY)
c.rect(0, H - 38*mm, W, 38*mm, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 16)
c.drawString(12*mm, H - 18*mm, "OPD ASTHMA CLINICAL DECISION FLOWCHART")
c.setFont("Helvetica", 10)
c.drawString(12*mm, H - 27*mm, title)
c.setFont("Helvetica-Oblique", 8)
c.drawRightString(W - 12*mm, H - 27*mm, f"GINA 2026 | Page {page_num}/{total_pages}")
if subtitle:
c.setFont("Helvetica", 8)
c.drawString(12*mm, H - 34*mm, subtitle)
def page_footer(c, note=""):
"""Draw footer."""
c.setFillColor(NAVY)
c.rect(0, 0, W, 10*mm, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Oblique", 7)
txt = note if note else "Based on GINA 2026 Strategy Report & Murray-Nadel's Textbook of Respiratory Medicine | For clinical use"
c.drawCentredString(W/2, 3.5*mm, txt)
def box(c, x, y, w, h, fill_col, border_col=None, radius=4):
"""Rounded rectangle."""
if border_col is None:
border_col = fill_col
c.setFillColor(fill_col)
c.setStrokeColor(border_col)
c.setLineWidth(1.2)
c.roundRect(x, y, w, h, radius, fill=1, stroke=1)
def diamond(c, cx, cy, hw, hh, fill_col, border_col=None):
"""Decision diamond."""
if border_col is None:
border_col = fill_col
p = c.beginPath()
p.moveTo(cx, cy + hh)
p.lineTo(cx + hw, cy)
p.lineTo(cx, cy - hh)
p.lineTo(cx - hw, cy)
p.closePath()
c.setFillColor(fill_col)
c.setStrokeColor(border_col)
c.setLineWidth(1.2)
c.drawPath(p, fill=1, stroke=1)
def label(c, x, y, w, text, font="Helvetica", size=8, color=colors.black,
align="center", bold=False):
"""Multi-line text inside a box - auto-wraps."""
font_name = "Helvetica-Bold" if bold else font
c.setFont(font_name, size)
c.setFillColor(color)
# Split into lines manually
words = text.split(" ")
lines = []
current = ""
max_chars = int(w / (size * 0.55))
for word in words:
test = (current + " " + word).strip()
if len(test) <= max_chars:
current = test
else:
if current:
lines.append(current)
current = word
if current:
lines.append(current)
line_h = size * 1.4
total_h = len(lines) * line_h
start_y = y + total_h / 2 - line_h * 0.3
for i, line in enumerate(lines):
ly = start_y - i * line_h
if align == "center":
c.drawCentredString(x + w/2, ly, line)
elif align == "left":
c.drawString(x + 3, ly, line)
def arrow(c, x1, y1, x2, y2, label_text="", color=GREY, lbl_color=None):
"""Arrow with optional label."""
c.setStrokeColor(color)
c.setLineWidth(1.2)
c.line(x1, y1, x2, y2)
# Arrowhead
import math
angle = math.atan2(y2 - y1, x2 - x1)
aw = 5
c.setFillColor(color)
p = c.beginPath()
p.moveTo(x2, y2)
p.lineTo(x2 - aw * math.cos(angle - 0.4), y2 - aw * math.sin(angle - 0.4))
p.lineTo(x2 - aw * math.cos(angle + 0.4), y2 - aw * math.sin(angle + 0.4))
p.closePath()
c.drawPath(p, fill=1, stroke=0)
if label_text:
mx, my = (x1+x2)/2, (y1+y2)/2
c.setFont("Helvetica-Bold", 7.5)
c.setFillColor(lbl_color if lbl_color else color)
c.drawCentredString(mx, my + 4, label_text)
def section_title(c, x, y, text, color=NAVY):
c.setFont("Helvetica-Bold", 10)
c.setFillColor(color)
c.drawString(x, y, text)
c.setStrokeColor(color)
c.setLineWidth(1)
c.line(x, y - 2, x + 170*mm, y - 2)
# ═══════════════════════════════════════════════════════════════════════════════
# PAGE 1 – DIAGNOSIS FLOWCHART
# ═══════════════════════════════════════════════════════════════════════════════
def page1_diagnosis(c):
c.setPageSize(A4)
page_header(c, "PAGE 1 — DIAGNOSIS ALGORITHM", "Step-by-step approach for new OPD patient", 1, 4)
page_footer(c)
TOP = H - 45*mm
BW = 130*mm # box width
BH = 11*mm # box height
CX = W/2 # centre x
BX = CX - BW/2
# ── Step 1: Presenting symptoms ──────────────────────────────────────────
box(c, BX, TOP - BH, BW, BH, LTBLUE, BLUE)
label(c, BX, TOP - BH, BW, "PATIENT PRESENTS WITH RESPIRATORY SYMPTOMS",
bold=True, size=9, color=NAVY)
arrow(c, CX, TOP - BH, CX, TOP - BH - 7*mm, color=BLUE)
# Symptoms list box
sy = TOP - BH - 7*mm - 24*mm
box(c, BX, sy, BW, 24*mm, LTBLUE, BLUE)
c.setFont("Helvetica-Bold", 8.5)
c.setFillColor(NAVY)
c.drawString(BX + 3*mm, sy + 19*mm, "Classic Symptom Quartet (ask ALL 4):")
c.setFont("Helvetica", 8)
c.setFillColor(colors.black)
items = ["1. Wheeze (expiratory, high-pitched)",
"2. Cough (worse at night / early morning)",
"3. Chest tightness",
"4. Breathlessness (especially on exertion)"]
for i, item in enumerate(items):
c.drawString(BX + 5*mm, sy + 14*mm - i*4.5*mm, item)
arrow(c, CX, sy, CX, sy - 7*mm, color=BLUE)
# ── Diamond 1: Variable symptoms? ────────────────────────────────────────
d1y = sy - 7*mm - 12*mm
diamond(c, CX, d1y, 52*mm, 12*mm, LTAMB, AMBER)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(AMBER)
c.drawCentredString(CX, d1y + 3, "Are symptoms VARIABLE over time &")
c.drawCentredString(CX, d1y - 5, "do they worsen with known triggers?")
# NO branch (right)
arrow(c, CX + 52*mm, d1y, CX + 68*mm, d1y, color=RED, label_text="NO")
box(c, CX + 68*mm, d1y - 7*mm, 40*mm, 14*mm, LTRED, RED)
label(c, CX + 68*mm, d1y - 7*mm, 40*mm,
"Consider alternative diagnosis (COPD, VCD, HF, PE)",
size=7, color=RED, bold=True)
# YES branch (down)
arrow(c, CX, d1y - 12*mm, CX, d1y - 12*mm - 7*mm, color=GREEN, label_text="YES")
# ── Spirometry block ─────────────────────────────────────────────────────
spy = d1y - 12*mm - 7*mm - 18*mm
box(c, BX, spy, BW, 18*mm, LTGREEN, GREEN)
c.setFont("Helvetica-Bold", 9)
c.setFillColor(GREEN)
c.drawString(BX + 3*mm, spy + 13*mm, "ORDER SPIROMETRY + BRONCHODILATOR TEST (MANDATORY)")
c.setFont("Helvetica", 8)
c.setFillColor(colors.black)
c.drawString(BX + 5*mm, spy + 8.5*mm, "Pre & Post salbutamol 400 mcg (4 puffs) via spacer")
c.drawString(BX + 5*mm, spy + 4.5*mm, "Measure FEV1, FVC, FEV1/FVC ratio | Repeat in 15 min")
arrow(c, CX, spy, CX, spy - 7*mm, color=BLUE)
# ── Diamond 2: FEV1/FVC < 0.70? ──────────────────────────────────────────
d2y = spy - 7*mm - 11*mm
diamond(c, CX, d2y, 52*mm, 11*mm, LTAMB, AMBER)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(AMBER)
c.drawCentredString(CX, d2y + 2, "FEV1/FVC < 0.70 AND")
c.drawCentredString(CX, d2y - 5, "FEV1 rises >=12% + >=200mL post-BD?")
# NO right
arrow(c, CX + 52*mm, d2y, CX + 68*mm, d2y, color=AMBER, label_text="NO")
box(c, CX + 68*mm, d2y - 7*mm, 40*mm, 14*mm, LTAMB, AMBER)
label(c, CX + 68*mm, d2y - 7*mm, 40*mm,
"Normal spirometry: consider methacholine challenge or 4-6 wk ICS trial",
size=7, color=AMBER, bold=False)
# YES down
arrow(c, CX, d2y - 11*mm, CX, d2y - 11*mm - 7*mm, color=GREEN, label_text="YES")
# ── Additional workup ─────────────────────────────────────────────────────
wky = d2y - 11*mm - 7*mm - 22*mm
box(c, BX, wky, BW, 22*mm, LTGREY, GREY)
c.setFont("Helvetica-Bold", 8.5)
c.setFillColor(GREY)
c.drawString(BX + 3*mm, wky + 17.5*mm, "ADDITIONAL WORKUP in OPD:")
c.setFont("Helvetica", 8)
c.setFillColor(colors.black)
items2 = ["CBC (eosinophilia), Total IgE, Skin prick test / specific IgE",
"Chest X-ray (exclude other causes)",
"FeNO (if available) — >25 ppb = type-2 airway inflammation",
"Peak flow diary: >10% diurnal variation x2 days/week x2 weeks"]
for i, it in enumerate(items2):
c.drawString(BX + 5*mm, wky + 13*mm - i*3.8*mm, f"\u2022 {it}")
arrow(c, CX, wky, CX, wky - 7*mm, color=BLUE)
# ── CONFIRMED DIAGNOSIS ───────────────────────────────────────────────────
cdy = wky - 7*mm - 11*mm
box(c, BX, cdy, BW, 11*mm, GREEN, GREEN)
label(c, BX, cdy, BW, "ASTHMA CONFIRMED — Proceed to Severity/Control Assessment (Page 2)",
bold=True, size=9, color=WHITE)
# ── Right sidebar: Differentials ─────────────────────────────────────────
sx = W - 52*mm
sy2 = TOP - BH - 10*mm
box(c, sx, sy2 - 70*mm, 43*mm, 70*mm, LTRED, RED)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(RED)
c.drawString(sx + 2*mm, sy2 - 4*mm, "RED FLAG DIFFERENTIALS")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
diffs = ["COPD (fixed obstruction,\n smoker, age >40)",
"Vocal cord dysfunction\n (inspiratory stridor)",
"Heart failure\n (bilateral crackles, BNP)",
"Pulmonary embolism\n (pleuritic pain, D-dimer)",
"Bronchiectasis\n (daily productive cough)",
"Endobronchial tumour\n (unilateral wheeze, haemoptysis)"]
for i, d in enumerate(diffs):
lines_ = d.split("\n")
c.setFont("Helvetica-Bold", 7)
c.drawString(sx + 3*mm, sy2 - 12*mm - i*11*mm, f"\u2022 {lines_[0]}")
if len(lines_) > 1:
c.setFont("Helvetica", 7)
c.drawString(sx + 5*mm, sy2 - 16*mm - i*11*mm, lines_[1])
# ── Bottom note ───────────────────────────────────────────────────────────
c.setFont("Helvetica-Oblique", 7.5)
c.setFillColor(NAVY)
c.drawString(12*mm, 12*mm,
"GINA 2026: Diagnosis MUST be confirmed with objective evidence of variable airflow limitation before starting regular treatment.")
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# PAGE 2 – SEVERITY / CONTROL & TREATMENT STEPS
# ═══════════════════════════════════════════════════════════════════════════════
def page2_control_treatment(c):
c.setPageSize(A4)
page_header(c, "PAGE 2 — CONTROL ASSESSMENT & TREATMENT STEPS",
"GINA 2026 Step Approach (Adults & Adolescents)", 2, 4)
page_footer(c)
y = H - 48*mm
# ── Section A: Control Assessment Table ──────────────────────────────────
section_title(c, 12*mm, y, "A. ASSESS ASTHMA CONTROL (ask at every OPD visit)")
y -= 8*mm
col_labels = ["Feature", "Well Controlled", "Partly Controlled", "Uncontrolled"]
col_w = [50*mm, 38*mm, 38*mm, 38*mm]
col_x = [12*mm]
for w_ in col_w[:-1]:
col_x.append(col_x[-1] + w_)
row_h = 9*mm
header_y = y
# Header row
for i, (lbl, cx_, cw_) in enumerate(zip(col_labels, col_x, col_w)):
fc = NAVY if i == 0 else (LTGREEN if i == 1 else (LTAMB if i == 2 else LTRED))
tc = WHITE if i == 0 else (GREEN if i == 1 else (AMBER if i == 2 else RED))
box(c, cx_, header_y - row_h, cw_, row_h, fc, NAVY if i == 0 else tc)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(tc if i == 0 else tc)
c.drawCentredString(cx_ + cw_/2, header_y - row_h + 3*mm, lbl)
# Data rows
rows = [
("Daytime symptoms", "<=2 days/week", ">2 days/week", ">2 days/week"),
("Night waking due to asthma", "None", "Any", "Any"),
("Reliever use (SABA/ICS-form)", "<=2 days/week", ">2 days/week", ">2 days/week"),
("Activity limitation", "None", "Any", "Any"),
("Feature count threshold", "0 features", "1-2 features", "3-4 features"),
]
for ri, row in enumerate(rows):
ry = header_y - (ri + 2) * row_h
for ci, (cell, cx_, cw_) in enumerate(zip(row, col_x, col_w)):
fc = LTGREY if ri % 2 == 0 else WHITE
if ci == 0:
box(c, cx_, ry, cw_, row_h, LTBLUE, BLUE)
c.setFont("Helvetica-Bold", 7.5)
c.setFillColor(NAVY)
else:
fc2 = LTGREEN if ci == 1 else (LTAMB if ci == 2 else LTRED)
box(c, cx_, ry, cw_, row_h, fc2, GREY)
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
c.drawCentredString(cx_ + cw_/2, ry + 3*mm, cell)
y -= (len(rows) + 2) * row_h + 4*mm
# ── Section B: Treatment Steps ────────────────────────────────────────────
section_title(c, 12*mm, y, "B. GINA 2026 TREATMENT STEPS (ICS = inhaled corticosteroid)")
y -= 8*mm
steps = [
("STEP 1", "Mild intermittent", LTBLUE, BLUE, NAVY,
"Track 1: As-needed low-dose ICS-formoterol (preferred)\nTrack 2: As-needed low-dose ICS-SABA [NEW GINA 2026]",
"As-needed ICS-formoterol"),
("STEP 2", "Mild persistent", LTGREEN, GREEN, GREEN,
"Track 1: Low-dose ICS daily + as-needed ICS-formoterol\nTrack 2: Low-dose ICS daily + as-needed SABA",
"As-needed ICS-formoterol"),
("STEP 3", "Moderate", LTAMB, AMBER, AMBER,
"MART: Low-dose ICS-formoterol as both controller AND reliever\n(Maintenance And Reliever Therapy - single inhaler)",
"As-needed ICS-formoterol"),
("STEP 4", "Uncontrolled on Step 3", LTORA, ORANGE, ORANGE,
"MART: Medium-dose ICS-formoterol + Consider add-on LAMA (tiotropium)\nReview diagnosis, adherence, technique before escalating",
"As-needed ICS-formoterol"),
("STEP 5", "Severe / Refractory", LTRED, RED, RED,
"High-dose ICS-LABA + LAMA + REFER TO SPECIALIST\nConsider biologics (see Page 4) | Minimise oral corticosteroids",
"As-needed ICS-formoterol"),
]
sw = (W - 24*mm)
sh = 16*mm
for i, (step, severity, fc, bc, tc, text, reliever) in enumerate(steps):
sy3 = y - i * (sh + 1.5*mm)
box(c, 12*mm, sy3 - sh, sw, sh, fc, bc, radius=5)
# Step badge
box(c, 12*mm, sy3 - sh, 22*mm, sh, bc, bc, radius=5)
c.setFont("Helvetica-Bold", 9)
c.setFillColor(WHITE)
c.drawCentredString(23*mm, sy3 - sh/2 - 1.5*mm, step)
# Severity label
c.setFont("Helvetica-Bold", 8)
c.setFillColor(tc)
c.drawString(36*mm, sy3 - 5*mm, severity)
# Main text
lines_ = text.split("\n")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
for li, line in enumerate(lines_):
c.drawString(36*mm, sy3 - 9.5*mm - li * 4*mm, line)
# Reliever badge
c.setFont("Helvetica-Oblique", 7)
c.setFillColor(GREY)
c.drawRightString(W - 14*mm, sy3 - 5*mm, f"Reliever: {reliever}")
# Step up/down arrows between steps (left margin)
for i in range(len(steps) - 1):
ay = y - i * (sh + 1.5*mm) - sh - 0.75*mm
arrow(c, 7*mm, ay, 7*mm, ay - 1.5*mm + 0.1, color=RED)
y -= len(steps) * (sh + 1.5*mm) + 4*mm
# ── Step up / down guidance ───────────────────────────────────────────────
box(c, 12*mm, y - 12*mm, (W-24*mm)/2 - 2*mm, 12*mm, LTGREEN, GREEN)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(GREEN)
c.drawString(15*mm, y - 5.5*mm, "STEP DOWN if:")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
c.drawString(15*mm, y - 9.5*mm, "Well controlled for >=3 months | Reduce ICS dose by 25-50%")
box(c, 12*mm + (W-24*mm)/2 + 2*mm, y - 12*mm, (W-24*mm)/2 - 2*mm, 12*mm, LTRED, RED)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(RED)
c.drawString(15*mm + (W-24*mm)/2 + 2*mm, y - 5.5*mm, "STEP UP if:")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
c.drawString(15*mm + (W-24*mm)/2 + 2*mm, y - 9.5*mm,
"Uncontrolled for >=1 month (check technique & adherence first!)")
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# PAGE 3 – ACUTE EXACERBATION MANAGEMENT
# ═══════════════════════════════════════════════════════════════════════════════
def page3_acute(c):
c.setPageSize(A4)
page_header(c, "PAGE 3 — ACUTE EXACERBATION MANAGEMENT (OPD / PRIMARY CARE)",
"GINA 2026 Protocol", 3, 4)
page_footer(c)
y = H - 48*mm
# ── Severity table ─────────────────────────────────────────────────────────
section_title(c, 12*mm, y, "STEP 1 — ASSESS SEVERITY IMMEDIATELY")
y -= 8*mm
col_labels2 = ["Feature", "MILD", "MODERATE", "SEVERE", "LIFE-THREATENING"]
col_w2 = [34*mm, 31*mm, 31*mm, 31*mm, 37*mm]
col_x2 = [12*mm]
for w_ in col_w2[:-1]:
col_x2.append(col_x2[-1] + w_)
hdr_cols = [NAVY, GREEN, AMBER, ORANGE, RED]
hdr_txt = [WHITE, WHITE, WHITE, WHITE, WHITE]
for i, (lbl, cx_, cw_, hc, ht) in enumerate(zip(col_labels2, col_x2, col_w2, hdr_cols, hdr_txt)):
box(c, cx_, y - 8*mm, cw_, 8*mm, hc, hc)
c.setFont("Helvetica-Bold", 7.5)
c.setFillColor(ht)
c.drawCentredString(cx_ + cw_/2, y - 5*mm, lbl)
rows2 = [
("Speech", "Full sentences", "Phrases only", "Words only", "Mute / confused"),
("SpO2", ">=95%", "92-94%", "<92%", "<90% / cyanosis"),
("Resp Rate", "Normal", "20-30/min", ">30/min", "Bradypnoea / gasping"),
("PEF (% predicted)", ">70%", "40-69%", "<40%", "<25% / not measurable"),
("Wheeze", "Moderate expiratory", "Loud", "Loud", "SILENT CHEST (danger!)"),
("Heart Rate", "<100 bpm", "100-120 bpm", ">120 bpm", "Bradycardia"),
]
row_h2 = 8*mm
for ri, row in enumerate(rows2):
ry = y - (ri + 2) * row_h2
for ci, (cell, cx_, cw_) in enumerate(zip(row, col_x2, col_w2)):
fc = [LTBLUE, LTGREEN, LTAMB, LTORA, LTRED][ci]
box(c, cx_, ry, cw_, row_h2, fc, GREY)
c.setFont("Helvetica-Bold" if ci == 0 else "Helvetica", 7)
c.setFillColor(NAVY if ci == 0 else colors.black)
c.drawCentredString(cx_ + cw_/2, ry + 2.5*mm, cell)
y -= (len(rows2) + 2) * row_h2 + 5*mm
# ── 3-column treatment boxes ───────────────────────────────────────────────
section_title(c, 12*mm, y, "STEP 2 — IMMEDIATE TREATMENT")
y -= 7*mm
col3_w = (W - 26*mm) / 3
col3_h = 52*mm
col3_data = [
(GREEN, "MILD\nExacerbation",
[
"1. ICS-formoterol 1-2 puffs PRN",
" OR salbutamol 4-10 puffs",
" via spacer q20min x3",
"",
"2. Shake MDI before each puff!",
" [GINA 2026 safety note]",
"",
"3. Monitor SpO2",
"",
"4. If no improvement in 1 hour",
" --> REFER to ED",
]),
(AMBER, "MODERATE\nExacerbation",
[
"1. Salbutamol 4-10 puffs via spacer",
" OR nebuliser q20min x3",
"",
"2. Ipratropium 4-8 puffs q20min",
" (add-on bronchodilation)",
"",
"3. O2 ONLY if SpO2 <92%",
" Target: 92-95% (NOT >95%)",
"",
"4. Prednisolone 40-50mg PO",
"",
"5. Reassess every 20 min",
" Transfer if no improvement",
]),
(RED, "SEVERE / LIFE-THREATENING",
[
"1. CALL FOR HELP / transfer NOW",
"",
"2. ANAPHYLAXIS present?",
" --> Epinephrine 0.5mg IM FIRST",
" THEN bronchodilator [GINA 2026]",
"",
"3. Continuous nebulised salbutamol",
" + ipratropium",
"",
"4. IV methylprednisolone 1mg/kg",
"",
"5. O2 to keep SpO2 92-95%",
"",
"6. IV magnesium sulphate 2g over",
" 20 min (if no response)",
"",
"7. Prepare for ICU",
]),
]
for i, (col, title, items_) in enumerate(col3_data):
cx_ = 12*mm + i * (col3_w + 1*mm)
box(c, cx_, y - col3_h, col3_w, col3_h, LTRED if col == RED else (LTAMB if col == AMBER else LTGREEN), col, radius=5)
c.setFont("Helvetica-Bold", 9)
c.setFillColor(col)
for ti, tl in enumerate(title.split("\n")):
c.drawCentredString(cx_ + col3_w/2, y - 7*mm - ti*5*mm, tl)
c.setFont("Helvetica", 7)
c.setFillColor(colors.black)
item_y = y - 17*mm
for item in items_:
c.drawString(cx_ + 3*mm, item_y, item)
item_y -= 3.5*mm
y -= col3_h + 5*mm
# ── Discharge / follow-up ──────────────────────────────────────────────────
section_title(c, 12*mm, y, "STEP 3 — DISCHARGE & FOLLOW-UP (GINA 2026)")
y -= 7*mm
box(c, 12*mm, y - 24*mm, W - 24*mm, 24*mm, LTBLUE, BLUE)
items_d = [
"Before discharge: review and correct inhaler technique | Provide written Asthma Action Plan",
"Prescribe: 5-7 days prednisolone 40-50mg/day (complete course) | Step up controller therapy",
"Arrange OPD follow-up within 2-7 days (high risk) or 1-3 months (stable)",
"Red flag education: SpO2 <92%, can't speak full sentences, silent chest = call emergency immediately",
]
for i, item in enumerate(items_d):
c.setFont("Helvetica", 7.5)
c.setFillColor(NAVY)
c.drawString(15*mm, y - 7*mm - i*4.5*mm, f"\u2022 {item}")
c.showPage()
# ═══════════════════════════════════════════════════════════════════════════════
# PAGE 4 – SPECIAL SITUATIONS + BIOLOGICS + OPD CHECKLIST
# ═══════════════════════════════════════════════════════════════════════════════
def page4_special(c):
c.setPageSize(A4)
page_header(c, "PAGE 4 — BIOLOGICS, SPECIAL SITUATIONS & OPD CHECKLIST",
"GINA 2026 Severe Asthma Guide + Pregnancy + Referral Criteria", 4, 4)
page_footer(c)
y = H - 48*mm
# ── A: Biologics ───────────────────────────────────────────────────────────
section_title(c, 12*mm, y, "A. BIOLOGIC THERAPY (Step 5 — Severe Asthma)")
y -= 7*mm
bios = [
(BLUE, "Omalizumab", "Anti-IgE", "Allergic asthma, high serum IgE, sensitized allergens. IgE 30-1500 IU/mL."),
(GREEN, "Mepolizumab", "Anti-IL-5", "Eosinophilic asthma. Blood eos >=150 cells/uL (>=300 for step-up)."),
(AMBER, "Benralizumab", "Anti-IL-5Ra", "Eosinophilic asthma. Blood eos >=300 cells/uL. Dosing q8wk after loading."),
(PURPLE, "Dupilumab", "Anti-IL-4Ra", "Type 2 asthma + comorbid CRS/nasal polyps/atopic dermatitis."),
(ORANGE, "Tezepelumab", "Anti-TSLP", "Broadest biologic - works in type 2 AND non-type 2. Eos >=300 for best response."),
(RED, "Depemokimab", "Anti-IL-5 (LA)", "NEW GINA 2026. Ultra-long acting anti-IL-5. Dosing every 6 MONTHS."),
]
bh = 9*mm
bw = W - 24*mm
for i, (col, name, mech, desc) in enumerate(bios):
by = y - i * (bh + 1*mm)
box(c, 12*mm, by - bh, bw, bh, LTGREY, col, radius=4)
box(c, 12*mm, by - bh, 32*mm, bh, col, col, radius=4)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(WHITE)
c.drawString(14*mm, by - bh/2 - 1.5*mm, name)
c.setFont("Helvetica-Bold", 7.5)
c.setFillColor(col)
c.drawString(46*mm, by - bh/2 - 1.5*mm, mech)
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
c.drawString(78*mm, by - bh/2 - 1.5*mm, desc)
y -= len(bios) * (bh + 1*mm) + 6*mm
# ── B: Special Situations ─────────────────────────────────────────────────
half_w = (W - 26*mm) / 2
section_title(c, 12*mm, y, "B. SPECIAL SITUATIONS")
y -= 7*mm
# Pregnancy box
box(c, 12*mm, y - 36*mm, half_w, 36*mm, LTPUR, PURPLE)
c.setFont("Helvetica-Bold", 9)
c.setFillColor(PURPLE)
c.drawString(14*mm, y - 5*mm, "ASTHMA IN PREGNANCY")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
preg = [
"Affects 4-8% of pregnancies",
"Poorly controlled asthma risks:",
" preeclampsia, preterm birth,",
" low birth weight, IUGR, perinatal death",
"ICS: SAFE - budesonide preferred",
"SABA: safe; LABA: use with ICS only",
"OCS: benefit outweighs risk when needed",
"Monitor: serial spirometry, home PEF",
"KEY: risks of poor control >> drug risks",
]
for i, p in enumerate(preg):
c.drawString(14*mm, y - 11*mm - i*3.2*mm, p)
# Occupational box
box(c, 12*mm + half_w + 2*mm, y - 36*mm, half_w, 36*mm, LTAMB, AMBER)
c.setFont("Helvetica-Bold", 9)
c.setFillColor(AMBER)
c.drawString(14*mm + half_w + 2*mm, y - 5*mm, "OCCUPATIONAL ASTHMA")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
occ = [
"Suspect if: adult onset, symptoms",
"improve on weekends/holidays,",
"specific occupational exposure",
"(baker, painter, nurse, farmer)",
"",
"Workup: serial PEF at work vs rest",
"Methacholine challenge",
"",
"Treatment:",
"REMOVE FROM EXPOSURE (1st step!)",
"ATS: reassess >=2 years post-removal",
]
for i, o in enumerate(occ):
c.drawString(14*mm + half_w + 4*mm, y - 11*mm - i*3.2*mm, o)
y -= 36*mm + 5*mm
# ── C: OPD Checklist ──────────────────────────────────────────────────────
section_title(c, 12*mm, y, "C. OPD VISIT CHECKLIST (Every Asthma Appointment)")
y -= 7*mm
chk_w = (W - 26*mm) / 2
chk_h = 44*mm
box(c, 12*mm, y - chk_h, chk_w, chk_h, LTBLUE, BLUE)
c.setFont("Helvetica-Bold", 8.5)
c.setFillColor(NAVY)
c.drawString(14*mm, y - 5*mm, "FIRST VISIT (New Patient)")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
first = [
"[ ] Symptom history (all 4 features)",
"[ ] Trigger history (allergens, exercise, ASA)",
"[ ] Atopic & family history",
"[ ] Prior hospitalizations / ICU admissions",
"[ ] Current medications (beta-blockers?)",
"[ ] Spirometry + bronchodilator test",
"[ ] Chest X-ray",
"[ ] CBC, IgE, FeNO",
"[ ] Start GINA step treatment",
"[ ] Teach inhaler technique (DEMONSTRATE!)",
"[ ] Provide written Asthma Action Plan",
]
for i, f in enumerate(first):
c.drawString(14*mm, y - 11*mm - i*3.1*mm, f)
box(c, 12*mm + chk_w + 2*mm, y - chk_h, chk_w, chk_h, LTGREEN, GREEN)
c.setFont("Helvetica-Bold", 8.5)
c.setFillColor(GREEN)
c.drawString(14*mm + chk_w + 2*mm, y - 5*mm, "FOLLOW-UP VISIT")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
fu = [
"[ ] Reliever use this week? (>2x = uncontrolled)",
"[ ] Night waking?",
"[ ] Activity limitation?",
"[ ] Any exacerbations / ED visits / OCS?",
"[ ] Missed doses?",
"[ ] WATCH INHALER TECHNIQUE",
"[ ] Check SpO2",
"[ ] Assess control (GINA table)",
"[ ] Step up or down?",
"[ ] Screen comorbidities (rhinitis, GERD, OSA)",
"[ ] Referral if step 3-4 uncontrolled",
]
for i, f in enumerate(fu):
c.drawString(14*mm + chk_w + 4*mm, y - 11*mm - i*3.1*mm, f)
y -= chk_h + 5*mm
# ── D: Referral criteria ──────────────────────────────────────────────────
box(c, 12*mm, y - 16*mm, W - 24*mm, 16*mm, LTRED, RED)
c.setFont("Helvetica-Bold", 9)
c.setFillColor(RED)
c.drawString(14*mm, y - 5.5*mm, "REFER TO PULMONOLOGIST / SPECIALIST:")
c.setFont("Helvetica", 7.5)
c.setFillColor(colors.black)
refs = ("Diagnosis uncertain | Poor control despite Step 3-4 | Suspected severe/difficult asthma | "
"Biologics candidate | Occupational asthma | >=2 severe exacerbations/year | "
"Prior near-fatal attack (ICU/intubation)")
c.drawString(14*mm, y - 11*mm, refs)
c.showPage()
# ── MAIN ──────────────────────────────────────────────────────────────────────
OUTPUT = "/home/daytona/workspace/asthma-flowchart/OPD_Asthma_Flowchart_GINA2026.pdf"
c = canvas.Canvas(OUTPUT, pagesize=A4)
c.setTitle("OPD Asthma Clinical Decision Flowchart - GINA 2026")
c.setAuthor("Clinical Decision Support - Murray & Nadel / GINA 2026")
c.setSubject("Asthma Diagnosis & Management")
page1_diagnosis(c)
page2_control_treatment(c)
page3_acute(c)
page4_special(c)
c.save()
print(f"PDF saved to: {OUTPUT}")
cd /home/daytona/workspace/asthma-flowchart && python generate_flowchart.py 2>&1
~/asthma-flowchart/generate_flowchart.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/asthma-flowchart/generate_flowchart.py'
~/asthma-flowchart/generate_flowchart.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/asthma-flowchart/generate_flowchart.py'
cd /home/daytona/workspace/asthma-flowchart && python generate_flowchart.py 2>&1
ls -lh /home/daytona/workspace/asthma-flowchart/OPD_Asthma_Flowchart_GINA2026.pdf && python3 -c "from pypdf import PdfReader; r=PdfReader('/home/daytona/workspace/asthma-flowchart/OPD_Asthma_Flowchart_GINA2026.pdf'); print(f'Pages: {len(r.pages)}')"
OPD Asthma Flowchart GINA2026
PDF Document · PDF
Can you explain classsification and phenotypes of asthma and meaning of mild intermittent persistent intermittent partially controlled
GINA 2026 asthma classification severity intermittent persistent mild moderate severe phenotypes
https://ginasthma.org/wp-content/uploads/2026/05/GINA-2026-S…
| System | Used By | Classifies Based On | Terms Used |
|---|---|---|---|
| NAEPP/EPR-3 (USA, older) | NHLBI, most textbooks | Symptoms BEFORE treatment | Intermittent, Mild Persistent, Moderate Persistent, Severe Persistent |
| GINA (global, modern) | GINA 2026, most current guidelines | Control WHILE on treatment | Well controlled, Partly controlled, Uncontrolled + Severity by step |
GINA 2026 says: The old intermittent/persistent classification was based on an arbitrary, untested assumption that patients with symptoms ≤2 days/week would not benefit from ICS. This is now known to be wrong - even "intermittent" asthma patients can have severe or fatal exacerbations. GINA now strongly prefers the control-based framework.
| Parameter | Criteria |
|---|---|
| Daytime symptoms | ≤2 days per week |
| Night waking | ≤2 nights per month |
| SABA use for symptoms | ≤2 days per week |
| Activity limitation | None |
| FEV1 / PEF | ≥80% predicted |
| PEF variability | <20% |
| Parameter | Criteria |
|---|---|
| Daytime symptoms | >2 days/week but NOT daily |
| Night waking | 3-4 nights/month |
| SABA use | >2 days/week but not daily |
| Activity limitation | Minor |
| FEV1 / PEF | ≥80% predicted |
| PEF variability | 20-30% |
| Parameter | Criteria |
|---|---|
| Daytime symptoms | Daily |
| Night waking | >1 night/week but not nightly |
| SABA use | Daily |
| Activity limitation | Some limitation |
| FEV1 / PEF | 60-79% predicted |
| PEF variability | >30% |
| Parameter | Criteria |
|---|---|
| Daytime symptoms | Continuous / throughout day |
| Night waking | Frequently (nightly) |
| SABA use | Several times daily |
| Activity limitation | Extremely limited |
| FEV1 / PEF | <60% predicted |
| PEF variability | >30% |
| Feature | Criterion |
|---|---|
| Daytime symptoms | ≤2 days/week |
| Night waking | None |
| Reliever use | ≤2 days/week |
| Activity limitation | None |
| Severity | What it means |
|---|---|
| Mild | Well controlled on GINA Step 1-2 |
| Moderate | Well controlled on GINA Step 3 |
| Severe | Requires Step 4-5 to stay controlled, OR uncontrolled despite Step 4-5 |
"Asthma is a heterogeneous disease, with various underlying disease processes. Recognizable clusters of demographic, clinical and/or pathophysiological characteristics are often called asthma phenotypes."
- GINA 2026
OLD NAEPP (before treatment) GINA (on treatment)
───────────────────────────────── ──────────────────────────────────
Mild Intermittent ──────────────> Controlled on Step 1 = Mild asthma
Mild Persistent ──────────────> Controlled on Step 2 = Mild asthma
Moderate Persistent ──────────────> Controlled on Step 3 = Moderate asthma
Severe Persistent ──────────────> Needs Step 4-5 to control = Severe asthma
Uncontrolled on Step 4-5 = Severe refractory
A patient comes in and says "my doctor told me I have mild asthma." You see them on salbutamol only. You think: mild = fine = no ICS needed.
| Term | System | Simple Meaning | Key Numbers |
|---|---|---|---|
| Mild Intermittent | NAEPP | Occasional, rare symptoms | Daytime ≤2/wk, Night ≤2/month, FEV1 ≥80% |
| Mild Persistent | NAEPP | More than twice a week, not daily | Daytime >2/wk, Night 3-4/month, FEV1 ≥80% |
| Moderate Persistent | NAEPP | Daily symptoms, some limitation | Daily symptoms, Night >1/wk, FEV1 60-79% |
| Severe Persistent | NAEPP | Constant symptoms, severe limitation | Continuous, Nightly, FEV1 <60% |
| Well Controlled | GINA | Current treatment is working | 0 of 4 features |
| Partly Controlled | GINA | Treatment partially working | 1-2 of 4 features |
| Uncontrolled | GINA | Treatment not working | 3-4 of 4 features |
| Mild asthma (GINA) | GINA severity | Controlled on Steps 1-2 | Needs low-dose ICS only |
| Severe asthma (GINA) | GINA severity | Needs Step 4-5 to control | High-dose ICS+LABA ± add-ons |
Dose of ics formetrol
GINA 2026 ICS formoterol dose budesonide formoterol MART AIR low medium high dose table mcg
Create a detailed ICS-formoterol dosing reference table in PDF