What a pharmacotherapeutics on asthama nd it's treatment algorithm with reference to standard books and research papers
asthma pharmacotherapy treatment guidelines
asthma management GINA guidelines inhaled corticosteroids
asthma stepwise therapy bronchodilator
| Component | Intermittent | Mild Persistent | Moderate Persistent | Severe Persistent |
|---|---|---|---|---|
| Daytime symptoms | ≤2 days/week | >2 days/wk, not daily | Daily | Throughout the day |
| Nighttime awakenings | ≤2×/month | 3–4×/month | >1×/week but not nightly | Often 7×/week |
| SABA use (relief) | ≤2 days/week | >2 days/wk, not daily | Daily | Several times/day |
| Activity limitation | None | Minor | Some | Extremely limited |
| FEV₁ | >80% predicted | >80% predicted | >60–<80% | <60% |
| FEV₁/FVC | Normal | Normal | Reduced 5% | Reduced >5% |
| OCS exacerbations | 0–1/year | ≥2/year | — | — |
| Drug | Low Dose | Medium Dose | High Dose |
|---|---|---|---|
| Beclomethasone HFA | 80–240 μg/day | >240–480 μg/day | >480 μg/day |
| Budesonide DPI | 180–600 μg/day | >600–1200 μg/day | >1200 μg/day |
| Fluticasone HFA/MDI | 88–264 μg/day | >264–440 μg/day | >440 μg/day |
| Fluticasone DPI | 100–300 μg/day | >300–500 μg/day | >500 μg/day |
| Mometasone DPI | 200 μg/day | 400 μg/day | >400 μg/day |
| Step | Preferred | Alternative |
|---|---|---|
| 1 | PRN SABA; at start of RTI: short-course daily ICS | — |
| 2 | Daily low-dose ICS + PRN SABA | Daily montelukast* or cromolyn* + PRN SABA |
| 3 | Daily medium-dose ICS + PRN SABA | — |
| 4 | Daily medium-dose ICS-LABA + PRN SABA | Daily medium-dose ICS + montelukast* + PRN SABA |
| 5 | Daily high-dose ICS-LABA + PRN SABA | Daily high-dose ICS + montelukast* + PRN SABA |
| 6 | Daily high-dose ICS-LABA + oral systemic CS + PRN SABA | — |


