clinical reference guide for bronchial asthma management
"asthma"[MeSH Terms] AND "management"[MeSH Terms]
| Endogenous Factors | Environmental Factors | Triggers |
|---|---|---|
| Atopy | Allergens (indoor: dust mite, cockroach, animal dander) | Allergens (house dust mite, pollens, fungi) |
| Airway hyperresponsiveness | Allergens (outdoor: fungi, pollens) | Cold air, thunderstorms |
| Ethnicity | Obesity | Drugs (NSAIDs, aspirin, ACE inhibitors, beta-blockers) |
| Gender | Occupational sensitizers | Exercise and hyperventilation |
| Genetic predisposition | Tobacco smoking (active/passive) | Respiratory infections (viral) |
| Respiratory infections (early childhood) | Irritants (sprays, paint fumes) | |
| Socioeconomic status | Sulfur dioxide, pollutant gases |
| Component | Intermittent | Mild Persistent | Moderate Persistent | Severe Persistent |
|---|---|---|---|---|
| Daytime symptoms | ≤2 days/week | >2 days/wk, not daily | Daily | Throughout the day |
| Night awakenings | ≤2x/month | 3-4x/month | >1x/week, not nightly | Often 7x/week |
| SABA use (rescue) | ≤2 days/week | >2 days/wk, not daily | Daily | Several times/day |
| Activity limitation | None | Minor | Some | Extremely limited |
| FEV1 | >80% predicted | >80% predicted | >60% but <80% | <60% predicted |
| FEV1/FVC | Normal | Normal | Reduced 5% | Reduced 5% |
| Exacerbations requiring OCS | 0-1/year | ≥2/year | - | - |
| Initial treatment step | Step 1 | Step 2 | Step 3 | Step 4 or 5 |
| Step | Preferred Controller | Reliever |
|---|---|---|
| Step 1 | As-needed low-dose ICS-formoterol | As-needed low-dose ICS-formoterol |
| Step 2 | Low-dose ICS, OR as-needed ICS-formoterol | As-needed low-dose ICS-formoterol |
| Step 3 | Low-dose maintenance ICS-formoterol | As-needed low-dose ICS-formoterol |
| Step 4 | Medium-dose maintenance ICS-formoterol | As-needed low-dose ICS-formoterol |
| Step 5 | High-dose ICS-LABA + add-on LAMA; refer for phenotypic assessment ± biologic (anti-IgE, anti-IL-5/5R, anti-IL-4R) | As-needed low-dose ICS-formoterol |
| Step | Controller | Reliever |
|---|---|---|
| Step 1 | Take ICS whenever SABA taken | As-needed SABA |
| Step 2 | Low-dose ICS | As-needed SABA |
| Step 3 | Low-dose ICS-LABA | As-needed SABA |
| Step 4 | Medium-dose ICS-LABA | As-needed SABA |
| Step 5 | High-dose ICS-LABA + add-ons ± biologics | As-needed SABA |
Key principle: Using ICS-formoterol as reliever (Track 1) reduces exacerbation risk compared with SABA reliever. Check adherence likelihood before choosing Track 2.
| Drug | Target | Phenotype | Dosing |
|---|---|---|---|
| Omalizumab | IgE | Moderate-severe atopic/allergic asthma, elevated IgE, perennial allergen sensitization | SC every 2-4 weeks; dose based on IgE level + body weight |
| Mepolizumab | IL-5 | Severe eosinophilic asthma (eos ≥300 cells/µL) | SC 100 mg every 4 weeks |
| Reslizumab | IL-5 | Severe eosinophilic asthma (eos ≥400 cells/µL) | IV 3 mg/kg every 4 weeks |
| Benralizumab | IL-5 receptor α | Severe eosinophilic asthma (eos ≥300 cells/µL) | SC 30 mg monthly x3, then every 2 months |
| Dupilumab | IL-4 receptor α (blocks IL-4 + IL-13) | Moderate-severe eosinophilic asthma; OCS-dependent; atopic dermatitis; CRSwNP | SC 200-300 mg every 2 weeks |
| Tezepelumab | TSLP (thymic stromal lymphopoietin) | Severe asthma regardless of eosinophil count | SC every 4 weeks |
| Interval | Action |
|---|---|
| 2-6 weeks after initiation | Evaluate level of control achieved; adjust therapy |
| Every visit on controller | Assess control; consider step-down if well controlled ≥3 months |
| Ongoing | Review adherence, inhaler technique, triggers, comorbidities |
| Annually | Spirometry; vaccination; exacerbation history |
| Situation | Treatment |
|---|---|
| Mild intermittent asthma (Step 1) | As-needed low-dose ICS-formoterol (preferred) OR SABA |
| Mild-moderate persistent (Steps 2-3) | Low-dose ICS + LABA; ICS-formoterol as maintenance + reliever |
| Moderate-severe (Steps 4-5) | Medium-high dose ICS-LABA ± tiotropium ± biologics |
| Acute mild-moderate exacerbation | SABA q20min x3; oral prednisone 40-60 mg; reassess |
| Acute severe exacerbation | SABA + ipratropium q20min; IV methylprednisolone; consider IV MgSO4 2g; admission |
| Refractory severe asthma | Biologic selection based on phenotype (eosinophil count, IgE, FeNO) |
| Exercise-induced bronchospasm | SABA 5-10 min before; ICS; montelukast |
| AERD | Avoid NSAIDs; leukotriene modifiers |
Generate a printable PDF quick-reference guide for asthma management
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace not found"
mkdir -p /home/daytona/workspace/asthma-guide && python3 -c "from reportlab.lib import colors; print('reportlab ok')" && python3 -c "from reportlab.platypus import SimpleDocTemplate; print('platypus ok')"
~/asthma-guide/build_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm, cm
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import HexColor
import os
# ── Colour palette ──────────────────────────────────────────────────────────
NAVY = HexColor("#0D3B66")
SKY = HexColor("#1A8FE3")
TEAL = HexColor("#0F7B6C")
ORANGE = HexColor("#E87722")
RED_ALERT = HexColor("#C0392B")
AMBER = HexColor("#F39C12")
GREEN = HexColor("#27AE60")
LIGHT_BG = HexColor("#EAF4FC")
LIGHT_GRN = HexColor("#E8F8F0")
LIGHT_YLW = HexColor("#FEF9E7")
LIGHT_RED = HexColor("#FDEDEC")
LIGHT_GRY = HexColor("#F4F6F7")
WHITE = colors.white
BLACK = HexColor("#1A1A2E")
MED_GRY = HexColor("#7F8C8D")
PAGE_W, PAGE_H = A4
MARGIN = 14 * mm
OUTPUT = "/home/daytona/workspace/asthma-guide/Asthma_Management_Quick_Reference.pdf"
# ── Document ─────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=12*mm, bottomMargin=14*mm,
title="Bronchial Asthma – Clinical Quick-Reference Guide",
author="Orris Medical Reference",
)
# ── Styles ───────────────────────────────────────────────────────────────────
base = getSampleStyleSheet()
def S(name, parent="Normal", **kw):
return ParagraphStyle(name, parent=base[parent], **kw)
TITLE_S = S("title_s", fontName="Helvetica-Bold", fontSize=20, textColor=WHITE,
spaceAfter=2, alignment=TA_CENTER)
SUBTITLE_S= S("sub_s", fontName="Helvetica", fontSize=9, textColor=HexColor("#D0E8FA"),
alignment=TA_CENTER)
H1 = S("h1", fontName="Helvetica-Bold", fontSize=11, textColor=WHITE,
spaceAfter=2, alignment=TA_LEFT)
H2 = S("h2", fontName="Helvetica-Bold", fontSize=9.5, textColor=NAVY,
spaceBefore=4, spaceAfter=2)
BODY = S("body", fontName="Helvetica", fontSize=7.8, textColor=BLACK,
leading=11, spaceAfter=2)
BODY_BOLD = S("body_b", fontName="Helvetica-Bold", fontSize=7.8, textColor=BLACK, leading=11)
SMALL = S("small", fontName="Helvetica", fontSize=6.8, textColor=MED_GRY, leading=9)
BULLET = S("bullet", fontName="Helvetica", fontSize=7.8, textColor=BLACK,
leading=11, leftIndent=8, spaceAfter=1, bulletIndent=2)
TH = S("th", fontName="Helvetica-Bold", fontSize=7.5, textColor=WHITE,
alignment=TA_CENTER, leading=10)
TD = S("td", fontName="Helvetica", fontSize=7.2, textColor=BLACK,
alignment=TA_LEFT, leading=10)
TD_C = S("td_c", fontName="Helvetica", fontSize=7.2, textColor=BLACK,
alignment=TA_CENTER, leading=10)
TD_B = S("td_b", fontName="Helvetica-Bold", fontSize=7.2, textColor=BLACK,
alignment=TA_LEFT, leading=10)
LABEL = S("label", fontName="Helvetica-Bold", fontSize=7, textColor=WHITE,
alignment=TA_CENTER)
FOOTER_S = S("footer", fontName="Helvetica", fontSize=6.2, textColor=MED_GRY,
alignment=TA_CENTER)
# ── Helpers ───────────────────────────────────────────────────────────────────
def sp(h=3):
return Spacer(1, h*mm)
def hr(color=NAVY, thickness=0.6):
return HRFlowable(width="100%", thickness=thickness, color=color, spaceAfter=2, spaceBefore=2)
def section_header(text, color=NAVY):
"""Coloured section banner."""
data = [[Paragraph(text, H1)]]
t = Table(data, colWidths=[PAGE_W - 2*MARGIN])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("ROUNDEDCORNERS", [3]),
]))
return t
def info_box(title, rows_html, bg=LIGHT_BG, title_color=SKY):
"""Titled info box with bullet-like rows."""
content = [Paragraph(title, S("ib_title", fontName="Helvetica-Bold", fontSize=8.2,
textColor=title_color, spaceAfter=3))]
for r in rows_html:
content.append(Paragraph(r, BULLET))
data = [[content]]
t = Table(data, colWidths=[PAGE_W - 2*MARGIN])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("BOX", (0,0), (-1,-1), 0.5, title_color),
]))
return t
def two_col(left_w, right_w, left_content, right_content, bg_l=WHITE, bg_r=WHITE):
data = [[left_content, right_content]]
t = Table(data, colWidths=[left_w, right_w])
t.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("BACKGROUND", (0,0), (0,-1), bg_l),
("BACKGROUND", (1,0), (1,-1), bg_r),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
]))
return t
def styled_table(headers, rows, col_widths, header_bg=NAVY, stripe=True, fontsize=7.2):
th_style = S(f"_th{id(headers)}", fontName="Helvetica-Bold", fontSize=fontsize,
textColor=WHITE, alignment=TA_CENTER, leading=10)
td_style = S(f"_td{id(rows)}", fontName="Helvetica", fontSize=fontsize,
textColor=BLACK, alignment=TA_LEFT, leading=10)
td_c_sty = S(f"_tdc{id(rows)}", fontName="Helvetica", fontSize=fontsize,
textColor=BLACK, alignment=TA_CENTER, leading=10)
data = [[Paragraph(str(h), th_style) for h in headers]]
for ri, row in enumerate(rows):
data.append([Paragraph(str(c), td_c_sty if i > 0 else td_style)
for i, c in enumerate(row)])
style_cmds = [
("BACKGROUND", (0,0), (-1,0), header_bg),
("ROWBACKGROUNDS",(0,1), (-1,-1),
[HexColor("#F0F7FF"), WHITE] if stripe else [WHITE]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#BDC3C7")),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]
t = Table(data, colWidths=col_widths)
t.setStyle(TableStyle(style_cmds))
return t
def zone_badge(label, bg, fg=WHITE):
data = [[Paragraph(label, S(f"z{label}", fontName="Helvetica-Bold", fontSize=7.5,
textColor=fg, alignment=TA_CENTER))]]
t = Table(data, colWidths=[18*mm], rowHeights=[6*mm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 1),
("BOTTOMPADDING", (0,0), (-1,-1), 1),
("LEFTPADDING", (0,0), (-1,-1), 2),
("RIGHTPADDING", (0,0), (-1,-1), 2),
("BOX", (0,0), (-1,-1), 0.3, bg),
]))
return t
# ═══════════════════════════════════════════════════════════════════════════
# PAGE HEADER / FOOTER callbacks
# ═══════════════════════════════════════════════════════════════════════════
def header_footer(canvas, doc):
canvas.saveState()
w, h = A4
# Top banner
canvas.setFillColor(NAVY)
canvas.rect(0, h - 10*mm, w, 10*mm, fill=1, stroke=0)
canvas.setFillColor(SKY)
canvas.rect(0, h - 10.8*mm, w, 0.8*mm, fill=1, stroke=0)
canvas.setFont("Helvetica-Bold", 9)
canvas.setFillColor(WHITE)
canvas.drawString(MARGIN, h - 7*mm, "BRONCHIAL ASTHMA — Clinical Quick-Reference Guide")
canvas.setFont("Helvetica", 7.5)
canvas.drawRightString(w - MARGIN, h - 7*mm, f"Page {doc.page}")
# Bottom footer
canvas.setFillColor(LIGHT_GRY)
canvas.rect(0, 0, w, 9*mm, fill=1, stroke=0)
canvas.setFont("Helvetica", 6)
canvas.setFillColor(MED_GRY)
canvas.drawCentredString(w/2, 3.5*mm,
"Sources: Murray & Nadel's Respiratory Medicine · Fishman's Pulmonary Diseases · Washington Manual · "
"Katzung Pharmacology 16e · Rosen's Emergency Medicine · Family Medicine 9e | For clinical use — verify against current guidelines")
canvas.restoreState()
# ═══════════════════════════════════════════════════════════════════════════
# BUILD CONTENT
# ═══════════════════════════════════════════════════════════════════════════
story = []
# ── COVER BAND ───────────────────────────────────────────────────────────────
cover_data = [[
Paragraph("BRONCHIAL ASTHMA", TITLE_S),
Paragraph("Clinical Quick-Reference Guide", SUBTITLE_S),
Paragraph("GINA 2024 · NHLBI · Murray & Nadel · Katzung · Rosen's EM", SUBTITLE_S),
]]
cover_table = Table([[
Paragraph("BRONCHIAL ASTHMA", TITLE_S),
]], colWidths=[PAGE_W - 2*MARGIN])
cover_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), NAVY),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 10),
]))
cover_sub = Table([[
Paragraph("Clinical Quick-Reference Guide | GINA 2024 · NHLBI · Pharmacology & Emergency Medicine", SUBTITLE_S),
]], colWidths=[PAGE_W - 2*MARGIN])
cover_sub.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), SKY),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
]))
story += [cover_table, cover_sub, sp(4)]
# ════════════════════════════════════════════════════════════════════════════
# SECTION 1 — DEFINITION & PATHOPHYSIOLOGY
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("1. DEFINITION & PATHOPHYSIOLOGY"))
story.append(sp(2))
def_box = info_box(
"Definition",
[
"Chronic <b>inflammatory</b> disorder of the airways with marked <b>temporal variability in airflow obstruction</b>, often reversible spontaneously or with treatment.",
"Presents with recurrent <b>wheezing, chest tightness, cough, dyspnea</b> and heightened airway hyperresponsiveness (AHR).",
"Heterogeneous disease — multiple endotypes; distinct pathophysiologic mechanisms; interplay of genetic and environmental factors.",
],
bg=LIGHT_BG, title_color=SKY
)
story.append(def_box)
story.append(sp(2))
path_rows = [
("Bronchoconstriction", "Airway smooth muscle spasm → lumen narrowing"),
("Mucosal Oedema", "Increased vascular permeability, inflammatory cell infiltration"),
("Mucus Hypersecretion","Goblet-cell hypertrophy; mucus plugging of small airways"),
("Eosinophilic Inflammation", "IL-5 driven; correlates with AHR; T2 phenotype"),
("Airway Remodelling", "Sub-epithelial fibrosis, smooth muscle hypertrophy (chronic/severe disease)"),
]
story.append(styled_table(
["Pathologic Feature", "Mechanism / Consequence"],
path_rows,
[60*mm, PAGE_W - 2*MARGIN - 60*mm],
header_bg=TEAL
))
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 2 — RISK FACTORS & TRIGGERS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("2. RISK FACTORS & TRIGGERS", color=TEAL))
story.append(sp(2))
rf_headers = ["Endogenous Factors", "Environmental Factors", "Acute Triggers"]
rf_rows = [
("Atopy / genetic predisposition", "Indoor allergens (dust mite, cockroach, pet dander)", "Viral URTI (most common trigger)"),
("Airway hyperresponsiveness", "Outdoor allergens (pollens, moulds)", "Allergen exposure"),
("Gender (boys > girls pre-puberty; women > men post-puberty)", "Tobacco smoke (active & passive)", "Cold/dry air, exercise"),
("Obesity", "Occupational sensitizers", "NSAIDs / aspirin (AERD ~5–10%)"),
("", "Respiratory viral infections (early childhood)", "ACE inhibitors, β-blockers"),
("", "Low socioeconomic status", "Emotional stress, strong odours"),
]
story.append(styled_table(rf_headers, rf_rows,
[(PAGE_W - 2*MARGIN)/3]*3, header_bg=NAVY))
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 3 — DIAGNOSIS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("3. DIAGNOSIS", color=SKY))
story.append(sp(2))
cw2 = (PAGE_W - 2*MARGIN - 4*mm) / 2
left_dx = [
Paragraph("Spirometry Criteria", H2),
Paragraph("• FEV₁/FVC ratio <b>reduced</b> (obstructive pattern)", BULLET),
Paragraph("• Bronchodilator reversibility: FEV₁ ↑ <b>≥12% AND ≥200 mL</b> after SABA", BULLET),
Paragraph("• PEF diurnal variability <b>>20%</b> supports diagnosis", BULLET),
Paragraph("• Low FEV₁ = strong predictor of future exacerbations", BULLET),
sp(2),
Paragraph("FeNO (Fractional Exhaled NO)", H2),
Paragraph("• >25 ppb suggests eosinophilic airway inflammation", BULLET),
Paragraph("• Guides ICS dose titration and biologic selection", BULLET),
]
right_dx = [
Paragraph("Key Differentials to Exclude", H2),
Paragraph("• COPD / Asthma-COPD Overlap (ACO)", BULLET),
Paragraph("• Vocal cord dysfunction / inducible laryngeal obstruction", BULLET),
Paragraph("• Heart failure", BULLET),
Paragraph("• GERD-related cough / postnasal drip", BULLET),
Paragraph("• Bronchiectasis", BULLET),
Paragraph("• ACE inhibitor cough", BULLET),
sp(2),
Paragraph("Validated Control Scores", H2),
Paragraph("• Asthma Control Test (ACT) — ≤19 = not well controlled", BULLET),
Paragraph("• Asthma Control Questionnaire (ACQ)", BULLET),
Paragraph("• Asthma Therapy Assessment Questionnaire (ATAQ)", BULLET),
]
story.append(two_col(cw2, cw2, left_dx, right_dx, bg_l=LIGHT_BG, bg_r=LIGHT_GRN))
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 4 — SEVERITY CLASSIFICATION
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("4. SEVERITY CLASSIFICATION (NHLBI/GINA — Adults ≥12 years)", color=ORANGE))
story.append(sp(2))
sev_h = ["Component", "Intermittent", "Mild Persistent", "Moderate Persistent", "Severe Persistent"]
sev_r = [
("Daytime symptoms", "≤2 days/week", ">2 d/wk, not daily", "Daily", "Throughout day"),
("Night awakenings", "≤2×/month", "3–4×/month", ">1×/wk, not nightly","Often 7×/week"),
("Rescue SABA use", "≤2 days/week", ">2 d/wk, not daily", "Daily", "Several times/day"),
("Activity limitation", "None", "Minor", "Some", "Extremely limited"),
("FEV₁ (% predicted)", ">80%", ">80%", ">60% but <80%", "<60%"),
("FEV₁/FVC", "Normal", "Normal", "Reduced 5%", "Reduced 5%"),
("OCS exacerbations", "0–1/year", "≥2/year", "—", "—"),
("Start at Step →", "Step 1", "Step 2", "Step 3", "Step 4–5"),
]
cw_sev = [(PAGE_W-2*MARGIN)*x for x in [0.24, 0.19, 0.19, 0.19, 0.19]]
story.append(styled_table(sev_h, sev_r, cw_sev, header_bg=ORANGE))
story.append(sp(2))
# GINA Control
gina_note = info_box("GINA Control Categories (for ongoing monitoring — use these after initiating therapy)",
[
"<b>Controlled</b> — All criteria met: no daytime sx >2×/wk, no awakenings, no limitation, SABA ≤2×/wk, normal lung function → Maintain or step down",
"<b>Partly Controlled</b> — 1–2 criteria present → Consider step up",
"<b>Uncontrolled</b> — 3–4 criteria present → Step up until controlled",
"<b>Exacerbation</b> — Treat per acute exacerbation algorithm",
], bg=LIGHT_YLW, title_color=ORANGE)
story.append(gina_note)
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 5 — STEPWISE CHRONIC MANAGEMENT
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("5. STEPWISE CHRONIC MANAGEMENT (GINA 2024)", color=NAVY))
story.append(sp(2))
story.append(Paragraph("▶ TRACK 1 (Preferred) — ICS-Formoterol as Reliever", H2))
story.append(Paragraph(
"Using ICS-formoterol as reliever <b>reduces exacerbation risk</b> vs. SABA reliever — recommended by GINA as the preferred track.",
BODY))
story.append(sp(1))
t1_h = ["Step", "Preferred Controller", "Preferred Reliever", "Notes"]
t1_r = [
("Step 1", "As-needed low-dose ICS-formoterol", "As-needed low-dose ICS-formoterol", "Mild intermittent; no daily controller needed"),
("Step 2", "Low-dose ICS (daily), OR as-needed ICS-formoterol", "As-needed low-dose ICS-formoterol", "Persistent symptoms; add daily ICS"),
("Step 3", "Low-dose ICS-formoterol (maintenance)", "As-needed low-dose ICS-formoterol", "Single inhaler therapy (MART)"),
("Step 4", "Medium-dose ICS-formoterol (maintenance)", "As-needed low-dose ICS-formoterol", "Uncontrolled on Step 3"),
("Step 5", "High-dose ICS-LABA + add-on LAMA;\nRefer for phenotyping ± biologic", "As-needed low-dose ICS-formoterol", "Severe refractory; see biologic table"),
]
cw_t1 = [(PAGE_W-2*MARGIN)*x for x in [0.08, 0.31, 0.28, 0.33]]
story.append(styled_table(t1_h, t1_r, cw_t1, header_bg=NAVY))
story.append(sp(2))
story.append(Paragraph("▶ TRACK 2 (Alternative) — SABA Reliever (if adherence with daily ICS is likely)", H2))
t2_r = [
("Step 1", "Take ICS whenever SABA taken (concomitant use)", "SABA as needed"),
("Step 2", "Low-dose ICS daily", "SABA as needed"),
("Step 3", "Low-dose ICS-LABA daily", "SABA as needed"),
("Step 4", "Medium-dose ICS-LABA daily", "SABA as needed"),
("Step 5", "High-dose ICS-LABA + add-ons ± biologic", "SABA as needed"),
]
cw_t2 = [(PAGE_W-2*MARGIN)*x for x in [0.08, 0.54, 0.38]]
story.append(styled_table(["Step", "Controller", "Reliever"], t2_r, cw_t2, header_bg=TEAL))
story.append(sp(2))
step_up_box = info_box(
"⚠ Before Stepping Up — Exclude These Modifiable Factors First",
[
"<b>Non-adherence to ICS</b> — most common cause of poor control; linked to exacerbations, hospitalisation, accelerated lung-function decline",
"<b>Incorrect inhaler technique</b> — reassess at every visit",
"<b>Ongoing allergen / irritant exposure</b> — occupational, domestic",
"<b>Comorbidities:</b> Obesity · Sinonasal disease / rhinitis · GERD · OSA · Depression / anxiety",
"<b>Alternative diagnosis</b> — see differential list (Section 3)",
], bg=LIGHT_YLW, title_color=ORANGE)
story.append(step_up_box)
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 6 — PHARMACOLOGY
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("6. PHARMACOLOGICAL AGENTS", color=TEAL))
story.append(sp(2))
pharm_h = ["Drug Class", "Examples", "Mechanism", "Key Clinical Notes"]
pharm_r = [
("SABA\n(Reliever)",
"Albuterol (salbutamol)\nLevalbuterol",
"β₂ agonist → ↑cAMP → smooth muscle relaxation; also ↓vascular permeability",
"Rapid onset; peak 60–90 min. >2×/wk = inadequate control → step up. Risk of receptor desensitisation with overuse."),
("LABA\n(Controller — always with ICS)",
"Formoterol\nSalmeterol\nVilanterol",
"Long-acting β₂ agonist. Formoterol has faster onset; ultra-LABAs once-daily.",
"NEVER as monotherapy. Formoterol enables MART regimen. Improve FEV₁, symptoms, exacerbation frequency."),
("ICS\n(Cornerstone)",
"Beclomethasone\nBudesonide\nFluticasone\nCiclesonide",
"Suppress airway eosinophilic inflammation; ↓AHR; ↓mucus",
"Start low-to-medium dose (e.g., BDP 200 µg BID). Non-adherence = #1 risk factor for exacerbations and death."),
("ICS-LABA Combo",
"Budesonide/formoterol\nFluticasone/salmeterol\nFluticasone/vilanterol",
"Combined anti-inflammatory + bronchodilator",
"Preferred fixed-dose combination from Step 3 onward. MART = maintenance AND reliever therapy (budesonide/formoterol only)."),
("LAMA\n(Add-on Step 4–5)",
"Tiotropium\nUmeclidinium",
"Long-acting muscarinic antagonist → bronchodilation via M3 blockade",
"Add-on to ICS-LABA; improves FEV₁ and reduces exacerbations. Once-daily inhaled."),
("Leukotriene\nModifiers",
"Montelukast 10 mg OD\nZafirlukast 20 mg BD\nZileuton 1200 mg BD SR",
"Block CysLT receptors (montelukast, zafirlukast) or inhibit 5-lipoxygenase (zileuton) → ↓bronchoconstriction, oedema, mucus",
"Oral route. Less effective than ICS alone. Particularly effective in AERD and exercise-induced asthma. Montelukast approved ≥12 months."),
("Methylxanthines",
"Theophylline",
"Phosphodiesterase inhibitor → ↑cAMP; also anti-inflammatory at low doses",
"Last-line only. Narrow therapeutic window (target 5–15 µg/mL). Multiple drug interactions. Monitor levels."),
]
cw_ph = [(PAGE_W-2*MARGIN)*x for x in [0.14, 0.18, 0.28, 0.40]]
story.append(styled_table(pharm_h, pharm_r, cw_ph, header_bg=TEAL, fontsize=7.0))
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 7 — BIOLOGIC THERAPIES
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("7. BIOLOGIC THERAPIES (Step 5 — Severe Refractory Asthma)", color=HexColor("#6C3483")))
story.append(sp(2))
story.append(Paragraph(
"Phenotypic assessment required before biologic selection: <b>blood eosinophil count, total IgE, FeNO, allergen sensitisation (RAST/skin prick).</b>",
BODY))
story.append(sp(1))
bio_h = ["Drug", "Target", "Phenotype / Indication", "Dosing", "Key Effect"]
bio_r = [
("Omalizumab", "IgE", "Moderate–severe atopic asthma\nElevated IgE + perennial allergen sensitisation",
"SC q2–4wk\n(dose by IgE + weight)", "↓exacerbations 88% hospitalisation rate; ↓OCS need"),
("Mepolizumab", "IL-5", "Severe eosinophilic asthma\nEos ≥300 cells/µL",
"SC 100 mg q4wk", "↓exacerbations; ↓OCS dose; ↑FEV₁"),
("Reslizumab", "IL-5", "Severe eosinophilic asthma\nEos ≥400 cells/µL",
"IV 3 mg/kg q4wk\n(20–50 min infusion)", "↑FEV₁; ↓exacerbations; risk of anaphylaxis (0.3%)"),
("Benralizumab", "IL-5Rα", "Severe eosinophilic asthma\nEos ≥300 cells/µL",
"SC 30 mg q4wk×3,\nthen q8wk", "Depletes eosinophils from blood, airway, bone marrow; ↓OCS"),
("Dupilumab", "IL-4Rα\n(IL-4+IL-13)", "Moderate–severe eosinophilic asthma\nOCS-dependent; atopic dermatitis; CRSwNP\nAge ≥12 yrs",
"SC 200–300 mg\nq2wk (loading × 2 first)", "↓exacerbations; ↓OCS; ↑FEV₁. Transient eosinophilia first 4 months."),
("Tezepelumab", "TSLP", "Severe asthma\n(any phenotype — does not require eosinophilia)",
"SC 210 mg q4wk", "Broad anti-inflammatory; ↓exacerbations regardless of T2 status"),
]
cw_bio = [(PAGE_W-2*MARGIN)*x for x in [0.13, 0.10, 0.25, 0.18, 0.34]]
story.append(styled_table(bio_h, bio_r, cw_bio, header_bg=HexColor("#6C3483"), fontsize=6.9))
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 8 — ACUTE EXACERBATION MANAGEMENT
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("8. ACUTE EXACERBATION MANAGEMENT", color=RED_ALERT))
story.append(sp(2))
# High-risk flags
risk_box = info_box(
"🚨 High-Risk Features — Low Threshold for Admission / ICU",
[
"Prior intubation or mechanical ventilation for asthma",
"ED visit or hospitalisation for asthma in past 12 months",
"Currently on / recently stopped oral corticosteroids",
"Not on ICS controller therapy",
"Overuse of SABA (>1 canister/month)",
"Poor adherence; no written asthma action plan; comorbid psychiatric illness",
"Food allergy in a patient with asthma",
], bg=LIGHT_RED, title_color=RED_ALERT)
story.append(risk_box)
story.append(sp(2))
# ABG warning
abg_box = info_box(
"ABG Interpretation in Acute Asthma",
[
"Early: <b>Mild hypoxaemia + respiratory alkalosis</b> (↓PaCO₂)",
"<b>Normalising PaCO₂ in an unimproving patient = DANGER SIGN → impending respiratory failure</b>",
"Late / severe: Rising PaCO₂ + metabolic acidosis (↑lactate) → urgent ITU review",
], bg=LIGHT_RED, title_color=RED_ALERT)
story.append(abg_box)
story.append(sp(2))
# Treatment table
tx_h = ["Severity", "Bronchodilator", "Corticosteroid", "Adjuncts / Disposition"]
tx_r = [
("Mild–Moderate\nPEF 40–70%\nSpO₂ ≥92%\nSpeech intact",
"Albuterol 2–6 puffs MDI + spacer OR 2.5 mg neb q20min × 3\nAdd ipratropium 0.5 mg q20min",
"Prednisone PO 40–60 mg/day (oral = IV if equivalent dose)",
"Reassess after 60–90 min. If PEF >70% + good response → discharge with OCS 5–7 days + ICS ↑"),
("Severe\nPEF <40%\nSpO₂ <92%\nAccessory muscles\nCannot complete sentences",
"Albuterol 2.5–5 mg + ipratropium 0.5 mg q20min neb\nOR continuous neb albuterol 10–15 mg/hr (with telemetry)\nConsider IV/SC epinephrine",
"IV methylprednisolone 1–2 mg/kg\nOral equivalent acceptable if tolerated",
"IV MgSO₄ 2 g over 20 min (refractory after 1 hr)\nHeliox 70:30 (refractory)\nHospital admission"),
("Life-threatening\nSilent chest\nCyanosis\nAltered consciousness\nFatigue",
"Continuous neb bronchodilator\nConsider IV bronchodilator (salbutamol IV)",
"IV methylprednisolone high-dose",
"ITU / HDU admission\nNIV or mechanical ventilation\nPermissive hypercapnia\nAnaesthesia review"),
]
cw_tx = [(PAGE_W-2*MARGIN)*x for x in [0.16, 0.30, 0.22, 0.32]]
story.append(styled_table(tx_h, tx_r, cw_tx, header_bg=RED_ALERT, fontsize=7.0))
story.append(sp(2))
# Discharge criteria
dc_box = info_box(
"Discharge Criteria & Plan",
[
"PEF or FEV₁ <b>≥70% predicted</b> (sustained ≥60 min after last bronchodilator)",
"SpO₂ ≥92% on room air · Minimal symptoms · Tolerating oral medication",
"<b>Discharge medications:</b> Prednisone 40 mg/day × 5–7 days + initiate or increase ICS dose",
"Provide written Asthma Action Plan; review inhaler technique; arrange follow-up within 1–2 weeks",
], bg=LIGHT_GRN, title_color=TEAL)
story.append(dc_box)
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 9 — PEF ACTION PLAN ZONES
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("9. PEAK FLOW (PEF) ACTION PLAN ZONES", color=GREEN))
story.append(sp(2))
zone_h = ["Zone", "PEF (% Personal Best)", "Symptoms", "Action"]
zone_r = [
("🟢 GREEN", ">80%", "None or minimal; normal activity", "Continue current medications; no change needed"),
("🟡 YELLOW","60–80%", "Some symptoms; activity may be limited", "Increase rescue SABA; consider doubling ICS or starting OCS; contact clinician if no improvement in 24 hrs"),
("🔴 RED", "<60%", "Severe symptoms; activity severely limited","Immediate SABA; start OCS (prednisone 40–60 mg); SEEK EMERGENCY CARE if no rapid improvement"),
]
cw_z = [(PAGE_W-2*MARGIN)*x for x in [0.11, 0.18, 0.30, 0.41]]
story.append(styled_table(zone_h, zone_r, cw_z, header_bg=GREEN))
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 10 — SPECIAL SITUATIONS
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("10. SPECIAL SITUATIONS", color=HexColor("#1F618D")))
story.append(sp(2))
spec_h = ["Situation", "Key Considerations", "Specific Treatment"]
spec_r = [
("Exercise-Induced\nBronchoconstriction (EIB)",
"Stimulus: airway dehydration → ↑osmolarity → histamine/LT/PG release → SM contraction",
"SABA 5–10 min pre-exercise (first-line). ICS strongly recommended. LABA + ICS if ICS insufficient. Montelukast, cromolyn, ipratropium also effective."),
("Cough-Variant Asthma",
"Chronic cough as sole manifestation; often nocturnal; confirm with methacholine challenge",
"Treat as standard asthma. Exclude: ACE inhibitors, GERD, postnasal drip, laryngeal dysfunction."),
("AERD (Aspirin-Exacerbated\nRespiratory Disease)",
"~5–10% of asthmatics. Triad: asthma + CRSwNP + NSAID sensitivity (COX-1 inhibition). Not IgE-mediated.",
"Avoid all NSAIDs/aspirin. Leukotriene modifiers are especially effective. Consider aspirin desensitisation in specialist centre."),
("Occupational Asthma",
"Sensitiser-induced (true OA) vs. irritant-induced. Variable patterns related to work schedule.",
"Remove from exposure promptly. Standard pharmacotherapy. Serial PEF monitoring at/away from work."),
("Perimenstrual Asthma",
"Affects ~40% of asthmatic women. Premenstrual worsening likely progesterone/oestrogen mediated.",
"Optimise baseline controller. Consider short-course OCS premenstrually if severe. OCP may help in some."),
("Brittle Asthma",
"Type I: chaotic daily PEF variability. Type II: normal baseline but abrupt catastrophic falls.",
"Maximise controller therapy. SC epinephrine auto-injector at all times. Medical alert ID. Specialist referral."),
]
cw_sp = [(PAGE_W-2*MARGIN)*x for x in [0.17, 0.33, 0.50]]
story.append(styled_table(spec_h, spec_r, cw_sp, header_bg=HexColor("#1F618D"), fontsize=7.0))
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 11 — NON-PHARMACOLOGICAL / PATIENT EDUCATION
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("11. NON-PHARMACOLOGICAL MANAGEMENT & PATIENT EDUCATION", color=TEAL))
story.append(sp(2))
cw2b = (PAGE_W - 2*MARGIN - 4*mm) / 2
left_np = [
Paragraph("Trigger Avoidance", H2),
Paragraph("• House dust mite reduction — complete removal ↓ AHR", BULLET),
Paragraph("• Pet allergen avoidance / removal from home", BULLET),
Paragraph("• Occupational sensitiser removal (earlier = better prognosis)", BULLET),
Paragraph("• Smoking cessation (active + passive smoke)", BULLET),
Paragraph("• NSAID / aspirin avoidance in AERD", BULLET),
sp(2),
Paragraph("Vaccinations (CDC Recommendations)", H2),
Paragraph("• Annual <b>influenza vaccine</b> (inactivated) for all adult asthmatics", BULLET),
Paragraph("• <b>Pneumovax (PPSV23)</b> — single dose, adults 19–64 with chronic illness including asthma", BULLET),
sp(2),
Paragraph("Comorbidity Management", H2),
Paragraph("• Obesity: weight loss improves control", BULLET),
Paragraph("• GERD: PPI therapy; dietary modification", BULLET),
Paragraph("• Rhinosinusitis / nasal polyps: intranasal ICS; consider biologic", BULLET),
Paragraph("• OSA: CPAP therapy", BULLET),
Paragraph("• Depression / anxiety: psychological support", BULLET),
]
right_np = [
Paragraph("Written Asthma Action Plan (AAP)", H2),
Paragraph("• <b>Evidence-based</b>: ↓ ED visits, hospitalisations, morbidity", BULLET),
Paragraph("• Most effective when combined with individual/group education sessions", BULLET),
Paragraph("• Include: zone-based instructions, OCS self-start criteria, emergency contacts", BULLET),
Paragraph("• Use simple language; tailor to patient's literacy and capability", BULLET),
sp(2),
Paragraph("Patient Education Must Cover", H2),
Paragraph("• Nature of asthma (inflammatory vs. bronchospasm)", BULLET),
Paragraph("• <b>Inhaler technique</b> — assess and correct at every visit", BULLET),
Paragraph("• Controller vs. reliever distinction — common source of confusion", BULLET),
Paragraph("• Trigger recognition and self-monitoring", BULLET),
Paragraph("• When to step up / seek emergency care", BULLET),
Paragraph("• Digital tools / apps may improve adherence and predict exacerbations", BULLET),
sp(2),
Paragraph("Follow-Up Schedule", H2),
Paragraph("• 2–6 weeks after initiating therapy — assess control achieved", BULLET),
Paragraph("• Every visit on controller — check control; consider step-down if controlled ≥3 months", BULLET),
Paragraph("• Annually: spirometry, vaccination review, exacerbation history", BULLET),
Paragraph("• Step-down: reduce ICS by ~25–50%; avoid during high-risk seasons", BULLET),
]
story.append(two_col(cw2b, cw2b, left_np, right_np, bg_l=LIGHT_GRN, bg_r=LIGHT_BG))
story.append(sp(4))
# ════════════════════════════════════════════════════════════════════════════
# SECTION 12 — QUICK REFERENCE CARD
# ════════════════════════════════════════════════════════════════════════════
story.append(section_header("12. QUICK-REFERENCE SUMMARY CARD", color=NAVY))
story.append(sp(2))
qr_h = ["Clinical Situation", "First-Line Treatment", "Key Points"]
qr_r = [
("Mild intermittent (Step 1)", "As-needed low-dose ICS-formoterol (Track 1 preferred)", "No daily controller required; address triggers"),
("Mild–moderate persistent (Steps 2–3)", "Low-dose ICS daily ± LABA (formoterol combo); MART from Step 3", "Reassess adherence and technique before step-up"),
("Moderate–severe persistent (Steps 4–5)", "Medium–high dose ICS-LABA ± LAMA; biologic if refractory", "Phenotype assessment (eosinophils, IgE, FeNO)"),
("Acute mild–moderate exacerbation", "SABA q20min ×3 + ipratropium; oral prednisone 40–60 mg", "Reassess at 60–90 min; discharge if PEF >70%"),
("Acute severe exacerbation", "Continuous neb albuterol; IV methylprednisolone; IV MgSO₄ 2 g if refractory", "Admit; monitor PCO₂; ICU if deteriorating"),
("Life-threatening / impending respiratory failure", "Continuous bronchodilators; high-dose IV steroids; consider IV salbutamol", "ICU admission; mechanical ventilation; permissive hypercapnia"),
("Exercise-induced bronchoconstriction", "SABA 5–10 min pre-exercise; ICS daily; montelukast add-on", "Warm-up exercises; face mask in cold air"),
("AERD", "Avoid ALL NSAIDs; leukotriene modifiers (montelukast)", "Consider specialist aspirin desensitisation"),
("Allergic / T2-high severe asthma (Step 5)", "Omalizumab (anti-IgE) for elevated IgE + allergen sensitisation", "Sub-cut q2–4wk; dose adjusted by IgE + body weight"),
("Eosinophilic severe asthma (Step 5)", "Mepolizumab / benralizumab / reslizumab (anti-IL-5/IL-5R)", "Blood eos ≥300 cells/µL; ↓OCS dose; ↓exacerbations"),
("T2-high (eos + atopic) or OCS-dependent", "Dupilumab (anti-IL-4Rα) — blocks IL-4 + IL-13", "Effective regardless of eos in OCS-dependent disease"),
("Severe asthma, any phenotype", "Tezepelumab (anti-TSLP)", "Broadest eligibility; does not require eosinophilia"),
]
cw_qr = [(PAGE_W-2*MARGIN)*x for x in [0.22, 0.38, 0.40]]
story.append(styled_table(qr_h, qr_r, cw_qr, header_bg=NAVY, fontsize=7.2))
story.append(sp(3))
story.append(Paragraph(
"Sources: Murray & Nadel's Textbook of Respiratory Medicine · Fishman's Pulmonary Diseases and Disorders · "
"The Washington Manual of Medical Therapeutics · Katzung's Basic and Clinical Pharmacology 16e · "
"Rosen's Emergency Medicine · Textbook of Family Medicine 9e · Harriet Lane Handbook 23e",
SMALL))
# ════════════════════════════════════════════════════════════════════════════
# BUILD
# ════════════════════════════════════════════════════════════════════════════
doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
print(f"PDF saved to: {OUTPUT}")
print(f"Size: {os.path.getsize(OUTPUT):,} bytes")
cd /home/daytona/workspace/asthma-guide && python3 build_pdf.py 2>&1
Asthma Management Quick Reference
PDF Document · PDF
| Section | Content |
|---|---|
| 1 | Definition & Pathophysiology |
| 2 | Risk Factors & Triggers table |
| 3 | Diagnosis (spirometry criteria, differentials, control scores) |
| 4 | NHLBI Severity Classification table (Intermittent → Severe Persistent) + GINA control categories |
| 5 | GINA Stepwise Management - Track 1 (ICS-formoterol reliever) & Track 2 (SABA reliever) + "step-up checklist" |
| 6 | Pharmacology table (SABA, LABA, ICS, ICS-LABA, LAMA, LTRA, methylxanthines) |
| 7 | Biologic therapies table (omalizumab, mepolizumab, reslizumab, benralizumab, dupilumab, tezepelumab) with dosing |
| 8 | Acute Exacerbation Management (mild/moderate/severe/life-threatening) + discharge criteria |
| 9 | PEF Action Plan Zones (Green/Yellow/Red) |
| 10 | Special Situations (EIB, cough-variant, AERD, occupational, perimenstrual, brittle asthma) |
| 11 | Non-pharmacological management, vaccinations, patient education, follow-up |
| 12 | Quick-Reference Summary Card (12 clinical scenarios at a glance) |
how to take case history with clinical examination of a patient with peripheral neuropathy