| Step | Preferred | Alternative |
|---|---|---|
| 1 (Intermittent) | PRN SABA | PRN concomitant ICS+SABA |
| 2 (Mild Persistent) | Daily low-dose ICS + PRN SABA | Daily LTRA, Cromolyn*, Nedocromil*, Zileuton*, or Theophylline* + PRN SABA |
| 3 (Moderate) | Daily + PRN low-dose ICS-formoterol | Daily medium-dose ICS; OR daily low-dose ICS-LABA; OR daily low-dose ICS+LAMA; OR daily low-dose ICS+LTRA* or ICS+Theophylline*; + PRN SABA |
| 4 (Moderate–Severe) | Daily + PRN medium-dose ICS-formoterol | Daily medium-dose ICS-LABA or medium-dose ICS+LAMA; OR medium-dose ICS+LTRA*; OR medium-dose ICS+Theophylline*; OR medium-dose ICS+Zileuton*; + PRN SABA |
| 5 (Severe) | Daily medium–high dose ICS-LABA + LAMA + PRN SABA | Daily medium–high dose ICS-LABA or high-dose ICS+LTRA*; + PRN SABA; Consider biologics |
| 6 (Very Severe) | Daily high-dose ICS-LABA + oral systemic CS + PRN SABA | — |
| Study | Key Finding |
|---|---|
| Murphy KR & Solis J, J Fam Pract 2021 (PMID 34432620) | NAEPP 2020 focused updates — key changes for primary care including SMART therapy endorsement and LAMA addition |
| Laitano R et al., Expert Opin Pharmacother 2024 (PMID 38864834) | Umbrella review: ICS/LABA/LAMA triple therapy significantly reduces exacerbation risk in asthma |
| Zhou XJ et al., Chin Med J 2021 (PMID 34784306) | Salmeterol/fluticasone superior to montelukast ± fluticasone in children and adolescents |
| Sin DD et al. (cited in Family Medicine 9e) | ICS reduced exacerbations 55% vs. placebo/SABA; LABAs reduced exacerbations 26%; ICS > LTRA as first-line monotherapy |
| Drug Class | Rank | Evidence Base |
|---|---|---|
| ICS | First-line controller | Level A; reduces exacerbations 55%; superior to LTRA |
| ICS-LABA | Preferred step-up | Level A; SMART therapy endorsed NAEPP 2020 |
| LAMA (tiotropium) | Add-on step 3–5 | Level A for triple therapy in NAEPP 2020 |
| LTRA (montelukast) | Alternative | Level A; inferior to ICS; FDA boxed warning |
| Anti-IgE / biologics | Steps 5–6 | Level A for specific endotypes |
| Theophylline | Alternative add-on | Level A but narrow window; not preferred |
| Immunotherapy | Adjunct steps 2–4 | Conditional recommendation; comparable to ICS |
Treatment algorithm in flow chart
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace not found"
mkdir -p /home/daytona/workspace/asthma-flowchart && echo "done"
~/asthma-flowchart/asthma_flowchart.py
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
import matplotlib.patheffects as pe
import numpy as np
fig = plt.figure(figsize=(28, 48))
ax = fig.add_axes([0, 0, 1, 1])
ax.set_xlim(0, 28)
ax.set_ylim(0, 48)
ax.axis('off')
fig.patch.set_facecolor('#F8F9FA')
# ── colour palette ──────────────────────────────────────────────────────────
C_TITLE = '#1A237E' # deep navy
C_DIAG = '#283593' # dark blue – diagnosis / assessment boxes
C_SEVER = '#1565C0' # mid blue – severity classification
C_STEP1 = '#2E7D32' # dark green – step 1
C_STEP2 = '#388E3C'
C_STEP3 = '#F57F17' # amber – step 3
C_STEP4 = '#E65100' # deep orange– step 4
C_STEP5 = '#B71C1C' # dark red – step 5/6
C_ACUTE = '#880E4F' # deep purple– acute exacerbation
C_CTRL = '#00695C' # teal – control assessment
C_ARROW = '#37474F'
C_WHITE = 'white'
C_LIGHT = '#E3F2FD'
C_WARN = '#FFF9C4'
C_BIO = '#4A148C' # violet – biologics
def box(ax, x, y, w, h, text, facecolor, textcolor='white', fontsize=9,
bold=False, radius=0.3, alpha=1.0, style='round,pad=0.1',
ha='center', va='center', wrap=True, border=None):
bp = FancyBboxPatch((x - w/2, y - h/2), w, h,
boxstyle=f"round,pad=0.15",
linewidth=1.4,
edgecolor=border if border else facecolor,
facecolor=facecolor, alpha=alpha, zorder=3)
ax.add_patch(bp)
weight = 'bold' if bold else 'normal'
ax.text(x, y, text, ha=ha, va=va, fontsize=fontsize,
color=textcolor, fontweight=weight,
wrap=True, zorder=4,
multialignment='center',
linespacing=1.35)
def diamond(ax, x, y, w, h, text, facecolor, textcolor='white', fontsize=8.5):
dx, dy = w/2, h/2
xs = [x, x+dx, x, x-dx, x]
ys = [y+dy, y, y-dy, y, y+dy]
poly = plt.Polygon(list(zip(xs, ys)), closed=True,
facecolor=facecolor, edgecolor=facecolor,
linewidth=1.4, zorder=3)
ax.add_patch(poly)
ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
color=textcolor, fontweight='bold', zorder=4, multialignment='center')
def arrow(ax, x1, y1, x2, y2, label='', color=C_ARROW, lw=2.0):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color,
lw=lw, connectionstyle='arc3,rad=0.0'),
zorder=5)
if label:
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx+0.15, my, label, fontsize=7.5, color=color, va='center')
def harrow(ax, x1, y1, x2, y2, label='', color=C_ARROW, lw=1.8):
"""Horizontal or bent arrow."""
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color, lw=lw,
connectionstyle='angle,angleA=0,angleB=90'),
zorder=5)
if label:
ax.text((x1+x2)/2, (y1+y2)/2+0.15, label, fontsize=7.5,
color=color, ha='center')
# ════════════════════════════════════════════════════════════════════════════
# TITLE
# ════════════════════════════════════════════════════════════════════════════
ax.add_patch(FancyBboxPatch((0.3, 46.2), 27.4, 1.5,
boxstyle="round,pad=0.15", facecolor=C_TITLE,
edgecolor=C_TITLE, linewidth=2, zorder=3))
ax.text(14, 47.15, 'ASTHMA PHARMACOTHERAPY — TREATMENT ALGORITHM',
ha='center', va='center', fontsize=16, color='white',
fontweight='bold', zorder=4)
ax.text(14, 46.65,
'Based on NAEPP 2020 Focused Updates | NAEPP EPR-3 | Harriet Lane Handbook 23rd ed. | Textbook of Family Medicine 9th ed.',
ha='center', va='center', fontsize=8.5, color='#BBDEFB', zorder=4)
# ════════════════════════════════════════════════════════════════════════════
# BLOCK 1 — PATIENT PRESENTATION
# ════════════════════════════════════════════════════════════════════════════
box(ax, 14, 45.3, 9, 0.75,
'PATIENT PRESENTS WITH SUSPECTED ASTHMA',
C_DIAG, fontsize=10, bold=True)
arrow(ax, 14, 44.92, 14, 44.3)
# ════════════════════════════════════════════════════════════════════════════
# BLOCK 2 — DIAGNOSIS
# ════════════════════════════════════════════════════════════════════════════
box(ax, 14, 43.85, 18, 0.8,
'DIAGNOSIS\n'
'History (wheeze, cough, dyspnoea, chest tightness) + Spirometry (FEV₁/FVC <0.70, ≥12% reversibility)\n'
'R/O: COPD, CHF, foreign body, GERD, vocal cord dysfunction',
C_DIAG, fontsize=8.2, bold=False)
arrow(ax, 14, 43.45, 14, 42.85)
# ════════════════════════════════════════════════════════════════════════════
# BLOCK 3 — SEVERITY CLASSIFICATION (diamond + table)
# ════════════════════════════════════════════════════════════════════════════
box(ax, 14, 42.55, 18, 0.7,
'CLASSIFY SEVERITY (before starting treatment) — by Impairment & Risk',
C_SEVER, fontsize=9, bold=True)
# four severity boxes side by side
sev_data = [
('INTERMITTENT\n'
'≤2 days/wk symptoms\n'
'Nighttime ≤2×/month\n'
'SABA ≤2 d/wk\n'
'FEV₁ >80% pred\n'
'→ Start: Step 1', C_STEP1, 4.0),
('MILD PERSISTENT\n'
'>2 d/wk but not daily\n'
'Nighttime 3–4×/month\n'
'Minor activity limit\n'
'FEV₁ >80% pred\n'
'→ Start: Step 2', C_STEP2, 10.0),
('MODERATE PERSISTENT\n'
'Daily symptoms\n'
'Nighttime >1×/wk\n'
'Some activity limit\n'
'FEV₁ 60–80% pred\n'
'→ Start: Step 3', C_STEP3, '#5D4037', 16.0),
('SEVERE PERSISTENT\n'
'Continuous symptoms\n'
'Nighttime often 7×/wk\n'
'Extreme limitation\n'
'FEV₁ <60% pred\n'
'→ Start: Step 4–5', C_STEP5, 22.0),
]
# 3-element tuples for first three, 2-element for last → fix
sev_fixed = [
('INTERMITTENT\n≤2 days/wk symptoms\nNighttime ≤2×/month\nSABA ≤2 d/wk\nFEV₁ >80% pred\n→ Start: Step 1',
C_STEP1, 'white', 4.0),
('MILD PERSISTENT\n>2 d/wk but not daily\nNighttime 3–4×/month\nMinor activity limit\nFEV₁ >80% pred\n→ Start: Step 2',
C_STEP2, 'white', 10.0),
('MODERATE PERSISTENT\nDaily symptoms\nNighttime >1×/wk\nSome activity limit\nFEV₁ 60–80% pred\n→ Start: Step 3',
C_STEP3, '#1A0000', 16.0),
('SEVERE PERSISTENT\nContinuous symptoms\nNighttime often 7×/wk\nExtreme limitation\nFEV₁ <60% pred\n→ Start: Step 4–5',
C_STEP5, 'white', 22.0),
]
for txt, fc, tc, xpos in sev_fixed:
box(ax, xpos, 41.3, 5.2, 1.8, txt, fc, textcolor=tc, fontsize=7.6)
# arrows from severity bar to boxes
for xpos in [4.0, 10.0, 16.0, 22.0]:
arrow(ax, xpos, 42.2, xpos, 42.2, color=C_ARROW, lw=0)
ax.annotate('', xy=(xpos, 42.22), xytext=(14, 42.2),
arrowprops=dict(arrowstyle='->', color=C_ARROW, lw=1.6,
connectionstyle='arc3,rad=0.0'), zorder=5)
# ── central down arrow from severity boxes to stepwise ───────────────────
for xpos in [4.0, 10.0, 16.0, 22.0]:
arrow(ax, xpos, 40.4, xpos, 39.9, color=C_ARROW)
# converge
ax.annotate('', xy=(14, 39.55), xytext=(14, 39.9),
arrowprops=dict(arrowstyle='->', color=C_ARROW, lw=2),
zorder=5)
ax.plot([4.0, 22.0], [39.9, 39.9], color=C_ARROW, lw=1.8, zorder=5)
ax.plot([4.0, 4.0], [40.4, 39.9], color=C_ARROW, lw=1.8, zorder=5)
ax.plot([22.0, 22.0],[40.4, 39.9], color=C_ARROW, lw=1.8, zorder=5)
# ════════════════════════════════════════════════════════════════════════════
# STEPWISE SECTION HEADER
# ════════════════════════════════════════════════════════════════════════════
box(ax, 14, 39.25, 20, 0.6,
'STEPWISE PHARMACOTHERAPY (Ages ≥12 years — Adults & Adolescents)',
C_TITLE, fontsize=10, bold=True)
# ════════════════════════════════════════════════════════════════════════════
# STEPS 1–6
# ════════════════════════════════════════════════════════════════════════════
steps = [
# (step_label, preferred_text, alternative_text, y_centre, pref_color, alt_color)
(
'STEP 1\nIntermittent',
'● PRN SABA (albuterol)\n'
'● PRN concomitant ICS + SABA▲\n'
' (new NAEPP 2020 option)',
'— No daily controller needed —',
38.1, C_STEP1, '#A5D6A7'
),
(
'STEP 2\nMild Persistent',
'● Daily LOW-DOSE ICS + PRN SABA\n'
' (e.g. fluticasone 88–264 μg/d)',
'● Daily LTRA* (montelukast⚠) + PRN SABA\n'
'● Cromolyn* / Nedocromil* / Theophylline* + PRN SABA\n'
' [*Limited availability; ⚠ FDA Black Box Warning]',
36.3, C_STEP2, '#C8E6C9'
),
(
'STEP 3\nModerate',
'● Daily + PRN LOW-DOSE ICS–formoterol▲\n'
' (SMART regimen; max 12 puffs/day)',
'● Daily MEDIUM-DOSE ICS + PRN SABA\n'
'● Daily low-dose ICS–LABA + PRN SABA\n'
'● Daily low-dose ICS + LAMA▲ + PRN SABA\n'
'● Daily low-dose ICS + LTRA* or Theophylline* + PRN SABA',
34.2, C_STEP3, '#FFF9C4'
),
(
'STEP 4\nModerate–Severe',
'● Daily + PRN MEDIUM-DOSE ICS–formoterol▲\n'
' (SMART; max 12 puffs/day)',
'● Daily medium-dose ICS–LABA + PRN SABA\n'
'● Daily medium-dose ICS + LAMA▲ + PRN SABA\n'
'● Daily medium-dose ICS + LTRA* + PRN SABA\n'
'● Daily medium-dose ICS + Zileuton* or Theophylline* + PRN SABA',
31.9, C_STEP4, '#FFE0B2'
),
(
'STEP 5\nSevere Persistent',
'● Daily MEDIUM–HIGH dose ICS–LABA\n'
' + LAMA (triple therapy) + PRN SABA▲\n'
' Consider BIOLOGICS:',
'● Daily medium–high dose ICS–LABA\n'
'● Daily HIGH-DOSE ICS + LTRA* + PRN SABA\n'
'Biologics: anti-IgE (Omalizumab) | anti-IL5 (Mepolizumab,\n'
'Reslizumab) | anti-IL5Rα (Benralizumab) | anti-IL4/13 (Dupilumab)',
29.4, C_STEP5, '#FFCDD2'
),
(
'STEP 6\nVery Severe',
'● Daily HIGH-DOSE ICS–LABA\n'
' + Oral systemic corticosteroid (prednisone)\n'
' + PRN SABA\n'
' Continue biologics if applicable',
'● High-dose ICS + LTRA* + oral CS + PRN SABA\n'
'● High-dose ICS + Theophylline* + oral CS + PRN SABA\n'
' Refer to Pulmonologist / Allergist',
27.0, '#7B1FA2', '#E1BEE7'
),
]
for step_lbl, pref, alt, yc, pc, ac in steps:
box_h = 1.55
# step label (left)
box(ax, 2.0, yc, 2.8, box_h, step_lbl, pc, fontsize=8.5, bold=True)
# preferred (centre)
box(ax, 10.5, yc, 10.5, box_h, '✔ PREFERRED\n' + pref,
C_LIGHT, textcolor='#0D47A1', fontsize=7.8,
border=pc)
# alternative (right)
box(ax, 22.5, yc, 9.5, box_h, '◈ ALTERNATIVE\n' + alt,
C_WARN, textcolor='#4E342E', fontsize=7.4,
border='#FFA000')
# connect step→preferred, step→alternative
arrow(ax, 3.4, yc, 5.25, yc, color=pc, lw=1.8)
arrow(ax, 15.75, yc, 17.75, yc, color='#78909C', lw=1.4)
# down arrows between steps
step_ycs = [s[3] for s in steps]
for i in range(len(step_ycs)-1):
y_top = step_ycs[i] - 0.78
y_bot = step_ycs[i+1] + 0.78
arrow(ax, 2.0, y_top, 2.0, y_bot, color='#263238', lw=2.2)
ax.text(2.75, (y_top+y_bot)/2, 'Step\nUp', fontsize=7,
color='#263238', ha='left', va='center', fontweight='bold')
# up arrows (step down)
for i in range(1, len(step_ycs)):
y_bot = step_ycs[i] + 0.78
y_top = step_ycs[i-1] - 0.78
ax.annotate('', xy=(1.1, y_top), xytext=(1.1, y_bot),
arrowprops=dict(arrowstyle='->', color='#2E7D32',
lw=1.5), zorder=5)
ax.text(0.55, (step_ycs[0]+step_ycs[-1])/2, 'Step\nDown\n(if ≥3 mo\ncontrolled)',
fontsize=6.8, color='#2E7D32', ha='center', va='center',
fontweight='bold', rotation=90)
# ════════════════════════════════════════════════════════════════════════════
# CONTROL ASSESSMENT BOX (between steps and acute)
# ════════════════════════════════════════════════════════════════════════════
arrow(ax, 14, 26.22, 14, 25.55)
box(ax, 14, 25.1, 22, 0.85,
'ASSESS CONTROL AT EVERY VISIT ▸ Check: adherence | inhaler technique | environmental triggers | comorbidities (rhinitis, GERD, obesity, OSA)\n'
'Step UP if needed (reassess in 2–6 weeks) | Step DOWN if well-controlled ≥3 consecutive months | Consult specialist if Step 4+ required',
C_CTRL, fontsize=8, bold=False)
# ════════════════════════════════════════════════════════════════════════════
# QUICK-RELIEF / RESCUE (below stepwise)
# ════════════════════════════════════════════════════════════════════════════
arrow(ax, 14, 24.67, 14, 24.05)
box(ax, 14, 23.65, 22, 0.75,
'QUICK-RELIEF (all steps): SABA PRN ▸ Up to 3 treatments q20 min for acute symptoms\n'
'SABA use >2 days/week for relief (not EIB prevention) = inadequate control → Step Up',
'#1565C0', fontsize=8.2)
# ════════════════════════════════════════════════════════════════════════════
# ACUTE EXACERBATION PANEL
# ════════════════════════════════════════════════════════════════════════════
arrow(ax, 14, 23.27, 14, 22.65)
box(ax, 14, 22.35, 26, 0.65,
'ACUTE EXACERBATION MANAGEMENT (Home → ED → Hospital)',
C_ACUTE, fontsize=10, bold=True)
acute_steps = [
('① MILD–MODERATE\n(Home / Clinic)',
'● SABA 2–4 puffs MDI q20 min × 3\n'
'● Short-course oral prednisone\n'
' (0.5–1 mg/kg/d, max 40 mg × 5–7 d)\n'
'● Continue controller medications\n'
'● Review action plan',
5.0, 21.1),
('② MODERATE–SEVERE\n(Emergency Dept)',
'● Nebulised albuterol q20 min × 3\n'
'● Add ipratropium (SOR: A)\n'
'● IV/oral methylprednisolone\n'
'● Supplemental O₂ (SpO₂ ≥92%)\n'
'● Peak flow / spirometry monitoring',
12.5, 21.1),
('③ SEVERE / REFRACTORY\n(ICU / Admit)',
'● Continuous SABA nebulisation\n'
'● IV magnesium sulfate (2 g over 20 min)\n'
'● Heliox-driven albuterol (if available)\n'
'● IV corticosteroids (high-dose)\n'
'● Consider intubation / mechanical ventilation\n'
'● NO aminophylline in acute setting',
20.5, 21.1),
('④ RESPONSE?\nDischarge Planning',
'● Good response: discharge with\n'
' 3–5 day oral prednisone burst\n'
' + controller step review\n'
' + written action plan\n'
'● Poor response: admit / ICU',
26.5, 21.1),
]
for title, content, xpos, ypos in acute_steps:
box(ax, xpos, ypos, 6.2, 2.0,
title + '\n' + content,
C_ACUTE, fontsize=7.4, alpha=0.88,
border='#AD1457')
# ════════════════════════════════════════════════════════════════════════════
# BIOLOGICS REFERENCE BOX
# ════════════════════════════════════════════════════════════════════════════
arrow(ax, 14, 20.1, 14, 19.5)
box(ax, 14, 19.05, 26, 0.8,
'BIOLOGICS (Steps 5–6) | For severe persistent allergic / eosinophilic asthma',
C_BIO, fontsize=9.5, bold=True)
bio_data = [
('OMALIZUMAB\n(anti-IgE)\n'
'Mechanism: Binds free IgE\n'
'↓98–99% circulating IgE\n'
'Dose: SC q2–4wk\n'
'Age: ≥6 years\n'
'Indication: Allergic asthma',
4.0, '#7B1FA2'),
('MEPOLIZUMAB\nRESLIZUMAB\n(anti-IL-5)\n'
'Mechanism: Block IL-5\n'
'↓ eosinophil survival\n'
'Indication: Eosinophilic\n'
'severe asthma\n'
'Age: ≥6 years (mepo)',
9.5, '#512DA8'),
('BENRALIZUMAB\n(anti-IL-5Rα)\n'
'Mechanism: Binds IL-5Rα\n'
'Direct eosinophil depletion\n'
'via ADCC\n'
'Indication: Eosinophilic\n'
'severe asthma ≥12 yr',
15.0, '#4527A0'),
('DUPILUMAB\n(anti-IL-4Rα)\n'
'Mechanism: Blocks shared\n'
'IL-4/IL-13 receptor\n'
'Indication: Moderate–severe\n'
'uncontrolled, T2 asthma\n'
'Age: ≥6 years',
20.5, '#311B92'),
('TEZEPELUMAB\n(anti-TSLP)\n'
'Mechanism: Blocks TSLP\n'
'(upstream alarmin)\n'
'Indication: Severe uncontrolled\n'
'regardless of phenotype\n'
'Age: ≥12 years',
26.0, '#1A237E'),
]
for bio_txt, xpos, fc in bio_data:
box(ax, xpos, 17.85, 5.2, 2.1, bio_txt, fc, fontsize=7.5, alpha=0.92)
# ════════════════════════════════════════════════════════════════════════════
# ICS DOSE TABLE
# ════════════════════════════════════════════════════════════════════════════
arrow(ax, 14, 16.8, 14, 16.25)
box(ax, 14, 15.95, 26, 0.6,
'ICS COMPARATIVE DAILY DOSE TABLE (Adults ≥12 years) — NAEPP EPR-3 / Family Medicine 9e, Table 16-4',
'#0D47A1', fontsize=9, bold=True)
dose_headers = ['Drug', 'Formulation', 'Low Dose', 'Medium Dose', 'High Dose']
dose_data = [
['Beclomethasone HFA', '40 or 80 μg/puff', '80–240 μg', '>240–480 μg', '>480 μg'],
['Budesonide DPI', '90/180/200 μg/inh','180–600 μg','>600–1200 μg','>1200 μg'],
['Fluticasone HFA/MDI','44/110/220 μg/puff','88–264 μg', '>264–440 μg', '>440 μg'],
['Fluticasone DPI', '50/100/250 μg/inh','100–300 μg','>300–500 μg', '>500 μg'],
['Mometasone DPI', '200 μg/inh', '200 μg', '400 μg', '>400 μg'],
['Flunisolide', '250 μg/puff', '500–1000 μg','>1000–2000 μg','>2000 μg'],
]
col_xs = [3.2, 8.0, 13.0, 18.5, 23.5]
col_ws = [4.8, 4.8, 4.8, 4.8, 4.8]
# header row
y_tbl = 15.35
for i, (hdr, cx) in enumerate(zip(dose_headers, col_xs)):
box(ax, cx, y_tbl, col_ws[i]-0.15, 0.45, hdr, '#0D47A1',
fontsize=8, bold=True)
for row_i, row in enumerate(dose_data):
y_row = y_tbl - 0.48*(row_i+1)
for col_i, (val, cx) in enumerate(zip(row, col_xs)):
fc = '#E3F2FD' if row_i % 2 == 0 else 'white'
box(ax, cx, y_row, col_ws[col_i]-0.15, 0.42, val,
fc, textcolor='#1A237E', fontsize=7.5, border='#90CAF9')
# ════════════════════════════════════════════════════════════════════════════
# SPECIAL POPULATIONS & KEY WARNINGS
# ════════════════════════════════════════════════════════════════════════════
y_sp = 12.1
box(ax, 14, y_sp + 0.4, 26, 0.5,
'SPECIAL POPULATIONS & KEY WARNINGS', '#37474F', fontsize=9.5, bold=True)
sp_boxes = [
('PREGNANCY\n'
'● Albuterol (SABA) and ICS: Safe\n'
'● Monitor PEFR ≥80% personal best\n'
'● Uncontrolled asthma → ↑ preterm\n'
' labour, LBW, preeclampsia\n'
'● Treat same as non-pregnant',
4.0, '#1565C0'),
('PAEDIATRICS (0–4 yrs)\n'
'● Step 1: PRN SABA ± short-course ICS at RTI\n'
'● Step 2: Low-dose ICS\n'
'● Steps 4–6: Medium→high ICS+LABA\n'
'● Montelukast: FDA Boxed Warning\n'
' (neuropsychiatric events) ⚠',
10.5, '#1B5E20'),
('EIB (Exercise-Induced)\n'
'● SABA 15–30 min pre-exercise\n'
'● Regular ICS reduces EIB frequency\n'
'● ICS-formoterol covers rescue + EIB\n'
'● Warm-up before exercise helps',
17.0, '#E65100'),
('KEY DRUG WARNINGS\n'
'● LABA alone: FDA BLACK BOX (↑ death)\n'
'● Montelukast: Neuropsychiatric ⚠\n'
'● Theophylline: Narrow window;\n'
' NOT for acute ED use\n'
'● Oral CS: Taper if prolonged use',
23.5, '#B71C1C'),
]
for sp_txt, xpos, fc in sp_boxes:
box(ax, xpos, y_sp - 0.55, 5.8, 1.8, sp_txt, fc, fontsize=7.5, alpha=0.9)
# ════════════════════════════════════════════════════════════════════════════
# MONITORING BOX
# ════════════════════════════════════════════════════════════════════════════
y_mon = 9.35
box(ax, 14, y_mon, 26, 0.5,
'MONITORING & FOLLOW-UP', '#004D40', fontsize=9.5, bold=True)
mon_txt = ('FEV₁ (spirometry): Gold standard for obstruction severity | '
'PEFR: Portable monitoring (≥80% personal best = well controlled) | '
'FeNO: Conditionally recommended for ICS titration in allergic asthma ≥5 yrs\n'
'Reassess in 2–6 weeks after any step change | '
'Asthma control: Well-controlled (Step Down possible) | Not well-controlled (Step Up) | Very poorly controlled (Step Up 1–2 steps or oral CS)\n'
'Written Asthma Action Plan (MAP: Management | Action | Prevention) | '
'Inhaler technique check at every visit | Allergen avoidance counselling')
box(ax, 14, y_mon - 0.75, 26, 1.2, mon_txt,
'#E0F2F1', textcolor='#004D40', fontsize=8.0, border='#004D40')
# ════════════════════════════════════════════════════════════════════════════
# REFERENCES FOOTER
# ════════════════════════════════════════════════════════════════════════════
y_ref = 7.15
box(ax, 14, y_ref, 26, 0.4,
'REFERENCES', '#263238', fontsize=9, bold=True)
refs = [
'1. National Asthma Education and Prevention Program (NAEPP). Expert Panel Report 3 (EPR-3). NHLBI, 2007.',
'2. NAEPP. 2020 Focused Updates to the Asthma Management Guidelines. National Heart, Lung and Blood Institute, December 2020.',
'3. The Harriet Lane Handbook, 23rd ed. (Johns Hopkins Hospital). Elsevier, 2021. — Chapter 25, Figures 25.4–25.8.',
'4. Textbook of Family Medicine, 9th ed. (Rakel RE). Elsevier Saunders, 2015. — Chapters 16 & 19, Tables 16-3, 16-4.',
'5. Murray & Nadel\'s Textbook of Respiratory Medicine, 2-Volume, 7th ed. Elsevier, 2022. — Chapter 35.',
'6. Murphy KR & Solis J. NAEPP 2020 Guidelines: What\'s Important for Primary Care. J Fam Pract. 2021. PMID: 34432620.',
'7. Laitano R et al. Asthma management with triple ICS/LABA/LAMA. Expert Opin Pharmacother. 2024. PMID: 38864834.',
'8. GINA. Global Strategy for Asthma Management and Prevention. 2023 Update. ginasthma.org.',
]
ref_y_start = y_ref - 0.45
for i, ref in enumerate(refs):
ax.text(1.0, ref_y_start - i * 0.43, ref, fontsize=7.2,
color='#263238', va='top', ha='left')
# ════════════════════════════════════════════════════════════════════════════
# LEGEND / ABBREVIATIONS
# ════════════════════════════════════════════════════════════════════════════
legend_txt = ('Abbreviations: ICS = Inhaled Corticosteroid | LABA = Long-Acting β₂-Agonist | LAMA = Long-Acting Muscarinic Antagonist | '
'SABA = Short-Acting β₂-Agonist | LTRA = Leukotriene Receptor Antagonist\n'
'SMART = Single Maintenance And Reliever Therapy | EIB = Exercise-Induced Bronchoconstriction | '
'PRN = as needed | OCS = Oral Corticosteroid | FeNO = Fractional exhaled Nitric Oxide\n'
'▲ = New/updated recommendation in NAEPP 2020 | * = Limited availability / increased monitoring required | '
'⚠ = FDA Boxed Warning | SOR = Strength of Recommendation')
box(ax, 14, 3.55, 26, 0.95, legend_txt,
'#ECEFF1', textcolor='#263238', fontsize=7.5, border='#90A4AE')
# ════════════════════════════════════════════════════════════════════════════
# COLOUR LEGEND
# ════════════════════════════════════════════════════════════════════════════
legend_items = [
(C_STEP1, 'Step 1'),
(C_STEP2, 'Step 2'),
(C_STEP3, 'Step 3'),
(C_STEP4, 'Step 4'),
(C_STEP5, 'Step 5'),
('#7B1FA2', 'Step 6'),
(C_ACUTE, 'Acute Exacerbation'),
(C_BIO, 'Biologics'),
(C_CTRL, 'Control Assessment'),
]
for i, (fc, lbl) in enumerate(legend_items):
xp = 1.3 + i * 3.0
ax.add_patch(FancyBboxPatch((xp - 0.35, 2.7), 0.7, 0.35,
boxstyle="round,pad=0.05",
facecolor=fc, edgecolor='none', zorder=3))
ax.text(xp + 0.45, 2.875, lbl, fontsize=7, color='#263238',
va='center', ha='left')
ax.text(14, 2.35, 'Colour Legend', ha='center', fontsize=7.8,
color='#37474F', fontweight='bold')
plt.tight_layout(pad=0)
plt.savefig('/home/daytona/workspace/asthma-flowchart/asthma_treatment_algorithm.png',
dpi=180, bbox_inches='tight', facecolor=fig.get_facecolor())
plt.savefig('/home/daytona/workspace/asthma-flowchart/asthma_treatment_algorithm_hires.png',
dpi=300, bbox_inches='tight', facecolor=fig.get_facecolor())
print("Saved successfully.")