Management of bronchial asthma acc to latest standard guidelines and harrison
"asthma"[MeSH Terms] AND "management" AND "guidelines"
GINA 2025 2026 asthma management guidelines latest update
https://www.guidelinecentral.com/insights/may-2026-gina-asth…
asthma stepwise management GINA treatment steps inhaler

Summary : This figure presents the 2021 Asthma Management Continuum for preschoolers, children, and adults, outlining a stepwise approach to asthma therapy based on severity and age, with regular reassessment and escalation of treatment as needed. flowchart: # Main Structure : • The continuum is depicted as a sloped, color-coded triangle progressing from bottom left (mild) to top right (severe). • The base includes foundational steps, with increasing therapy intensity as you move upward. # Steps and Nodes : • Confirm Diagnosis (bottom, blue bar) • Environmental Control, Education and Written Action Plan (above, teal bar) • SABA or bud/form* as needed (grey bar) • Inhaled Corticosteroid (ICS) (large yellow-green area, central) – Second-Line: Leukotriene Receptor Antagonist (LTRA) – ICS Dosing: • Low Dose: – ≥12 yrs: ≤250 mcg/day § – 6-11 yrs: ≤200 mcg/day § – 1-5 yrs: <200 mcg/day § • Medium Dose: – ≥12 yrs: 251–500 mcg/day § – 6-11 yrs: 201–400 mcg/day § – 1-5 yrs: 200–250 mcg/day § • High Dose: – ≥12 yrs: >500 mcg/day § – 6-11 yrs: >400 mcg/day § – 1-5 yrs: Refer • Add-on therapies (upper right, color-coded by age group): – ≥12 yrs: Add LABA, then LTRA and/or tiotropium – 6-11 yrs: Add LABA or LTRA – 1-11 yrs: Increase ICS • Severe Asthma (top right, red): Add LTRA and/or tiotropium for ≥12 yrs # Regular Reassessment Box (left side) : • Control • Risk of exacerbation • Spirometry or PEF • Inhaler technique • Adherence • Triggers • Comorbidities • Sputum eosinophils # Connectors and Flow : • Large grey arrow labeled “Adjust Therapy to Achieve Control and Prevent Future Risk” runs diagonally upward, indicating escalation. • Steps are arranged from foundational (bottom) to advanced (top right). # Design Encodings : • Color gradient from blue (mild) to red (severe). • Age groups and therapy steps are color-coded. • Text boxes and arrows guide progression and reassessment. # Analysis : • The figure emphasizes a stepwise, age-specific approach to asthma management, starting with diagnosis and environmental control, then escalating pharmacologic therapy as needed. • Regular reassessment is highlighted as essential at all stages. • The structure supports individualized therapy adjustment based on control and risk, with clear escalation pathways for increasing severity.

Summary : This figure presents two flowcharts outlining the pharmacological management of chronic obstructive pulmonary disease (COPD) based on the primary symptom: dyspnea or exacerbations. It details stepwise treatment escalation, decision points based on blood eosinophil counts, and additional therapeutic options. flowchart: # Dyspnea Management : Nodes : • LABA or LAMA (rounded rectangle) • LABA + LAMA* (rounded rectangle) • Consider switching inhaler device or molecules; Implement or escalate non-pharmacological treatment(s); Consider adding ensifentrine; Investigate (and treat) other causes of dyspnea (large rounded rectangle) Connectors : • Downward arrows from LABA or LAMA → LABA + LAMA* → management options box Layout : • Vertical, single branch, two main steps followed by a box with four bullet points # Exacerbations Management : Nodes : • LABA or LAMA (rounded rectangle) • LABA + LAMA* (rounded rectangle) • LABA + LAMA + ICS* (rounded rectangle) • Roflumilast (rounded rectangle, “FEV1 < 50% & chronic bronchitis”) • Azithromycin (rounded rectangle, “preferentially in former smokers”) • Dupilumab (rounded rectangle, “chronic bronchitis”) Connectors : • LABA or LAMA → LABA + LAMA* (if blood eos < 300) • LABA or LAMA → LABA + LAMA + ICS* (if blood eos ≥ 300) • LABA + LAMA* → LABA + LAMA + ICS* (if blood eos ≥ 100) • LABA + LAMA + ICS* → Roflumilast, Azithromycin, Dupilumab (branching arrows) • LABA + LAMA + ICS* → Dupilumab (if blood eos ≥ 300) Layout : • Vertical with multiple branches based on blood eosinophil count thresholds (<100, <300, ≥100, ≥300) • Final step branches into three parallel options # Additional Information : • Footnote: Single inhaler therapy may be more convenient and effective than multiple inhalers; single inhalers improve adherence to treatment. • Consider de-escalation of ICS if pneumonia or other considerable side-effects. In case of blood eos ≥ 300 cells/μl de-escalation is more likely to be associated with the development of exacerbations. • Exacerbations refers to the number of exacerbations per year. # Analysis : • The dyspnea pathway is linear, focusing on escalation and device/molecule switching, with consideration for non-pharmacological treatments and other causes. • The exacerbation pathway is more complex, with branching based on blood eosinophil counts, leading to different combinations of inhaled therapies and add-on treatments for specific patient subgroups (chronic bronchitis, former smokers, high eosinophil counts). • The figure emphasizes personalized treatment escalation and the importance of blood eosinophil count in guiding therapy for COPD exacerbations.

Summary : This flowchart outlines the evaluation and management process for patients with Sjögren’s syndrome who present with signs and/or symptoms of airway disorders, including recommended diagnostic steps and treatment options. flowchart: # Nodes : • “Sjögren’s patient with signs and/or symptoms of airway disorders” (rounded rectangle, top) • “Obtain PFTs and HRCT” (rounded rectangle) • “Airways disorder” (rounded rectangle) • Treatment options (rectangle with numbered list): 1. Asthma/COPD: Manage as per clinical practice guidelines. Inhaled corticosteroids and beta-agonists are the mainstay of management. Avoid anticholinergics to prevent drying of secretions. 2. Bronchiolitis: Trial of inhaled corticosteroids ± macrolides. 3. Nebulized saline and secretagogues in patients with concern for xerotrachea. • “Annual PFTs” (rounded rectangle, bottom) # Connectors : • Downward arrows connect each node sequentially from top to bottom. • No branching; all steps are linear. # Layout : • Vertical, single-column arrangement. • All nodes are aligned centrally, with the treatment options node larger to accommodate the list. # Analysis : • The flowchart presents a stepwise, linear approach for managing airway disorders in Sjögren’s patients: initial assessment, diagnostic testing, confirmation of disorder, tailored treatment based on specific airway condition, and ongoing annual monitoring. No decision branches or alternative pathways are depicted; the process is straightforward and protocol-driven.
| Score | Control Level |
|---|---|
| 0 "Yes" | Well controlled |
| 1-2 "Yes" | Partly controlled |
| 3-4 "Yes" | Uncontrolled |
| Severity | Step Required |
|---|---|
| Mild | Well controlled at Step 1 or 2 |
| Moderate | Well controlled at Step 3 |
| Severe | Requires Step 4-5, or uncontrolled despite it |
"A major change in the stepwise approach, advocated for more than two decades, has occurred. Evidence has accumulated that as-needed ICS can be used instead of regular ICS in milder asthma... Since formoterol is a LABA with a rapid onset, combination ICS/formoterol has been used as a single agent in multiple studies: as needed without background therapy in milder asthma... GINA, as part of a comprehensive strategy, recommends ICS/formoterol as the reliever in all steps of asthma severity, including intermittent asthma (step 1)."
| Step | Preferred Controller | Reliever | Notes |
|---|---|---|---|
| Step 1 (Intermittent) | None (AIR track) or low-dose ICS | As-needed low-dose ICS-formoterol (preferred) or ICS-SABA | SABA alone no longer preferred per GINA 2024+ |
| Step 2 (Mild persistent) | Low-dose ICS daily | As-needed low-dose ICS-formoterol or SABA + ICS | LTRA alternative if ICS intolerable |
| Step 3 (Moderate persistent) | Low-dose ICS-LABA daily | As-needed low-dose ICS-formoterol (MART) | Medium ICS mono or ICS+LTRA as alternatives |
| Step 4 (Severe) | Medium/high-dose ICS-LABA | As-needed low-dose ICS-formoterol | Add LAMA (tiotropium) if uncontrolled; check technique, adherence |
| Step 5 (Very severe) | High-dose ICS-LABA + LAMA | As-needed ICS-formoterol | Add biologic; consider low-dose OCS |
| Biologic | Target | Biomarker | Indication |
|---|---|---|---|
| Omalizumab | Anti-IgE | Elevated serum IgE, allergic sensitization | Allergic asthma ≥6 yrs |
| Mepolizumab | Anti-IL-5 | Blood eos ≥150-300/µL | Eosinophilic asthma ≥6 yrs |
| Benralizumab | Anti-IL-5Rα | Blood eos ≥300/µL | Eosinophilic asthma ≥12 yrs |
| Dupilumab | Anti-IL-4Rα (blocks IL-4 + IL-13) | Blood eos ≥150/µL or FeNO ≥25 ppb | Type 2 asthma ≥6 yrs; also COPD with eos + recurrent exacerbations |
| Tezepelumab | Anti-TSLP | Broad (even non-T2) | Severe asthma ≥12 yrs; shows benefit even without elevated biomarkers |
| Depemokimab | Anti-IL-5 (ultra-long-acting) | Blood eos ≥300/µL | NEW in GINA 2026 - eosinophilic asthma ≥12 yrs; also CRSwNP ≥18 yrs |
| PEFR | Action |
|---|---|
| >60% predicted | Often responds to beta-2 agonists alone |
| <60% predicted | Add ipratropium + systemic steroids; consider admission |
| <30% predicted | ICU-level care, consider IV magnesium |
50% have persistent eosinophilic inflammation
| Device | Pros | Cons |
|---|---|---|
| pMDI | Compact, portable, cheap, multidose | Needs hand-mouth coordination; oropharyngeal deposition |
| pMDI + spacer | Removes coordination need; less oropharyngeal deposition | Bulkier |
| DPI | Breath-actuated, no coordination needed | Needs adequate inspiratory flow; humidity sensitive |
| Nebulizer | Good for acute/severe, elderly, young children | Bulky, time-consuming, expensive |

Create a concise clinical summary document for easy reference
ls -la /tmp/workspace/ 2>/dev/null || mkdir -p /tmp/workspace && echo "workspace ready"
mkdir -p /tmp/workspace/asthma-summary && cat > /tmp/workspace/asthma-summary/create_doc.py << 'PYEOF' from docx import Document from docx.shared import Pt, Inches, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.oxml.ns import qn from docx.oxml import OxmlElement import copy doc = Document() # ── Page margins ────────────────────────────────────────────────── section = doc.sections[0] section.page_width = Inches(8.5) section.page_height = Inches(11) section.top_margin = Inches(0.75) section.bottom_margin = Inches(0.75) section.left_margin = Inches(0.85) section.right_margin = Inches(0.85) # ── Base style ──────────────────────────────────────────────────── normal = doc.styles["Normal"] normal.font.name = "Calibri" normal.font.size = Pt(9.5) # ── Colour palette ──────────────────────────────────────────────── NAVY = RGBColor(0x1A, 0x35, 0x5E) # headings TEAL = RGBColor(0x00, 0x7A, 0x87) # sub-headings RED = RGBColor(0xC0, 0x39, 0x2B) # warnings / alerts GREEN = RGBColor(0x1E, 0x7A, 0x45) # positive / new GREY = RGBColor(0xF2, 0xF5, 0xF7) # table header fill WHITE = RGBColor(0xFF, 0xFF, 0xFF) BLACK = RGBColor(0x00, 0x00, 0x00) # ── Helper: set cell background ─────────────────────────────────── def set_cell_bg(cell, rgb: RGBColor): tc = cell._tc tcPr = tc.get_or_add_tcPr() shd = OxmlElement("w:shd") hex6 = f"{rgb[0]:02X}{rgb[1]:02X}{rgb[2]:02X}" shd.set(qn("w:val"), "clear") shd.set(qn("w:color"), "auto") shd.set(qn("w:fill"), hex6) tcPr.append(shd) def set_cell_borders(cell, border_color="999999", border_sz="4"): tc = cell._tc tcPr = tc.get_or_add_tcPr() tcBorders = OxmlElement("w:tcBorders") for side in ["top","left","bottom","right"]: b = OxmlElement(f"w:{side}") b.set(qn("w:val"), "single") b.set(qn("w:sz"), border_sz) b.set(qn("w:space"), "0") b.set(qn("w:color"), border_color) tcBorders.append(b) tcPr.append(tcBorders) def para_spacing(para, before=0, after=60): pPr = para._p.get_or_add_pPr() spacing = OxmlElement("w:spacing") spacing.set(qn("w:before"), str(before)) spacing.set(qn("w:after"), str(after)) pPr.append(spacing) # ── Helper: coloured heading ────────────────────────────────────── def add_heading(text, level=1, color=NAVY, size=13, bold=True, before=120, after=40): p = doc.add_paragraph() para_spacing(p, before=before, after=after) run = p.add_run(text) run.bold = True run.font.color.rgb = color run.font.size = Pt(size) run.font.name = "Calibri" return p def add_sub(text, color=TEAL, size=10, bold=True): p = doc.add_paragraph() para_spacing(p, before=60, after=20) run = p.add_run(text) run.bold = bold run.font.color.rgb = color run.font.size = Pt(size) run.font.name = "Calibri" return p def add_body(text, bold_prefix=None, color=BLACK, size=9.5, indent=False): p = doc.add_paragraph() para_spacing(p, before=0, after=30) if indent: pPr = p._p.get_or_add_pPr() ind = OxmlElement("w:ind") ind.set(qn("w:left"), "200") pPr.append(ind) if bold_prefix: r = p.add_run(bold_prefix + " ") r.bold = True r.font.size = Pt(size) r.font.name = "Calibri" r.font.color.rgb = color r2 = p.add_run(text) r2.font.size = Pt(size) r2.font.name = "Calibri" r2.font.color.rgb = color return p def add_bullet(text, bold_prefix=None, level=0): p = doc.add_paragraph(style="List Bullet") para_spacing(p, before=0, after=18) if bold_prefix: r = p.add_run(bold_prefix + " ") r.bold = True r.font.size = Pt(9.5) r.font.name = "Calibri" r2 = p.add_run(text) r2.font.size = Pt(9.5) r2.font.name = "Calibri" return p def add_alert(text, color=RED, size=9.5): p = doc.add_paragraph() para_spacing(p, before=40, after=40) pPr = p._p.get_or_add_pPr() # left border pBdr = OxmlElement("w:pBdr") left = OxmlElement("w:left") left.set(qn("w:val"), "thick") left.set(qn("w:sz"), "18") left.set(qn("w:space"), "8") left.set(qn("w:color"), f"{color[0]:02X}{color[1]:02X}{color[2]:02X}") pBdr.append(left) pPr.append(pBdr) r = p.add_run(text) r.bold = True r.font.color.rgb = color r.font.size = Pt(size) r.font.name = "Calibri" return p # ══════════════════════════════════════════════════════════════════ # TITLE BLOCK # ══════════════════════════════════════════════════════════════════ title_p = doc.add_paragraph() para_spacing(title_p, before=0, after=20) tr = title_p.add_run("BRONCHIAL ASTHMA — Clinical Management Summary") tr.bold = True tr.font.size = Pt(15) tr.font.color.rgb = WHITE tr.font.name = "Calibri" # set para shading (navy banner) pPr2 = title_p._p.get_or_add_pPr() shd2 = OxmlElement("w:shd") shd2.set(qn("w:val"), "clear") shd2.set(qn("w:color"), "auto") shd2.set(qn("w:fill"), "1A355E") pPr2.append(shd2) title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER sub_p = doc.add_paragraph() para_spacing(sub_p, before=0, after=60) sr = sub_p.add_run("Based on Harrison's 22E (2025) · GINA 2026 · Murray & Nadel's · Fishman's") sr.italic = True sr.font.size = Pt(8.5) sr.font.color.rgb = RGBColor(0x55, 0x55, 0x55) sr.font.name = "Calibri" sub_p.alignment = WD_ALIGN_PARAGRAPH.CENTER # ══════════════════════════════════════════════════════════════════ # 1. ASSESSMENT OF CONTROL # ══════════════════════════════════════════════════════════════════ add_heading("1. ASSESSMENT OF SYMPTOM CONTROL (GINA 4-Question Tool)", level=1, size=11) tbl = doc.add_table(rows=1, cols=3) tbl.style = "Table Grid" hdr = tbl.rows[0].cells for i, txt in enumerate(["Question (past 4 weeks)", "Yes", "No"]): hdr[i].text = txt hdr[i].paragraphs[0].runs[0].bold = True hdr[i].paragraphs[0].runs[0].font.size = Pt(9) set_cell_bg(hdr[i], NAVY) hdr[i].paragraphs[0].runs[0].font.color.rgb = WHITE rows_data = [ "Daytime symptoms >2×/week?", "Night waking due to asthma?", "Reliever needed >2×/week?", "Any activity limitation?", ] for q in rows_data: row = tbl.add_row().cells row[0].text = q row[0].paragraphs[0].runs[0].font.size = Pt(9) set_cell_bg(row[1], RGBColor(0xF8,0xF8,0xF8)) set_cell_bg(row[2], RGBColor(0xF8,0xF8,0xF8)) # score legend score_row = tbl.add_row().cells score_row[0].merge(score_row[2]) score_row[0].text = "0 Yes = Well controlled | 1–2 Yes = Partly controlled | 3–4 Yes = Uncontrolled" sr2 = score_row[0].paragraphs[0].runs[0] sr2.bold = True sr2.font.size = Pt(9) set_cell_bg(score_row[0], RGBColor(0xE8,0xF4,0xFD)) doc.add_paragraph() # Risk factors box add_sub("Risk Factors for Poor Outcomes") risks = [ "Uncontrolled symptoms / ≥1 severe exacerbation in past year", "SABA overuse (>1 canister/month) — marker of poor control", "No ICS, poor adherence, or incorrect inhaler technique", "FEV1 <60% predicted or high bronchodilator reversibility", "Comorbidities: obesity, chronic rhinosinusitis, GERD, depression", "Smoking, occupational exposure, indoor allergens", "Blood eosinophilia / elevated FeNO", ] for r in risks: add_bullet(r) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # 2. SEVERITY CLASSIFICATION # ══════════════════════════════════════════════════════════════════ add_heading("2. SEVERITY CLASSIFICATION (GINA)", level=1, size=11) sev_tbl = doc.add_table(rows=1, cols=2) sev_tbl.style = "Table Grid" for i, h in enumerate(["Severity", "Definition"]): sev_tbl.rows[0].cells[i].text = h sev_tbl.rows[0].cells[i].paragraphs[0].runs[0].bold = True sev_tbl.rows[0].cells[i].paragraphs[0].runs[0].font.size = Pt(9) set_cell_bg(sev_tbl.rows[0].cells[i], TEAL) sev_tbl.rows[0].cells[i].paragraphs[0].runs[0].font.color.rgb = WHITE for sev, defn in [ ("Mild", "Well controlled on Step 1 or Step 2"), ("Moderate", "Well controlled on Step 3"), ("Severe", "Requires Step 4–5 to maintain control, OR uncontrolled despite Step 4–5"), ]: row = sev_tbl.add_row().cells row[0].text = sev row[0].paragraphs[0].runs[0].font.size = Pt(9) row[0].paragraphs[0].runs[0].bold = True row[1].text = defn row[1].paragraphs[0].runs[0].font.size = Pt(9) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # 3. STEPWISE TREATMENT # ══════════════════════════════════════════════════════════════════ add_heading("3. STEPWISE PHARMACOLOGIC MANAGEMENT (Adults & Adolescents ≥12 yrs)", level=1, size=11) add_alert("⚑ GINA 2026 PARADIGM SHIFT: SABA monotherapy is NO LONGER preferred at any step.\n" " All patients should receive ICS-containing therapy. ICS-formoterol is the preferred reliever at every step.") doc.add_paragraph() step_tbl = doc.add_table(rows=1, cols=5) step_tbl.style = "Table Grid" step_hdrs = ["Step", "Severity", "Controller (Daily)", "Reliever (As-needed)", "Notes"] for i, h in enumerate(step_hdrs): c = step_tbl.rows[0].cells[i] c.text = h c.paragraphs[0].runs[0].bold = True c.paragraphs[0].runs[0].font.size = Pt(8.5) set_cell_bg(c, NAVY) c.paragraphs[0].runs[0].font.color.rgb = WHITE steps = [ ("1", "Intermittent", "None (AIR track)\nOR Low-dose ICS daily", "As-needed low-dose ICS-formoterol ★\nOR ICS-SABA combo (new 2026)\n[SABA alone: no longer preferred]", "AIR = Anti-Inflammatory Reliever strategy"), ("2", "Mild\nPersistent", "Low-dose ICS daily", "As-needed low-dose ICS-formoterol ★\nOR SABA + concurrent ICS\nAlt: LTRA if ICS intolerable", "Caution: montelukast — FDA black-box warning (suicidal ideation)"), ("3", "Moderate\nPersistent", "Low-dose ICS-LABA\n(preferred)\nAlt: Medium ICS or ICS+LTRA", "As-needed low-dose ICS-formoterol\n(MART strategy ★★)", "MART: same inhaler for maintenance + relief\n(budesonide-formoterol)"), ("4", "Severe", "Medium/High-dose ICS-LABA\n+ consider add-on LAMA", "As-needed low-dose ICS-formoterol", "Check adherence & inhaler technique first\nRefer to specialist"), ("5", "Very Severe\n/ Refractory", "High-dose ICS-LABA + LAMA\n+ Biologic therapy\n(see biologic table)", "As-needed ICS-formoterol", "Minimize/avoid maintenance OCS\nBronchial thermoplasty option"), ] row_fills = [RGBColor(0xEA,0xF4,0xEA), RGBColor(0xFF,0xFF,0xE8), RGBColor(0xFD,0xF0,0xD8), RGBColor(0xFD,0xE8,0xE8), RGBColor(0xF5,0xE8,0xF5)] for idx, (step, sev, ctrl, rel, note) in enumerate(steps): row = step_tbl.add_row().cells for ci, txt in enumerate([step, sev, ctrl, rel, note]): row[ci].text = txt for run in row[ci].paragraphs[0].runs: run.font.size = Pt(8.5) set_cell_bg(row[ci], row_fills[idx]) row[0].paragraphs[0].runs[0].bold = True row[0].paragraphs[0].runs[0].font.size = Pt(10) p_note = doc.add_paragraph() para_spacing(p_note, before=20, after=20) rn = p_note.add_run("★ ICS-formoterol (e.g., budesonide/formoterol) preferred reliever across all steps per GINA 2026 " "★★ MART = Maintenance And Reliever Therapy") rn.italic = True rn.font.size = Pt(8) rn.font.color.rgb = RGBColor(0x44,0x44,0x44) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # 4. DRUG CLASSES # ══════════════════════════════════════════════════════════════════ add_heading("4. KEY DRUG CLASSES", level=1, size=11) drug_tbl = doc.add_table(rows=1, cols=4) drug_tbl.style = "Table Grid" for i, h in enumerate(["Drug Class", "Examples", "Mechanism / Role", "Key Cautions"]): c = drug_tbl.rows[0].cells[i] c.text = h c.paragraphs[0].runs[0].bold = True c.paragraphs[0].runs[0].font.size = Pt(9) set_cell_bg(c, TEAL) c.paragraphs[0].runs[0].font.color.rgb = WHITE drug_rows = [ ("SABA", "Salbutamol, Terbutaline", "β2 activation → ↑cAMP → smooth muscle relaxation\nRelief/rescue; PRN use", "Overuse (>1 can/month) = poor control marker\nTachyphylaxis with scheduled use"), ("LABA", "Formoterol, Salmeterol, Indacaterol", "Long-acting β2 agonist; combine with ICS\nFormoterol: rapid onset → suitable for MART", "NEVER monotherapy in asthma\n(↑mortality — black box warning)"), ("ICS", "Budesonide, Fluticasone, Beclomethasone, Mometasone", "Suppress eosinophilic inflammation\nCornerstoneof all asthma therapy", "Oral candidiasis, dysphonia\n→ use spacer, rinse mouth after"), ("ICS-LABA\nCombination", "Budesonide/formoterol\nFluticasone/salmeterol\nFluticasone/vilanterol", "Dual controller; MART possible with\nbudesonide/formoterol", "Preferred combination at Steps 3–5"), ("LAMA", "Tiotropium (Respimat)\nUmeclidinium", "Long-acting muscarinic antagonist\nAdd-on at Steps 4–5", "Triple Rx (ICS-LABA+LAMA) < biologics\nfor reducing severe exacerbations"), ("LTRA", "Montelukast, Zafirlukast", "Leukotriene receptor antagonist\nAlt/add-on; useful in AERD, allergic rhinitis", "⚠ Montelukast: FDA black box — suicidal ideation/behaviour"), ("Theophylline", "Aminophylline (IV)\nTheophylline (oral)", "PDE inhibition → ↑cAMP; anti-inflammatory\nNarrow therapeutic window", "Serum level: 10–20 mcg/mL\nDrug interactions: fluoroquinolones, macrolides"), ("OCS", "Prednisolone\nMethylprednisolone (IV)", "Acute: short courses 5–7 days, 30–50 mg/day\nChronic: only if all else fails", "Minimize long-term use\nCumulative toxicity: T2DM, osteoporosis, cataracts"), ] alt_fill = [RGBColor(0xFF,0xFF,0xFF), RGBColor(0xF7,0xF9,0xFC)] for idx, row_data in enumerate(drug_rows): row = drug_tbl.add_row().cells for ci, txt in enumerate(row_data): row[ci].text = txt for run in row[ci].paragraphs[0].runs: run.font.size = Pt(8.5) set_cell_bg(row[ci], alt_fill[idx % 2]) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # 5. BIOLOGICS # ══════════════════════════════════════════════════════════════════ add_heading("5. BIOLOGIC AGENTS (Step 5 — Severe / Type 2 Asthma)", level=1, size=11) bio_tbl = doc.add_table(rows=1, cols=5) bio_tbl.style = "Table Grid" for i, h in enumerate(["Biologic", "Target", "Biomarker Threshold", "Age", "Notes"]): c = bio_tbl.rows[0].cells[i] c.text = h c.paragraphs[0].runs[0].bold = True c.paragraphs[0].runs[0].font.size = Pt(9) set_cell_bg(c, NAVY) c.paragraphs[0].runs[0].font.color.rgb = WHITE bio_rows = [ ("Omalizumab\n(biosimilar available)", "Anti-IgE", "Elevated serum IgE\n+ allergic sensitization", "≥6 yrs", "Allergic/atopic asthma\nMonthly or bimonthly SC"), ("Mepolizumab", "Anti-IL-5", "Blood eos ≥150–300/μL", "≥6 yrs", "Eosinophilic asthma\nMonthly SC"), ("Benralizumab", "Anti-IL-5Rα", "Blood eos ≥300/μL", "≥12 yrs", "Eosinophilic; Q8 weekly SC\nafter 3 monthly loading doses"), ("Dupilumab", "Anti-IL-4Rα\n(blocks IL-4 + IL-13)", "Blood eos ≥150/μL\nOR FeNO ≥25 ppb", "≥6 yrs", "Type 2 asthma; also approved\nfor COPD with eos + exacerbations"), ("Tezepelumab", "Anti-TSLP\n(upstream cytokine)", "Broad; works even without\nelevated T2 biomarkers", "≥12 yrs", "Severe asthma including\nnon-Type 2 phenotype"), ("Depemokimab ★NEW", "Anti-IL-5\n(ultra-long-acting)", "Blood eos ≥300/μL", "≥12 yrs\n(CRSwNP ≥18)", "NEW — GINA 2026\nDosing every 6 months SC"), ] for idx, row_data in enumerate(bio_rows): row = bio_tbl.add_row().cells for ci, txt in enumerate(row_data): row[ci].text = txt for run in row[ci].paragraphs[0].runs: run.font.size = Pt(8.5) set_cell_bg(row[ci], alt_fill[idx % 2]) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # 6. ACUTE EXACERBATION MANAGEMENT # ══════════════════════════════════════════════════════════════════ add_heading("6. ACUTE EXACERBATION MANAGEMENT", level=1, size=11) add_sub("Initial Assessment — PEFR/FEV1 Classification") pefr_tbl = doc.add_table(rows=1, cols=4) pefr_tbl.style = "Table Grid" for i, h in enumerate(["Severity", "PEFR", "Features", "Immediate Action"]): c = pefr_tbl.rows[0].cells[i] c.text = h c.paragraphs[0].runs[0].bold = True c.paragraphs[0].runs[0].font.size = Pt(9) set_cell_bg(c, TEAL) c.paragraphs[0].runs[0].font.color.rgb = WHITE pefr_rows = [ ("Mild–Moderate", ">60% predicted", "Wheeze, SOB; speaks in sentences\nSpO2 ≥92%", "Inhaled SABA q20 min ×3 (or ICS-formoterol)\nMonitor, OCS if no response in 1–2h"), ("Severe", "40–60% predicted", "Speaks in words; accessory muscle use\nSpO2 <92%", "SABA + ipratropium q20 min ×3\nSystemic OCS\nControlled O2 (target SpO2 93–95%)"), ("Life-Threatening", "<40% predicted", "Silent chest, cyanosis, altered consciousness\nPEFR <30% = ICU", "IV methylprednisolone\nIV/nebulized bronchodilators\nIV MgSO4 2g over 20 min\nConsider intubation"), ] exac_fills = [RGBColor(0xEA,0xF7,0xEA), RGBColor(0xFF,0xF3,0xCC), RGBColor(0xFD,0xE8,0xE8)] for idx, row_data in enumerate(pefr_rows): row = pefr_tbl.add_row().cells for ci, txt in enumerate(row_data): row[ci].text = txt for run in row[ci].paragraphs[0].runs: run.font.size = Pt(8.5) set_cell_bg(row[ci], exac_fills[idx]) doc.add_paragraph() add_alert("⚑ O2 THERAPY (GINA 2026): Supplemental O2 ONLY if SpO2 <92%\n" " Target: 93–95% (adults, adolescents, children 6–11 yrs) | ≥92% (children ≤5 yrs)\n" " ⚑ ANAPHYLAXIS + ASTHMA: Give EPINEPHRINE first, then bronchodilators") doc.add_paragraph() add_sub("ICU / Ventilated Patient") icu_bullets = [ "Permissive hypercapnia acceptable (avoid barotrauma)", "Low tidal volumes 5–7 mL/kg ideal body weight", "Prolonged expiratory time; minimise auto-PEEP", "Heliox (He:O2 70:30) — reduces turbulent airflow in critical obstruction", "IV ketamine: bronchodilatory anaesthetic induction agent", "Avoid propofol-only without ICS if reactive airways", ] for b in icu_bullets: add_bullet(b) doc.add_paragraph() add_sub("Discharge Criteria") dc = [ "PEFR ≥60% predicted (ideally ≥70–80%)", "Symptoms controlled; no nocturnal symptoms", "Inhaler technique verified", "Written asthma action plan provided", "Follow-up arranged within 1–2 weeks", "Prednisolone course completed or continued as prescribed", ] for d in dc: add_bullet(d) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # 7. GINA 2026 KEY UPDATES # ══════════════════════════════════════════════════════════════════ add_heading("7. GINA 2026 — KEY NEW UPDATES (May 5, 2026)", level=1, size=11, color=GREEN, before=80) updates = [ ("ICS-SABA combo at Step 1:", "Added as Track 2 (AIR) alternative to ICS-formoterol at Step 1"), ("ICS-formoterol in mild exacerbations:", "Now an alternate to inhaled SABA in primary care / ED (adults, adolescents, children 6–11)"), ("Depemokimab:", "New biologic (anti-IL-5, every-6-month dosing) added for severe eosinophilic asthma ≥12 yrs"), ("Biosimilar omalizumab:", "Generic anti-IgE added as biologic option"), ("O2 thresholds revised:", "O2 only if SpO2 <92%; target 93–95% adults; ≥92% for children ≤5 yrs"), ("Anaphylaxis protocol:", "If anaphylaxis + asthma co-present → epinephrine FIRST, then bronchodilators"), ("Triple therapy clarification:", "ICS-LABA + LAMA reduces exacerbations vs ICS-LABA, but far less than biologics"), ("OCS minimization:", "Strongly emphasized — optimize ICS-LABA, modifiable factors, and biologics before maintenance OCS"), ("New assessment tools:", "CAAT, Peds-AIRQ, PRAM added to guideline toolkit"), ("Vaccines:", "Evidence updated for influenza, RSV, and COVID-19 vaccines in asthma"), ("GLP-1 receptor agonists:", "Observational data added on potential role in improving asthma outcomes (future direction)"), ("New flowcharts:", "4 new flowcharts for primary care / ED management of exacerbations by age group"), ] for bold_t, body_t in updates: add_bullet(body_t, bold_prefix=bold_t) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # 8. SPECIAL POPULATIONS # ══════════════════════════════════════════════════════════════════ add_heading("8. SPECIAL POPULATIONS", level=1, size=11) sp_tbl = doc.add_table(rows=1, cols=2) sp_tbl.style = "Table Grid" for i, h in enumerate(["Population", "Key Considerations"]): c = sp_tbl.rows[0].cells[i] c.text = h c.paragraphs[0].runs[0].bold = True c.paragraphs[0].runs[0].font.size = Pt(9) set_cell_bg(c, TEAL) c.paragraphs[0].runs[0].font.color.rgb = WHITE sp_rows = [ ("Elderly (>65 yrs)", "5× higher mortality than younger patients\nAdjust β2-agonist dose for arrhythmia/tremulousness\nAlways consider COPD coexistence\nNew-onset: ~50% are/were smokers; ~25% occupational aetiology"), ("Asthma-COPD Overlap", "Smoking blunts ICS response\nDupilumab approved in COPD with eos + recurrent exacerbations\nEarlier initiation of LAMAs beneficial"), ("Severe Refractory\n(~5–10% of asthma)", "Uncontrolled on Step 5 after confirming adherence, technique, and treating comorbidities\n>50% have persistent eosinophilic inflammation\nMacrolides useful in neutrophilic/non-eosinophilic subset\nBronchial thermoplasty: bronchoscopic option — reduces airway smooth muscle mass"), ("Pregnancy", "Uncontrolled asthma more dangerous than ICS therapy\nContinue ICS; avoid stepping down\nSalbutamol safe as reliever; budesonide preferred ICS"), ("Exercise-Induced", "Pre-exercise SABA or ICS-formoterol 15 min before\nRegular ICS reduces EIB\nLTRA useful adjunct"), ("Aspirin/NSAID-Exacerbated\n(AERD)", "Avoid NSAIDs, aspirin\nLTRA particularly useful\nAspirin desensitisation under specialist supervision"), ] for idx, row_data in enumerate(sp_rows): row = sp_tbl.add_row().cells for ci, txt in enumerate(row_data): row[ci].text = txt for run in row[ci].paragraphs[0].runs: run.font.size = Pt(8.5) set_cell_bg(row[ci], alt_fill[idx % 2]) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # 9. NON-PHARMACOLOGIC / MONITORING # ══════════════════════════════════════════════════════════════════ add_heading("9. NON-PHARMACOLOGIC MEASURES & MONITORING", level=1, size=11) left_items = [ ("Trigger avoidance:", "Remove pets, occupational antigens, second-hand smoke, cannabis"), ("Allergen immunotherapy:", "Mild–moderate allergic asthma; SCIT under controlled conditions"), ("Vaccinations:", "Annual influenza; pneumococcal; COVID-19; RSV"), ("Inhaler education:", "Technique check at every visit — poor technique in up to 50% of 'refractory' cases"), ("Written action plan:", "Green/yellow/red zones based on PEFR or symptoms"), ("FeNO monitoring:", "≥25 ppb = eosinophilic inflammation; predicts ICS response"), ("Weight loss / OSA:", "Obesity worsens control; screen and treat OSA"), ("Rhinosinusitis / GERD:", "Manage upper airway disease and reflux as asthma comorbidities"), ] for bold_t, body_t in left_items: add_bullet(body_t, bold_prefix=bold_t) doc.add_paragraph() # ══════════════════════════════════════════════════════════════════ # FOOTER # ══════════════════════════════════════════════════════════════════ footer_p = doc.add_paragraph() para_spacing(footer_p, before=60, after=0) pPr3 = footer_p._p.get_or_add_pPr() shd3 = OxmlElement("w:shd") shd3.set(qn("w:val"), "clear") shd3.set(qn("w:color"), "auto") shd3.set(qn("w:fill"), "1A355E") pPr3.append(shd3) footer_r = footer_p.add_run( "Sources: Harrison's Principles of Internal Medicine 22E (2025, McGraw-Hill) · " "GINA 2026 Strategy Report (May 5 2026, ginasthma.org) · " "Murray & Nadel's Textbook of Respiratory Medicine · " "Fishman's Pulmonary Diseases & Disorders | Prepared: August 2026" ) footer_r.font.size = Pt(7.5) footer_r.font.color.rgb = WHITE footer_r.font.name = "Calibri" footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER # ── Save ────────────────────────────────────────────────────────── out = "/tmp/workspace/asthma-summary/Asthma_Clinical_Summary.docx" doc.save(out) print(f"SAVED: {out}") PYEOF python3 /tmp/workspace/asthma-summary/create_doc.py
python3 -c " from docx import Document doc = Document('/tmp/workspace/asthma-summary/Asthma_Clinical_Summary.docx') headings = [p.text for p in doc.paragraphs if p.text.strip() and len(p.text) > 5][:30] print('Paragraphs preview:') for h in headings: print(' -', h[:90]) import os size = os.path.getsize('/tmp/workspace/asthma-summary/Asthma_Clinical_Summary.docx') print(f'File size: {size:,} bytes') "
Asthma Clinical Summary
Word Document · DOCX
| Section | Contents |
|---|---|
| 1. Symptom Control Assessment | GINA 4-question tool table + risk factors for poor outcomes |
| 2. Severity Classification | Mild / Moderate / Severe defined by treatment step |
| 3. Stepwise Treatment | Step 1-5 table with colour-coded rows — controller, reliever, and notes per step; GINA 2026 paradigm shift callout box |
| 4. Drug Classes | SABA, LABA, ICS, ICS-LABA, LAMA, LTRA, Theophylline, OCS — mechanism, examples, key cautions |
| 5. Biologics | All 6 agents (incl. Depemokimab - new 2026) — target, biomarker threshold, age, notes |
| 6. Acute Exacerbation | PEFR severity table, revised O2 targets, ICU/ventilation tips, discharge criteria |
| 7. GINA 2026 Updates | 12 key changes highlighted with bold prefixes |
| 8. Special Populations | Elderly, asthma-COPD overlap, refractory, pregnancy, exercise-induced, AERD |
| 9. Non-pharmacologic & Monitoring | Trigger avoidance, vaccination, FeNO, written action plans, comorbidities |
Provide a simplified flowchart for the above information for clinical usee
mkdir -p /tmp/workspace/asthma-summary && python3 -c "import matplotlib; print(matplotlib.__version__)"
~/asthma-summary/flowchart.py
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
# ── Canvas ────────────────────────────────────────────────────────
fig = plt.figure(figsize=(22, 30), facecolor="#F8FAFC")
ax = fig.add_axes([0, 0, 1, 1])
ax.set_xlim(0, 22)
ax.set_ylim(0, 30)
ax.axis("off")
# ── Palette ───────────────────────────────────────────────────────
NAVY = "#1A355E"
TEAL = "#007A87"
GREEN = "#1E7A45"
AMBER = "#E67E22"
RED = "#C0392B"
PURPLE = "#6C3483"
LGREY = "#EAF0F6"
MGREY = "#D5E8F0"
LYELLOW = "#FEF9E7"
LGREEN = "#E9F7EF"
LORANGE = "#FDF2E9"
LRED = "#FDEDEC"
LPURPLE = "#F4ECF7"
WHITE = "#FFFFFF"
DARK = "#1C1C1C"
# ── Helper functions ──────────────────────────────────────────────
def box(ax, x, y, w, h, text, bg=LGREY, border=NAVY, fontsize=8.5,
bold=False, text_color=DARK, radius=0.25, alpha=1.0, linew=1.5,
va="center", ha="center", wrap=False):
rect = FancyBboxPatch((x - w/2, y - h/2), w, h,
boxstyle=f"round,pad=0.05,rounding_size={radius}",
facecolor=bg, edgecolor=border,
linewidth=linew, zorder=3, alpha=alpha)
ax.add_patch(rect)
weight = "bold" if bold else "normal"
ax.text(x, y, text, ha=ha, va=va, fontsize=fontsize,
color=text_color, fontweight=weight, zorder=4,
wrap=wrap, multialignment="center",
linespacing=1.35)
def header_box(ax, x, y, w, h, text, bg=NAVY, fontsize=10, text_color=WHITE):
rect = FancyBboxPatch((x - w/2, y - h/2), w, h,
boxstyle="round,pad=0.05,rounding_size=0.3",
facecolor=bg, edgecolor=bg,
linewidth=2, zorder=3)
ax.add_patch(rect)
ax.text(x, y, text, ha="center", va="center", fontsize=fontsize,
color=text_color, fontweight="bold", zorder=4,
multialignment="center", linespacing=1.3)
def arrow(ax, x1, y1, x2, y2, color=NAVY, lw=1.8, style="->"):
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle=style, color=color,
lw=lw, connectionstyle="arc3,rad=0.0"),
zorder=2)
def label_arrow(ax, x1, y1, x2, y2, label, color=AMBER, fontsize=7.5, lw=1.8):
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle="->", color=color, lw=lw),
zorder=2)
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx+0.08, my, label, fontsize=fontsize, color=color,
fontweight="bold", zorder=5)
def diamond(ax, x, y, w, h, text, bg=LYELLOW, border=AMBER, fontsize=8):
dx, dy = w/2, h/2
pts = [(x, y+dy), (x+dx, y), (x, y-dy), (x-dx, y)]
from matplotlib.patches import Polygon
poly = Polygon(pts, closed=True, facecolor=bg, edgecolor=border,
linewidth=2, zorder=3)
ax.add_patch(poly)
ax.text(x, y, text, ha="center", va="center", fontsize=fontsize,
fontweight="bold", color=DARK, zorder=4, multialignment="center",
linespacing=1.3)
def divider(ax, y, color="#CCCCCC"):
ax.plot([0.3, 21.7], [y, y], color=color, lw=0.8, ls="--", zorder=1)
# ══════════════════════════════════════════════════════════════════
# TITLE
# ══════════════════════════════════════════════════════════════════
header_box(ax, 11, 29.3, 21.4, 0.9,
"BRONCHIAL ASTHMA — Clinical Management Flowchart\n"
"Harrison's 22E (2025) · GINA 2026 · Murray & Nadel's",
bg=NAVY, fontsize=10.5)
# ══════════════════════════════════════════════════════════════════
# SECTION A — DIAGNOSIS & CONTROL ASSESSMENT
# ══════════════════════════════════════════════════════════════════
header_box(ax, 11, 28.5, 21.4, 0.5,
"STEP 1 — DIAGNOSIS & CONTROL ASSESSMENT", bg=TEAL, fontsize=9.5)
# --- GINA 4-question tool
box(ax, 5.5, 27.3, 10.0, 1.4,
"GINA SYMPTOM CONTROL (past 4 weeks)\n"
"① Daytime symptoms >2×/week?\n"
"② Any night waking due to asthma?\n"
"③ Reliever needed >2×/week?\n"
"④ Any activity limitation?",
bg=LGREY, border=TEAL, fontsize=8, bold=False)
# --- Scoring
box(ax, 5.5, 25.85, 9.8, 0.9,
"0 Yes = Well controlled | 1–2 Yes = Partly controlled | 3–4 Yes = Uncontrolled",
bg=MGREY, border=TEAL, fontsize=8.5, bold=True)
arrow(ax, 5.5, 26.6, 5.5, 26.3)
# --- Severity classification (right column)
box(ax, 16.8, 27.0, 8.2, 1.8,
"SEVERITY (GINA)\n\n"
" Mild → Controlled at Step 1–2\n"
" Moderate → Controlled at Step 3\n"
" Severe → Requires Step 4–5",
bg=LGREEN, border=GREEN, fontsize=8.5, bold=False)
# connector between two panels
ax.plot([10.5, 12.5], [27.3, 27.3], color=TEAL, lw=1.2, ls="--", zorder=2)
ax.plot([12.5, 12.5], [27.3, 27.05], color=TEAL, lw=1.2, ls="--", zorder=2)
arrow(ax, 12.5, 27.05, 12.7, 27.05)
divider(ax, 25.35)
# ══════════════════════════════════════════════════════════════════
# SECTION B — NON-PHARMACOLOGIC + CHECK BEFORE STEPPING UP
# ══════════════════════════════════════════════════════════════════
header_box(ax, 11, 25.1, 21.4, 0.45,
"STEP 2 — NON-PHARMACOLOGIC MEASURES (ALL PATIENTS)", bg=GREEN, fontsize=9)
box(ax, 7.0, 24.15, 13.0, 1.3,
"• Remove triggers: allergens, smoke, occupational antigens, pets\n"
"• Vaccinate: annual influenza · pneumococcal · COVID-19 · RSV\n"
"• Allergen immunotherapy (mild–moderate allergic asthma, controlled)\n"
"• Treat comorbidities: rhinosinusitis · GERD · obesity · OSA · anxiety",
bg=LGREEN, border=GREEN, fontsize=8.2)
box(ax, 18.5, 24.15, 7.0, 1.3,
"CHECK FIRST before stepping up:\n"
"✓ Inhaler technique correct?\n"
"✓ Adherence adequate?\n"
"✓ Comorbidities treated?",
bg=LYELLOW, border=AMBER, fontsize=8.2)
arrow(ax, 11, 25.1, 11, 24.8)
divider(ax, 23.5)
# ══════════════════════════════════════════════════════════════════
# SECTION C — STEPWISE PHARMACOTHERAPY
# ══════════════════════════════════════════════════════════════════
header_box(ax, 11, 23.25, 21.4, 0.45,
"STEP 3 — STEPWISE PHARMACOTHERAPY (Adults & Adolescents ≥12 yrs)", bg=NAVY, fontsize=9.5)
arrow(ax, 11, 23.5, 11, 23.25)
# GINA 2026 alert banner
box(ax, 11, 22.85, 21.0, 0.6,
"⚑ GINA 2026 SHIFT: SABA alone NO LONGER preferred at any step."
" All patients need ICS-containing therapy."
" ICS-formoterol = preferred reliever at ALL steps.",
bg="#FDEDEC", border=RED, fontsize=8.5, bold=True, text_color=RED)
# --- Step labels (vertical cascade, left column)
step_data = [
# (y_center, step_label, bg, border, controller, reliever, note_color, note)
(21.85, "STEP 1\nIntermittent", "#E8F8F5", TEAL,
"No daily controller\n(AIR track)\nOR Low-dose ICS daily",
"★ As-needed ICS-formoterol\n (preferred)\nOR ICS-SABA combo (new 2026)\n [not SABA alone]",
TEAL, ""),
(20.35, "STEP 2\nMild\nPersistent", "#EAF4FB", "#2980B9",
"Low-dose ICS daily\n(Alt: LTRA if ICS intolerable)",
"★ As-needed ICS-formoterol\nOR SABA + concurrent ICS\n⚠ Montelukast: FDA black-box\n (suicidal ideation)",
"#2980B9", ""),
(18.70, "STEP 3\nModerate\nPersistent", "#FEF9E7", AMBER,
"Low-dose ICS-LABA\n(preferred)\nAlt: Medium ICS or ICS+LTRA",
"★★ MART strategy\n (ICS-formoterol for\n maintenance + relief)",
AMBER, ""),
(17.10, "STEP 4\nSevere", "#FDF2E9", "#E67E22",
"Medium/High ICS-LABA\n+ Add LAMA (tiotropium)\nif uncontrolled",
"As-needed ICS-formoterol\n→ Refer to specialist\n→ Check adherence + technique",
"#E67E22", ""),
(15.50, "STEP 5\nVery Severe /\nRefractory", "#FDEDEC", RED,
"High-dose ICS-LABA + LAMA\n+ BIOLOGIC THERAPY\n(see biologic table below)",
"As-needed ICS-formoterol\nMinimize / avoid OCS\n[Bronchial thermoplasty: option]",
RED, ""),
]
for (yc, step_lbl, bg_c, brd, ctrl, rel, nc, note) in step_data:
# Step badge
box(ax, 1.6, yc, 2.5, 1.1, step_lbl,
bg=brd, border=brd, bold=True, fontsize=9, text_color=WHITE)
# Controller
box(ax, 7.2, yc, 8.8, 1.1, "CONTROLLER (Daily)\n" + ctrl,
bg=bg_c, border=brd, fontsize=7.8)
# Reliever
box(ax, 16.5, yc, 8.8, 1.1, "RELIEVER (As-needed)\n" + rel,
bg=bg_c, border=brd, fontsize=7.8)
# down arrows between steps
if yc > 15.5:
arrow(ax, 1.6, yc - 0.55, 1.6, yc - 1.05, color=brd, lw=2.0)
# "step up" label on left
for yc in [20.35, 18.70, 17.10, 15.50]:
ax.text(0.35, yc + 0.55, "▲ Step\nUp", fontsize=7, color="#888888",
ha="center", va="center", rotation=0)
# Step-down annotation
ax.text(0.35, 21.35, "▼ Step\nDown\nif controlled\n3+ months",
fontsize=6.5, color=GREEN, ha="center", va="center")
divider(ax, 14.85)
# ══════════════════════════════════════════════════════════════════
# SECTION D — BIOLOGICS
# ══════════════════════════════════════════════════════════════════
header_box(ax, 11, 14.6, 21.4, 0.45,
"STEP 3b — BIOLOGIC AGENTS (Step 5 — Severe / Type 2 Asthma)", bg=PURPLE, fontsize=9.5)
bio_data = [
("Omalizumab\n(+ biosimilar)\n★ Anti-IgE", "Elevated IgE\n+ allergic sensitisation", "Allergic\nasthma\n≥6 yrs", "#F4ECF7", PURPLE),
("Mepolizumab\n★ Anti-IL-5", "Blood eos\n≥150–300/μL", "Eosinophilic\nasthma\n≥6 yrs", "#EBF5FB", "#2980B9"),
("Benralizumab\n★ Anti-IL-5Rα", "Blood eos\n≥300/μL", "Eosinophilic\nasthma\n≥12 yrs","#EBF5FB","#1A5276"),
("Dupilumab\n★ Anti-IL-4Rα\n(IL-4 + IL-13)", "Blood eos ≥150/μL\nOR FeNO ≥25 ppb", "Type 2\nasthma\n≥6 yrs", "#E9F7EF", GREEN),
("Tezepelumab\n★ Anti-TSLP", "Broad — works even\nwithout T2 markers", "Severe asthma\nincl. non-T2\n≥12 yrs","#FEF9E7",AMBER),
("Depemokimab\n★ Anti-IL-5 (NEW)\nEvery 6 months", "Blood eos\n≥300/μL", "Eosinophilic\nasthma\n≥12 yrs","#FDEDEC", RED),
]
bx_w = 3.2
bx_gap = 3.53
bx_start = 1.85
bx_y = 13.55
for i, (name, marker, age, bg_b, brd_b) in enumerate(bio_data):
bx = bx_start + i * bx_gap
box(ax, bx, bx_y, bx_w, 1.8,
name + "\n─────────\n" + marker + "\n─────────\n" + age,
bg=bg_b, border=brd_b, fontsize=7.5)
ax.text(11, 12.6, "★ NEW — GINA 2026: Depemokimab added | Biosimilar omalizumab added | "
"GLP-1 agonists: potential future role (observational data)",
ha="center", va="center", fontsize=7.5, color=PURPLE,
fontstyle="italic")
divider(ax, 12.25)
# ══════════════════════════════════════════════════════════════════
# SECTION E — ACUTE EXACERBATION
# ══════════════════════════════════════════════════════════════════
header_box(ax, 11, 12.05, 21.4, 0.45,
"STEP 4 — ACUTE EXACERBATION MANAGEMENT", bg=RED, fontsize=9.5)
# ---- 3 severity columns ----
sev_cols = [
(4.0, "MILD – MODERATE\nPEFR >60%",
"• SpO₂ ≥92%; speaks sentences\n"
"• SABA q20 min ×3\n"
" (or ICS-formoterol — new 2026)\n"
"• If no improvement in 1–2h:\n"
" → Add ipratropium\n"
" → Oral prednisolone 30–50 mg\n"
"• Observe; discharge if PEFR ≥60%",
LGREEN, GREEN),
(11.0, "SEVERE\nPEFR 40–60%",
"• SpO₂ <92%; speaks in words\n"
"• Nebulised SABA + ipratropium\n"
" q20 min ×3\n"
"• Systemic OCS (IV or oral)\n"
"• Controlled O₂ → target 93–95%\n"
" (give O₂ ONLY if SpO₂ <92%)\n"
"• Admit if no rapid improvement",
LORANGE, AMBER),
(18.0, "LIFE-THREATENING\nPEFR <40% / Silent chest",
"• ICU admission\n"
"• IV methylprednisolone\n"
"• Continuous nebulised SABA\n"
"• IV MgSO₄ 2 g over 20 min\n"
"• Heliox (70:30) if available\n"
"• NIPPV (selected patients)\n"
"• Intubate if imminent resp. failure\n"
" (permissive hypercapnia strategy)",
LRED, RED),
]
for (bx, title, body, bg_e, brd_e) in sev_cols:
box(ax, bx, 10.4, 6.8, 3.0,
title + "\n──────────\n" + body,
bg=bg_e, border=brd_e, fontsize=7.8)
# Alert: O2 + anaphylaxis
box(ax, 11, 8.65, 20.8, 0.8,
"⚑ O₂ THERAPY (GINA 2026): Give supplemental O₂ ONLY if SpO₂ <92% — Target 93–95% (adults) / ≥92% (children ≤5 yrs)\n"
"⚑ ANAPHYLAXIS + ASTHMA: Give EPINEPHRINE FIRST → then bronchodilators",
bg="#FDEDEC", border=RED, fontsize=8.5, bold=True, text_color=RED)
divider(ax, 8.1)
# ══════════════════════════════════════════════════════════════════
# SECTION F — DISCHARGE + FOLLOW-UP
# ══════════════════════════════════════════════════════════════════
header_box(ax, 11, 7.9, 21.4, 0.45,
"STEP 5 — DISCHARGE CRITERIA & FOLLOW-UP", bg=GREEN, fontsize=9.5)
discharge_items = [
"PEFR ≥60% (ideally ≥70–80%)", "Symptoms controlled; no nocturnal symptoms",
"Inhaler technique verified", "Written asthma action plan given",
"Follow-up within 1–2 weeks", "Step-up therapy reviewed; OCS course completed"
]
cols_per_row = 3
box_w = 6.6
x_starts = [2.2, 9.0, 15.8]
for i, item in enumerate(discharge_items):
row_i = i // cols_per_row
col_i = i % cols_per_row
bx = x_starts[col_i]
by = 7.2 - row_i * 0.65
box(ax, bx, by, box_w, 0.5, "✓ " + item,
bg=LGREEN, border=GREEN, fontsize=8.2, ha="center")
divider(ax, 5.85)
# ══════════════════════════════════════════════════════════════════
# SECTION G — MONITORING LOOP
# ══════════════════════════════════════════════════════════════════
header_box(ax, 11, 5.65, 21.4, 0.45,
"ONGOING MONITORING (Every Visit)", bg=TEAL, fontsize=9.5)
monitor_items = [
("Symptom\nControl", "GINA 4Q tool\nevery visit", LGREY, TEAL),
("Spirometry /\nPEFR", "FEV1; reassess\nevery 3–12 months", "#EBF5FB", "#2980B9"),
("Inhaler\nTechnique", "Check & correct\nat every visit\n(50% have errors)", LYELLOW, AMBER),
("Adherence", "Address barriers;\nsimplify regimen", LGREEN, GREEN),
("FeNO", "≥25 ppb = eosinophilic\nInflammation; predicts\nICS response", LPURPLE, PURPLE),
("Step\nReview", "Step DOWN if\nwell controlled\n≥3 months", "#E8F8F5", TEAL),
]
mon_x = [2.0, 5.7, 9.4, 13.1, 16.8, 20.5]
for i, (title, body, bg_m, brd_m) in enumerate(monitor_items):
box(ax, mon_x[i], 4.8, 3.2, 1.45,
title + "\n─────\n" + body,
bg=bg_m, border=brd_m, fontsize=7.7)
# curved "reassess" arrow
ax.annotate("", xy=(0.7, 23.4), xytext=(0.7, 4.5),
arrowprops=dict(arrowstyle="->", color="#AAAAAA", lw=1.5,
connectionstyle="arc3,rad=-0.3"),
zorder=1)
ax.text(0.1, 14.0, "Reassess\n& adjust\ntherapy",
fontsize=7.5, color="#888888", ha="center", rotation=90)
divider(ax, 3.65)
# ══════════════════════════════════════════════════════════════════
# LEGEND
# ══════════════════════════════════════════════════════════════════
legend_items = [
(NAVY, "Main pathway"),
(GREEN, "Non-pharmacologic / discharge"),
(AMBER, "Moderate severity"),
(RED, "Severe / warning"),
(PURPLE, "Biologic therapy"),
(TEAL, "Assessment / monitoring"),
]
lx = 1.5
ax.text(lx, 3.4, "LEGEND:", fontsize=8, fontweight="bold", color=DARK)
for i, (col, lbl) in enumerate(legend_items):
cx = lx + i * 3.4
rect = FancyBboxPatch((cx, 2.85), 0.35, 0.3,
boxstyle="round,pad=0.02",
facecolor=col, edgecolor=col, zorder=3)
ax.add_patch(rect)
ax.text(cx + 0.5, 3.0, lbl, fontsize=7.5, color=DARK, va="center")
# ── Footer ─────────────────────────────────────────────────────────
footer_rect = FancyBboxPatch((0.15, 0.1), 21.7, 0.55,
boxstyle="round,pad=0.02",
facecolor=NAVY, edgecolor=NAVY, zorder=3)
ax.add_patch(footer_rect)
ax.text(11, 0.38,
"Sources: Harrison's Principles of Internal Medicine 22E (2025) · GINA 2026 Strategy Report (May 2026) · "
"Murray & Nadel's Textbook of Respiratory Medicine · Fishman's Pulmonary Diseases & Disorders | Prepared: August 2026",
ha="center", va="center", fontsize=7.5, color=WHITE, zorder=4)
# ── Key footnotes ─────────────────────────────────────────────────
ax.text(11, 2.35,
"★ ICS-formoterol (budesonide/formoterol) = preferred reliever at ALL steps (GINA 2026) "
"★★ MART = Maintenance And Reliever Therapy (same inhaler for maintenance + rescue) "
"AIR = Anti-Inflammatory Reliever strategy",
ha="center", va="center", fontsize=7.5, color=DARK, fontstyle="italic",
multialignment="center")
plt.tight_layout(pad=0)
out = "/tmp/workspace/asthma-summary/Asthma_Flowchart.png"
plt.savefig(out, dpi=180, bbox_inches="tight", facecolor="#F8FAFC")
print(f"SAVED: {out}")
import os; print(f"Size: {os.path.getsize(out):,} bytes")
cd /tmp/workspace/asthma-summary && python3 flowchart.py 2>&1
python3 -c " from PIL import Image img = Image.open('/tmp/workspace/asthma-summary/Asthma_Flowchart.png') print(f'Size: {img.size} Mode: {img.mode}') "
Asthma Flowchart
Image · PNG
| Panel | Contents |
|---|---|
| Step 1 — Diagnosis & Assessment | GINA 4-question control tool with scoring + Severity classification (Mild/Moderate/Severe) |
| Step 2 — Non-Pharmacologic | Trigger removal, vaccinations, immunotherapy, comorbidities; "Check before stepping up" checklist |
| Step 3 — Stepwise Therapy | Colour-coded rows for Steps 1–5 with controller and reliever columns; GINA 2026 paradigm shift alert banner; step-up/step-down arrows |
| Step 3b — Biologics | All 6 agents in individual cards (Omalizumab, Mepolizumab, Benralizumab, Dupilumab, Tezepelumab, Depemokimab) with target, biomarker threshold, and age |
| Step 4 — Acute Exacerbation | Three-column severity table (Mild-Moderate / Severe / Life-Threatening) with PEFR thresholds, actions, and revised GINA 2026 O2 targets; anaphylaxis protocol alert |
| Step 5 — Discharge | Six discharge criteria tick-boxes |
| Ongoing Monitoring | Six monitoring domains (symptoms, spirometry, inhaler technique, adherence, FeNO, step review) |
Make an interactive PowerPoint presentation with the stepwise management slides
~/asthma-summary/build_pptx.py
"""
Bronchial Asthma — Interactive Stepwise Management Presentation
python-pptx | 16:9 widescreen | interactive hyperlink navigation
Slides:
0 Title / Navigation Hub
1 Overview: Goals + Assessment
2 Step 1 — Intermittent
3 Step 2 — Mild Persistent
4 Step 3 — Moderate Persistent (MART)
5 Step 4 — Severe
6 Step 5 — Very Severe / Biologic
7 Acute Exacerbation Algorithm
8 Biologic Selection Guide
9 Monitoring & Step-Down
"""
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.oxml.ns import qn, nsmap
from pptx.oxml import parse_xml
from pptx.util import Inches, Pt
from lxml import etree
from io import BytesIO
import copy, textwrap
# ── Slide dimensions 16:9 ────────────────────────────────────────
W = Inches(13.333)
H = Inches(7.5)
prs = Presentation()
prs.slide_width = W
prs.slide_height = H
BLANK = prs.slide_layouts[6]
# ── Colour palette ────────────────────────────────────────────────
NAVY = RGBColor(0x1A, 0x35, 0x5E)
TEAL = RGBColor(0x00, 0x7A, 0x87)
GREEN = RGBColor(0x1E, 0x7A, 0x45)
AMBER = RGBColor(0xE6, 0x7E, 0x22)
RED = RGBColor(0xC0, 0x39, 0x2B)
PURPLE = RGBColor(0x6C, 0x34, 0x83)
BLUE = RGBColor(0x29, 0x80, 0xB9)
LGREY = RGBColor(0xF0, 0xF4, 0xF8)
MGREY = RGBColor(0xD5, 0xE8, 0xF0)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
DARK = RGBColor(0x1C, 0x1C, 0x1C)
LGREEN = RGBColor(0xE9, 0xF7, 0xEF)
LYELLOW = RGBColor(0xFE, 0xF9, 0xE7)
LORANGE = RGBColor(0xFD, 0xF2, 0xE9)
LRED = RGBColor(0xFD, 0xED, 0xEC)
LPURPLE = RGBColor(0xF4, 0xEC, 0xF7)
LBLUE = RGBColor(0xEB, 0xF5, 0xFB)
# step accent colours
STEP_COLORS = {
1: (TEAL, RGBColor(0xE0,0xF4,0xF6), "STEP 1"),
2: (BLUE, LBLUE, "STEP 2"),
3: (AMBER, LYELLOW, "STEP 3"),
4: (RGBColor(0xE6,0x7E,0x22), LORANGE, "STEP 4"),
5: (RED, LRED, "STEP 5"),
}
# ── Helper: add slide ─────────────────────────────────────────────
def new_slide():
return prs.slides.add_slide(BLANK)
# ── Helper: solid background ──────────────────────────────────────
def set_bg(slide, color: RGBColor):
bg = slide.background
fill = bg.fill
fill.solid()
fill.fore_color.rgb = color
# ── Helper: rectangle ────────────────────────────────────────────
def add_rect(slide, x, y, w, h, fill_color, line_color=None, line_w=Pt(0)):
from pptx.util import Emu
shape = slide.shapes.add_shape(
1, # MSO_SHAPE_TYPE.RECTANGLE
x, y, w, h
)
shape.fill.solid()
shape.fill.fore_color.rgb = fill_color
if line_color:
shape.line.color.rgb = line_color
shape.line.width = line_w
else:
shape.line.fill.background()
return shape
# ── Helper: rounded rect ─────────────────────────────────────────
def add_rrect(slide, x, y, w, h, fill_color, line_color=None, line_w=Pt(1.5), radius_pct=5):
"""Add a rounded rectangle via XML tweak."""
shape = slide.shapes.add_shape(5, x, y, w, h) # 5 = ROUNDED_RECTANGLE
shape.fill.solid()
shape.fill.fore_color.rgb = fill_color
if line_color:
shape.line.color.rgb = line_color
shape.line.width = line_w
else:
shape.line.fill.background()
# set corner rounding
sp = shape._element
spPr = sp.find(qn('p:spPr'))
prstGeom = spPr.find(qn('a:prstGeom'))
if prstGeom is not None:
avLst = prstGeom.find(qn('a:avLst'))
if avLst is None:
avLst = etree.SubElement(prstGeom, qn('a:avLst'))
gd = etree.SubElement(avLst, qn('a:gd'))
gd.set('name', 'adj')
gd.set('fmla', f'val {radius_pct * 1000}')
return shape
# ── Helper: textbox ───────────────────────────────────────────────
def add_tb(slide, x, y, w, h, text, size=Pt(11), color=DARK,
bold=False, align=PP_ALIGN.LEFT, wrap=True,
italic=False, anchor=MSO_ANCHOR.TOP):
tb = slide.shapes.add_textbox(x, y, w, h)
tf = tb.text_frame
tf.word_wrap = wrap
tf.auto_size = None
tf.vertical_anchor = anchor
tf.margin_left = Inches(0.05)
tf.margin_right = Inches(0.05)
tf.margin_top = Inches(0.03)
tf.margin_bottom= Inches(0.03)
p = tf.paragraphs[0]
p.alignment = align
r = p.add_run()
r.text = text
r.font.size = size
r.font.color.rgb = color
r.font.bold = bold
r.font.italic = italic
r.font.name = "Calibri"
return tb, tf
def add_tb_lines(slide, x, y, w, h, lines, default_size=Pt(10),
default_color=DARK, anchor=MSO_ANCHOR.TOP, align=PP_ALIGN.LEFT):
"""lines = list of (text, size, color, bold, italic) tuples"""
tb = slide.shapes.add_textbox(x, y, w, h)
tf = tb.text_frame
tf.word_wrap = True
tf.auto_size = None
tf.vertical_anchor = anchor
tf.margin_left = Inches(0.06)
tf.margin_right = Inches(0.06)
tf.margin_top = Inches(0.04)
tf.margin_bottom = Inches(0.04)
first = True
for line in lines:
text = line[0]
size = line[1] if len(line) > 1 else default_size
col = line[2] if len(line) > 2 else default_color
bold = line[3] if len(line) > 3 else False
ital = line[4] if len(line) > 4 else False
if first:
para = tf.paragraphs[0]
first = False
else:
para = tf.add_paragraph()
para.alignment = align
para.space_after = Pt(2)
para.space_before = Pt(1)
run = para.add_run()
run.text = text
run.font.size = size
run.font.color.rgb = col
run.font.bold = bold
run.font.italic = ital
run.font.name = "Calibri"
return tb, tf
# ── Helper: hyperlink on shape ────────────────────────────────────
def add_hyperlink(shape, slide_index, prs):
"""Add a slide-jump hyperlink to a shape (click action)."""
target_slide = prs.slides[slide_index]
rId = shape.part.relate_to(
target_slide.part,
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide'
)
sp = shape._element
spPr_tag = sp.find(qn('p:spPr'))
# Build <p:sp><p:nvSpPr><p:cNvPr> click action via hlinkClick
nvSpPr = sp.find(qn('p:nvSpPr'))
nvPr = nvSpPr.find(qn('p:nvPr'))
if nvPr is None:
nvPr = etree.SubElement(nvSpPr, qn('p:nvPr'))
hlinkMouseClick = etree.SubElement(nvPr, qn('p:hlinkMouseClick'))
hlinkMouseClick.set(qn('r:id'), rId)
# ── Helper: nav button ─────────────────────────────────────────────
def nav_btn(slide, x, y, label, target_idx, bg=NAVY, fg=WHITE, size=Pt(8.5)):
w, h = Inches(1.35), Inches(0.32)
shape = add_rrect(slide, x, y, w, h, bg, line_color=None, radius_pct=8)
tb = slide.shapes.add_textbox(x, y, w, h)
tf = tb.text_frame
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = 0
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
r = p.add_run()
r.text = label
r.font.size = size
r.font.color.rgb = fg
r.font.bold = True
r.font.name = "Calibri"
add_hyperlink(shape, target_idx, prs)
return shape
# ── Helper: header bar ────────────────────────────────────────────
def header_bar(slide, title, subtitle="", bg=NAVY):
# full-width bar at top
add_rect(slide, 0, 0, W, Inches(1.05), bg)
add_tb(slide, Inches(0.3), Inches(0.05), Inches(10), Inches(0.55),
title, size=Pt(22), color=WHITE, bold=True, align=PP_ALIGN.LEFT)
if subtitle:
add_tb(slide, Inches(0.3), Inches(0.6), Inches(10), Inches(0.38),
subtitle, size=Pt(10), color=RGBColor(0xB8,0xCC,0xE4),
align=PP_ALIGN.LEFT, italic=True)
def source_bar(slide):
add_rect(slide, 0, Inches(7.2), W, Inches(0.3), NAVY)
add_tb(slide, Inches(0.2), Inches(7.21), Inches(13), Inches(0.25),
"Sources: Harrison's 22E (2025) · GINA 2026 · Murray & Nadel's · Fishman's Pulmonary",
size=Pt(7), color=RGBColor(0xAA,0xBB,0xCC), align=PP_ALIGN.LEFT)
# ── Helper: step badge ────────────────────────────────────────────
def step_badge(slide, x, y, step_num):
ac, _, lbl = STEP_COLORS[step_num]
w, h = Inches(1.1), Inches(1.0)
shape = add_rrect(slide, x, y, w, h, ac, radius_pct=10)
add_tb(slide, x, y, w, h,
f"STEP\n{step_num}", size=Pt(14), color=WHITE, bold=True,
align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
# ── Helper: info card ────────────────────────────────────────────
def info_card(slide, x, y, w, h, title, body_lines, title_bg=TEAL, body_bg=LGREY, accent=None):
# title strip
th = Inches(0.38)
add_rrect(slide, x, y, w, th, title_bg, radius_pct=5)
add_tb(slide, x + Inches(0.06), y, w - Inches(0.1), th,
title, size=Pt(9.5), color=WHITE, bold=True,
align=PP_ALIGN.LEFT, anchor=MSO_ANCHOR.MIDDLE)
# body
bh = h - th
add_rrect(slide, x, y + th, w, bh, body_bg, line_color=title_bg, line_w=Pt(1), radius_pct=3)
line_objs = [(ln, Pt(8.5), DARK, False, False) for ln in body_lines]
add_tb_lines(slide, x + Inches(0.08), y + th + Inches(0.04),
w - Inches(0.12), bh - Inches(0.06), line_objs)
# ══════════════════════════════════════════════════════════════════
# SLIDE INDEX CONSTANTS (0-based)
# ══════════════════════════════════════════════════════════════════
S_TITLE = 0
S_ASSESS = 1
S_STEP1 = 2
S_STEP2 = 3
S_STEP3 = 4
S_STEP4 = 5
S_STEP5 = 6
S_EXAC = 7
S_BIO = 8
S_MON = 9
TOTAL = 10 # slides 0-9
# Pre-create all slides so hyperlinks resolve correctly
slides = [new_slide() for _ in range(TOTAL)]
# ── NAV BAR helper (reusable bottom strip) ────────────────────────
NAV_LABELS = [
("🏠 Home", S_TITLE),
("Assess", S_ASSESS),
("Step 1", S_STEP1),
("Step 2", S_STEP2),
("Step 3", S_STEP3),
("Step 4", S_STEP4),
("Step 5", S_STEP5),
("Exacerb.", S_EXAC),
("Biologics", S_BIO),
("Monitor", S_MON),
]
def add_nav_bar(slide, current_idx):
# strip background
add_rect(slide, 0, Inches(6.88), W, Inches(0.32), RGBColor(0x10,0x20,0x40))
gap = W / TOTAL
for i, (lbl, tgt) in enumerate(NAV_LABELS):
x = i * gap + Inches(0.04)
bg = RGBColor(0x00,0x5F,0x73) if tgt == current_idx else RGBColor(0x10,0x20,0x40)
nav_btn(slide, x, Inches(6.9), lbl, tgt, bg=bg, size=Pt(7.5))
# ══════════════════════════════════════════════════════════════════
# SLIDE 0 — TITLE / NAVIGATION HUB
# ══════════════════════════════════════════════════════════════════
sl = slides[S_TITLE]
set_bg(sl, NAVY)
# decorative gradient strip
add_rect(sl, 0, Inches(1.8), W, Inches(0.06), TEAL)
add_rect(sl, 0, Inches(5.9), W, Inches(0.06), TEAL)
# Title text
add_tb(sl, Inches(0.5), Inches(0.25), Inches(12.3), Inches(0.8),
"BRONCHIAL ASTHMA", size=Pt(38), color=WHITE, bold=True,
align=PP_ALIGN.CENTER)
add_tb(sl, Inches(0.5), Inches(1.0), Inches(12.3), Inches(0.65),
"Interactive Stepwise Management Guide", size=Pt(18),
color=RGBColor(0x7F,0xC4,0xD0), bold=False, align=PP_ALIGN.CENTER)
add_tb(sl, Inches(0.5), Inches(1.5), Inches(12.3), Inches(0.3),
"Based on Harrison's 22E (2025) · GINA 2026 · Murray & Nadel's · Fishman's",
size=Pt(9), color=RGBColor(0x88,0xAA,0xBB), align=PP_ALIGN.CENTER, italic=True)
# GINA 2026 alert banner
add_rrect(sl, Inches(1.2), Inches(1.95), Inches(10.9), Inches(0.52),
RGBColor(0x3D,0x11,0x11), line_color=RED, line_w=Pt(2), radius_pct=5)
add_tb(sl, Inches(1.3), Inches(1.98), Inches(10.7), Inches(0.45),
"⚑ GINA 2026: SABA alone NO LONGER preferred at any step — "
"All patients need ICS-containing therapy — ICS-formoterol = preferred reliever",
size=Pt(9.5), color=RGBColor(0xFF,0x88,0x88), bold=True, align=PP_ALIGN.CENTER)
# Navigation tiles (2 rows × 4-5 cols)
hub_items = [
("📋 Assessment\n& Control", S_ASSESS, TEAL, RGBColor(0xE0,0xF4,0xF6)),
("1️⃣ Step 1\nIntermittent", S_STEP1, TEAL, RGBColor(0xE0,0xF4,0xF6)),
("2️⃣ Step 2\nMild Persistent", S_STEP2, BLUE, LBLUE),
("3️⃣ Step 3\nModerate / MART", S_STEP3, AMBER, LYELLOW),
("4️⃣ Step 4\nSevere", S_STEP4, RGBColor(0xE6,0x7E,0x22), LORANGE),
("5️⃣ Step 5\nVery Severe", S_STEP5, RED, LRED),
("🚨 Acute\nExacerbation", S_EXAC, RED, LRED),
("💉 Biologic\nSelection", S_BIO, PURPLE, LPURPLE),
("📈 Monitoring\n& Step-Down", S_MON, GREEN, LGREEN),
]
tile_w = Inches(2.5)
tile_h = Inches(1.0)
cols = 5
gap_x = (W - Inches(0.4) - tile_w * cols) / (cols - 1) + tile_w
row1_y = Inches(2.6)
row2_y = Inches(3.75)
for i, (lbl, tgt, ac, lc) in enumerate(hub_items):
col_i = i % cols
row_i = i // cols
tx = Inches(0.2) + col_i * gap_x
ty = row1_y if row_i == 0 else row2_y
shape = add_rrect(sl, tx, ty, tile_w, tile_h, lc, line_color=ac, line_w=Pt(2), radius_pct=8)
add_tb(sl, tx, ty, tile_w, tile_h, lbl,
size=Pt(11), color=DARK, bold=True, align=PP_ALIGN.CENTER,
anchor=MSO_ANCHOR.MIDDLE)
add_hyperlink(shape, tgt, prs)
# Key goal box
add_rrect(sl, Inches(0.3), Inches(4.95), Inches(12.7), Inches(0.72),
RGBColor(0x10,0x28,0x4A), line_color=TEAL, line_w=Pt(1.5), radius_pct=5)
add_tb_lines(sl, Inches(0.4), Inches(5.0), Inches(12.5), Inches(0.6), [
("GOALS OF THERAPY: ", Pt(9), RGBColor(0x7F,0xC4,0xD0), True, False),
("Control symptoms · Prevent exacerbations · Maintain normal lung function · "
"Normal activity · Minimise medication side-effects · Prevent asthma deaths",
Pt(8.5), RGBColor(0xCC,0xDD,0xEE), False, False),
])
source_bar(sl)
add_nav_bar(sl, S_TITLE)
# ══════════════════════════════════════════════════════════════════
# SLIDE 1 — ASSESSMENT & CONTROL
# ══════════════════════════════════════════════════════════════════
sl = slides[S_ASSESS]
set_bg(sl, LGREY)
header_bar(sl, "ASSESSMENT OF ASTHMA CONTROL", "GINA 4-Question Tool + Risk Factor Evaluation", TEAL)
# GINA 4Q table
tbl_shape = sl.shapes.add_table(6, 3, Inches(0.3), Inches(1.15), Inches(6.5), Inches(2.3))
t = tbl_shape.table
t.columns[0].width = Inches(4.0)
t.columns[1].width = Inches(1.25)
t.columns[2].width = Inches(1.25)
hdrs = ["GINA 4-Question Tool (Past 4 weeks)", "Yes", "No"]
for ci, h in enumerate(hdrs):
c = t.cell(0, ci)
c.fill.solid(); c.fill.fore_color.rgb = TEAL
c.text = h
r = c.text_frame.paragraphs[0].runs[0]
r.font.bold = True; r.font.size = Pt(9.5); r.font.color.rgb = WHITE
r.font.name = "Calibri"
q_rows = [
"① Daytime asthma symptoms >2×/week?",
"② Any night waking due to asthma?",
"③ Reliever medication needed >2×/week?",
"④ Any activity limitation due to asthma?",
]
for ri, q in enumerate(q_rows, 1):
row_bg = RGBColor(0xF5,0xFC,0xFC) if ri % 2 == 0 else WHITE
for ci in range(3):
c = t.cell(ri, ci)
c.fill.solid(); c.fill.fore_color.rgb = row_bg
txt = q if ci == 0 else ("☐" if ci == 1 else "☐")
c.text = txt
r = c.text_frame.paragraphs[0].runs[0]
r.font.size = Pt(9); r.font.name = "Calibri"
if ci == 0: r.font.color.rgb = DARK
else: r.font.size = Pt(14); r.font.color.rgb = TEAL
# Merge score row
sc = t.cell(5, 0)
sc.merge(t.cell(5, 2))
sc.fill.solid(); sc.fill.fore_color.rgb = RGBColor(0xD4,0xEF,0xF4)
sc.text = "Score: 0 Yes = Well controlled | 1–2 Yes = Partly controlled | 3–4 Yes = Uncontrolled"
r = sc.text_frame.paragraphs[0].runs[0]
r.font.bold = True; r.font.size = Pt(9); r.font.color.rgb = TEAL; r.font.name = "Calibri"
# Severity classification card (right)
info_card(sl, Inches(7.0), Inches(1.15), Inches(6.0), Inches(2.3),
"SEVERITY CLASSIFICATION (GINA)",
[" Mild → Well controlled at Step 1–2",
" Moderate → Well controlled at Step 3",
" Severe → Requires Step 4–5 for control",
" OR uncontrolled despite Step 4–5"],
NAVY, LGREY)
# Risk factors card
info_card(sl, Inches(0.3), Inches(3.55), Inches(6.5), Inches(2.75),
"RISK FACTORS FOR POOR OUTCOMES",
["• SABA overuse (>1 canister/month) — marker of poor control",
"• No ICS / poor adherence / incorrect inhaler technique",
"• FEV₁ <60% predicted; high bronchodilator reversibility",
"• Comorbidities: obesity, rhinosinusitis, GERD, depression, OSA",
"• Smoking, occupational exposures, indoor allergens",
"• Blood eosinophilia / elevated FeNO",
"• History of ≥1 severe exacerbation in past year"],
RED, LRED)
# Check-before-step-up card
info_card(sl, Inches(7.0), Inches(3.55), Inches(6.0), Inches(2.75),
"✓ CHECK BEFORE STEPPING UP",
["✓ Inhaler technique correct? (errors in ~50%)",
"✓ Adherence adequate?",
"✓ Triggers identified & removed?",
"✓ Comorbidities treated?",
"✓ Correct diagnosis (not vocal cord, COPD)?"],
GREEN, LGREEN)
source_bar(sl)
add_nav_bar(sl, S_ASSESS)
# ══════════════════════════════════════════════════════════════════
# SLIDES 2–6 — STEPWISE TREATMENT (one slide per step)
# ══════════════════════════════════════════════════════════════════
STEP_DATA = {
1: {
"slide_idx": S_STEP1,
"title": "STEP 1 — INTERMITTENT ASTHMA",
"subtitle": "Symptoms ≤2×/month | No night waking | Normal lung function",
"track1_ctrl": ["No daily controller required\n(Anti-Inflammatory Reliever — AIR — track)"],
"track1_rel": ["★ As-needed low-dose ICS-formoterol\n (budesonide/formoterol) ← PREFERRED",
" OR ICS-SABA combination inhaler (new GINA 2026)",
" ⚠ SABA monotherapy ALONE no longer recommended"],
"track2_ctrl": ["Low-dose ICS daily (daily controller track)"],
"track2_rel": ["As-needed SABA (salbutamol)\n(acceptable if ICS-formoterol unavailable)"],
"notes": ["AIR = Anti-Inflammatory Reliever strategy",
"ICS-SABA added at Step 1 as GINA 2026 new option",
"SABA alone → no anti-inflammatory protection",
"LTRA (montelukast): alternative — ⚠ FDA black-box (suicidal ideation)"],
"ac": TEAL,
"lc": RGBColor(0xE0,0xF4,0xF6),
},
2: {
"slide_idx": S_STEP2,
"title": "STEP 2 — MILD PERSISTENT ASTHMA",
"subtitle": "Symptoms >2×/month but <daily | Risk of exacerbations present",
"track1_ctrl": ["Low-dose ICS daily\n(e.g., budesonide 200–400 mcg/day)"],
"track1_rel": ["★ As-needed low-dose ICS-formoterol ← PREFERRED",
" OR As-needed SABA + concurrent ICS"],
"track2_ctrl": ["LTRA (montelukast) — alternative to ICS\n⚠ FDA black-box warning: neuropsychiatric events"],
"track2_rel": ["As-needed SABA"],
"notes": ["Low-dose ICS: budesonide ≤400 mcg/day (adults)",
"Fluticasone propionate ≤250 mcg/day equivalent",
"ICS-formoterol as reliever reduces exacerbations vs SABA alone",
"Address modifiable risk factors at every visit"],
"ac": BLUE,
"lc": LBLUE,
},
3: {
"slide_idx": S_STEP3,
"title": "STEP 3 — MODERATE PERSISTENT ASTHMA (MART Strategy)",
"subtitle": "Daily symptoms | FEV₁ 60–80% predicted | Step up from Step 2",
"track1_ctrl": ["Low-dose ICS-LABA daily ← PREFERRED\n"
"e.g., budesonide/formoterol 160/4.5 mcg 1–2 puffs BD"],
"track1_rel": ["★★ MART Strategy (same inhaler for maintenance + rescue)\n"
" ICS-formoterol as-needed IN ADDITION to daily ICS-formoterol",
" → Reduces exacerbations vs fixed-dose + SABA",
" ★ MART = Maintenance And Reliever Therapy"],
"track2_ctrl": ["Medium-dose ICS daily (alternative)\nOR Low-dose ICS + LTRA"],
"track2_rel": ["As-needed SABA (if not on MART)"],
"notes": ["Medium ICS dose: budesonide 400–800 mcg/day (adults)",
"MART evidence strongest with budesonide/formoterol",
"Formoterol's rapid onset makes it suitable as reliever",
"FeNO ≥25 ppb supports eosinophilic inflammation → ICS responsive"],
"ac": AMBER,
"lc": LYELLOW,
},
4: {
"slide_idx": S_STEP4,
"title": "STEP 4 — SEVERE ASTHMA",
"subtitle": "Uncontrolled on Step 3 | Confirm adherence, technique, comorbidities first",
"track1_ctrl": ["Medium/High-dose ICS-LABA daily ← PREFERRED\n"
"e.g., budesonide/formoterol 320/9 mcg or fluticasone/salmeterol 250/50 mcg"],
"track1_rel": ["As-needed ICS-formoterol (MART if on budesonide/formoterol)",
"OR As-needed SABA"],
"track2_ctrl": ["Add-on LAMA (tiotropium Respimat)\n"
"if uncontrolled on ICS-LABA alone\n"
"→ Triple therapy (ICS-LABA-LAMA)"],
"track2_rel": ["Add-on LTRA or theophylline if needed\n"
"(step 4 adjunct; limited role)"],
"notes": ["High ICS dose: budesonide >800 mcg/day (adults)",
"Triple therapy reduces exacerbations vs ICS-LABA",
" BUT less effective than biologics",
"Refer to respiratory specialist",
"Consider FeNO, blood eosinophils, IgE for biologic eligibility"],
"ac": RGBColor(0xE6,0x7E,0x22),
"lc": LORANGE,
},
5: {
"slide_idx": S_STEP5,
"title": "STEP 5 — VERY SEVERE / REFRACTORY ASTHMA",
"subtitle": "Uncontrolled on Step 4-5 | 5–10% of all asthma patients | ~50% have eosinophilic inflammation",
"track1_ctrl": ["High-dose ICS-LABA + LAMA (triple therapy)\n"
"+ BIOLOGIC THERAPY ← mainstay of Step 5"],
"track1_rel": ["As-needed ICS-formoterol",
"MINIMIZE oral corticosteroids — only if all else fails",
"Cumulative OCS toxicity: T2DM, osteoporosis, cataracts, adrenal suppression"],
"track2_ctrl": ["Macrolide (azithromycin) — neutrophilic/non-eosinophilic subset\n"
"Low-dose maintenance OCS — last resort only"],
"track2_rel": ["Bronchial thermoplasty\n"
"(bronchoscopic; reduces airway smooth muscle mass)\n"
"Selected patients at specialist centres"],
"notes": ["Definition: uncontrolled despite Step 4–5 after addressing adherence,",
" inhaler technique, comorbidities, and triggers",
"Biologics: see Biologic Selection slide",
"IV magnesium / OCS for severe exacerbations",
"Dupilumab also approved in COPD + eos + exacerbations"],
"ac": RED,
"lc": LRED,
},
}
for step_num, data in STEP_DATA.items():
sl = slides[data["slide_idx"]]
ac = data["ac"]
lc = data["lc"]
set_bg(sl, LGREY)
header_bar(sl, data["title"], data["subtitle"], ac)
# Step badge
step_badge(sl, Inches(12.15), Inches(0.05), step_num)
# --- Track layout -----------------------------------------------
# Track 1 (Preferred)
add_rrect(sl, Inches(0.25), Inches(1.15), Inches(6.25), Inches(0.32),
ac, radius_pct=5)
add_tb(sl, Inches(0.3), Inches(1.15), Inches(6.1), Inches(0.32),
"TRACK 1 — PREFERRED (GINA 2026)",
size=Pt(9), color=WHITE, bold=True)
info_card(sl, Inches(0.25), Inches(1.48), Inches(3.0), Inches(2.1),
"CONTROLLER (Daily)",
data["track1_ctrl"], ac, lc)
info_card(sl, Inches(3.5), Inches(1.48), Inches(3.0), Inches(2.1),
"RELIEVER (As-needed)",
data["track1_rel"], ac, lc)
# --- Track 2 (Alternative) ---------------------------------------
add_rrect(sl, Inches(0.25), Inches(3.7), Inches(6.25), Inches(0.32),
RGBColor(0x55,0x55,0x55), radius_pct=5)
add_tb(sl, Inches(0.3), Inches(3.7), Inches(6.1), Inches(0.32),
"TRACK 2 — ALTERNATIVE",
size=Pt(9), color=WHITE, bold=True)
info_card(sl, Inches(0.25), Inches(4.03), Inches(3.0), Inches(2.0),
"ALTERNATIVE CONTROLLER",
data["track2_ctrl"], RGBColor(0x55,0x55,0x55), WHITE)
info_card(sl, Inches(3.5), Inches(4.03), Inches(3.0), Inches(2.0),
"ALTERNATIVE RELIEVER",
data["track2_rel"], RGBColor(0x55,0x55,0x55), WHITE)
# --- Key notes panel (right side) --------------------------------
info_card(sl, Inches(6.85), Inches(1.15), Inches(6.2), Inches(5.45),
"📌 KEY NOTES",
data["notes"], NAVY, LGREY)
# --- Navigation buttons ------------------------------------------
# Prev / Next step arrows
if step_num > 1:
prev_idx = data["slide_idx"] - 1
nav_btn(sl, Inches(0.25), Inches(6.55), "◀ Step " + str(step_num-1), prev_idx,
bg=RGBColor(0x33,0x55,0x77), fg=WHITE, size=Pt(8))
if step_num < 5:
next_idx = data["slide_idx"] + 1
nav_btn(sl, Inches(1.75), Inches(6.55), "Step " + str(step_num+1) + " ▶", next_idx,
bg=ac, fg=WHITE, size=Pt(8))
# Quick-jump buttons
nav_btn(sl, Inches(4.5), Inches(6.55), "🚨 Exacerbation", S_EXAC, bg=RED, fg=WHITE, size=Pt(8))
nav_btn(sl, Inches(6.0), Inches(6.55), "💉 Biologics", S_BIO, bg=PURPLE, fg=WHITE, size=Pt(8))
nav_btn(sl, Inches(7.5), Inches(6.55), "📈 Monitor", S_MON, bg=GREEN, fg=WHITE, size=Pt(8))
nav_btn(sl, Inches(9.0), Inches(6.55), "📋 Assess", S_ASSESS,bg=TEAL, fg=WHITE, size=Pt(8))
nav_btn(sl, Inches(10.5), Inches(6.55), "🏠 Home", S_TITLE, bg=NAVY, fg=WHITE, size=Pt(8))
source_bar(sl)
add_nav_bar(sl, data["slide_idx"])
# ══════════════════════════════════════════════════════════════════
# SLIDE 7 — ACUTE EXACERBATION ALGORITHM
# ══════════════════════════════════════════════════════════════════
sl = slides[S_EXAC]
set_bg(sl, LGREY)
header_bar(sl, "ACUTE EXACERBATION MANAGEMENT", "PEFR-based severity assessment | GINA 2026 updated O₂ targets", RED)
# O2 alert
add_rrect(sl, Inches(0.25), Inches(1.12), Inches(12.85), Inches(0.5),
RGBColor(0x3D,0x11,0x11), line_color=RED, line_w=Pt(2), radius_pct=5)
add_tb(sl, Inches(0.3), Inches(1.14), Inches(12.7), Inches(0.44),
"⚑ O₂ ONLY if SpO₂ <92% — Target 93–95% (adults) / ≥92% (children ≤5 yrs) "
"⚑ ANAPHYLAXIS + ASTHMA: Give EPINEPHRINE first, THEN bronchodilators",
size=Pt(9.5), color=RGBColor(0xFF,0x88,0x88), bold=True, align=PP_ALIGN.CENTER)
# Three severity columns
SEV_COLS = [
("MILD – MODERATE\nPEFR >60%", GREEN, LGREEN,
["SpO₂ ≥92% | Speaks in sentences",
"",
"① SABA 4–8 puffs (or 2.5 mg neb)",
" q20 min × 3 (or ICS-formoterol)",
"② Assess PEFR after 1 h",
"③ If improving: continue SABA PRN",
"④ If not improving:",
" → Add ipratropium 4 puffs",
" → Oral prednisolone 30–50 mg",
"",
"DISCHARGE if PEFR ≥60%:",
"✓ Written action plan",
"✓ Follow-up in 1–2 weeks",
"✓ Step-up therapy reviewed"]),
("SEVERE\nPEFR 40–60%", AMBER, LORANGE,
["SpO₂ <92% | Speaks in words",
"Accessory muscle use",
"",
"① Nebulised SABA + ipratropium",
" q20 min × 3",
"② IV or oral OCS (early)",
"③ Controlled O₂ → target 93–95%",
"④ PEFR reassessment in 1 h",
"⑤ If no rapid improvement → ADMIT",
"",
"IN HOSPITAL:",
"• Continuous neb. bronchodilators",
"• IV methylprednisolone",
"• Monitor SpO₂, PEFR, blood gases"]),
("LIFE-THREATENING\nPEFR <40% / Silent chest", RED, LRED,
["Signs: Silent chest, cyanosis,",
" altered consciousness, bradycardia",
"",
"→ ICU ADMISSION",
"",
"① IV methylprednisolone 125 mg",
"② Continuous nebulised SABA",
"③ IV MgSO₄ 2g over 20 min",
"④ Heliox 70:30 (if available)",
"⑤ NIPPV (selected patients)",
"",
"If respiratory failure imminent:",
"• Intubate & ventilate",
"• Permissive hypercapnia",
"• Low VT 5–7 mL/kg, long exp. time",
"• Avoid auto-PEEP"]),
]
col_w = Inches(4.15)
for i, (title, ac_e, lc_e, lines) in enumerate(SEV_COLS):
cx = Inches(0.25) + i * Inches(4.36)
add_rrect(sl, cx, Inches(1.7), col_w, Inches(0.4), ac_e, radius_pct=5)
add_tb(sl, cx + Inches(0.05), Inches(1.7), col_w - Inches(0.1), Inches(0.4),
title, size=Pt(9.5), color=WHITE, bold=True,
align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
add_rrect(sl, cx, Inches(2.11), col_w, Inches(4.55), lc_e,
line_color=ac_e, line_w=Pt(1.5), radius_pct=3)
line_objs = [(ln, Pt(8.5), DARK, False, False) for ln in lines]
add_tb_lines(sl, cx + Inches(0.08), Inches(2.15),
col_w - Inches(0.12), Inches(4.48), line_objs)
nav_btn(sl, Inches(0.25), Inches(6.6), "🏠 Home", S_TITLE, bg=NAVY, size=Pt(8))
nav_btn(sl, Inches(1.75), Inches(6.6), "💉 Biologics", S_BIO, bg=PURPLE, size=Pt(8))
nav_btn(sl, Inches(3.25), Inches(6.6), "📈 Monitor", S_MON, bg=GREEN, size=Pt(8))
source_bar(sl)
add_nav_bar(sl, S_EXAC)
# ══════════════════════════════════════════════════════════════════
# SLIDE 8 — BIOLOGIC SELECTION
# ══════════════════════════════════════════════════════════════════
sl = slides[S_BIO]
set_bg(sl, LGREY)
header_bar(sl, "BIOLOGIC THERAPY SELECTION GUIDE", "Step 5 — Severe / Refractory Asthma | GINA 2026", PURPLE)
bio_tbl = sl.shapes.add_table(8, 5,
Inches(0.25), Inches(1.15),
Inches(12.85), Inches(5.0))
t = bio_tbl.table
t.columns[0].width = Inches(2.2)
t.columns[1].width = Inches(2.0)
t.columns[2].width = Inches(3.2)
t.columns[3].width = Inches(1.5)
t.columns[4].width = Inches(3.95)
hdrs = ["Biologic (Target)", "Mechanism", "Biomarker / Indication", "Age", "Notes"]
for ci, h in enumerate(hdrs):
c = t.cell(0, ci)
c.fill.solid(); c.fill.fore_color.rgb = PURPLE
c.text = h
r = c.text_frame.paragraphs[0].runs[0]
r.font.bold = True; r.font.size = Pt(9.5); r.font.color.rgb = WHITE; r.font.name = "Calibri"
bio_rows = [
("Omalizumab\n(Anti-IgE)\n+ biosimilar available",
"Binds free IgE →\n↓ mast cell / basophil\nactivation",
"Elevated serum IgE\n+ allergen sensitisation\n(allergic asthma)",
"≥6 yrs",
"SC monthly or bimonthly\nFor allergic/atopic phenotype",
RGBColor(0xF4,0xEC,0xF7)),
("Mepolizumab\n(Anti-IL-5)",
"Blocks IL-5 →\n↓ eosinophil maturation\n& survival",
"Blood eos ≥150–300/μL\n(≥300 for best response)\nEosinophilic asthma",
"≥6 yrs",
"SC monthly\nAlso: EGPA, HES",
LBLUE),
("Benralizumab\n(Anti-IL-5Rα)",
"Binds IL-5Rα →\nDirect eosinophil\ndepletion (ADCC)",
"Blood eos ≥300/μL\nEosinophilic asthma",
"≥12 yrs",
"SC Q4w × 3, then Q8w\nFaster eosinophil depletion\nvs mepolizumab",
LGREEN),
("Dupilumab\n(Anti-IL-4Rα)\n[IL-4 + IL-13 blockade]",
"Blocks shared IL-4Rα\n→ ↓ type-2 airway\ninflammation",
"Blood eos ≥150/μL\nOR FeNO ≥25 ppb\nType 2 (T2-high) asthma",
"≥6 yrs",
"SC biweekly or monthly\nAlso approved: COPD+eos,\natopic dermatitis, CRSwNP",
LYELLOW),
("Tezepelumab\n(Anti-TSLP)",
"Blocks TSLP (upstream\nalarmins) → broad\nT2 + non-T2 effects",
"BROADEST INDICATION\nWorks even without\nelevated T2 biomarkers",
"≥12 yrs",
"SC monthly\nOnly biologic for non-T2\nor mixed-endotype asthma",
LORANGE),
("Depemokimab\n(Anti-IL-5)\n★ NEW — GINA 2026",
"Ultra-long-acting anti-IL-5\n↓ eosinophil\nmaturation & survival",
"Blood eos ≥300/μL\nSevere eosinophilic asthma\nAlso: CRSwNP (≥18 yrs)",
"≥12 yrs\n(CRSwNP\n≥18 yrs)",
"SC every 6 MONTHS\n(half-yearly dosing)\nNew addition in GINA 2026",
LRED),
]
alt_fills = [RGBColor(0xF4,0xEC,0xF7), LBLUE, LGREEN, LYELLOW, LORANGE, LRED]
for ri, (name, mech, marker, age, notes, bg_r) in enumerate(bio_rows, 1):
for ci, txt in enumerate([name, mech, marker, age, notes]):
c = t.cell(ri, ci)
c.fill.solid(); c.fill.fore_color.rgb = bg_r
c.text = txt
r = c.text_frame.paragraphs[0].runs[0]
r.font.size = Pt(8.5); r.font.name = "Calibri"
if ci == 0: r.font.bold = True; r.font.color.rgb = PURPLE
else: r.font.color.rgb = DARK
# last row — selection hint
for ci in range(5):
t.cell(7, ci).fill.solid()
t.cell(7, ci).fill.fore_color.rgb = RGBColor(0x1A,0x35,0x5E)
t.cell(7, 0).merge(t.cell(7, 4))
t.cell(7, 0).text = ("SELECTION HINT: Allergic → Omalizumab | Blood eos high → Mepolizumab / Benralizumab / Depemokimab | "
"T2-high (eos OR FeNO) → Dupilumab | Non-T2 or mixed → Tezepelumab")
r = t.cell(7, 0).text_frame.paragraphs[0].runs[0]
r.font.bold = True; r.font.size = Pt(8.5); r.font.color.rgb = WHITE; r.font.name = "Calibri"
nav_btn(sl, Inches(0.25), Inches(6.57), "◀ Step 5", S_STEP5, bg=RED, size=Pt(8))
nav_btn(sl, Inches(1.75), Inches(6.57), "🚨 Exacerb.", S_EXAC, bg=RED, size=Pt(8))
nav_btn(sl, Inches(3.25), Inches(6.57), "📈 Monitor", S_MON, bg=GREEN, size=Pt(8))
nav_btn(sl, Inches(11.6), Inches(6.57), "🏠 Home", S_TITLE, bg=NAVY, size=Pt(8))
source_bar(sl)
add_nav_bar(sl, S_BIO)
# ══════════════════════════════════════════════════════════════════
# SLIDE 9 — MONITORING & STEP-DOWN
# ══════════════════════════════════════════════════════════════════
sl = slides[S_MON]
set_bg(sl, LGREY)
header_bar(sl, "ONGOING MONITORING & STEP-DOWN", "Every visit checklist | When & how to step down", GREEN)
# 6 monitoring cards in 2 rows × 3 cols
mon_cards = [
("📋 Symptom Control", GREEN, LGREEN,
["GINA 4-question tool every visit",
"Well controlled ≥3 months → consider step-down",
"Partly controlled → check technique, adherence",
"Uncontrolled → step up after addressing barriers"]),
("🌬 Spirometry / PEFR", BLUE, LBLUE,
["FEV₁ at diagnosis, then annually",
"Reassess after 3–6 months of new therapy",
"PEFR diaries for self-monitoring (severe asthma)",
"FEV₁ <60% predicted = high-risk marker"]),
("💨 Inhaler Technique", TEAL, RGBColor(0xE0,0xF4,0xF6),
["Check technique at every single visit",
"Poor technique found in up to 50% of 'refractory'",
"pMDI: slow deep breath, 5 s hold; shake suspensions",
"DPI: fast forceful breath; spacer with pMDI for children/elderly"]),
("✅ Adherence", AMBER, LYELLOW,
["Identify and address barriers",
"Simplify regimen (MART reduces inhaler count)",
"Assess prescription refill rates",
"Poor adherence → cause of apparent refractory asthma"]),
("🔬 Biomarker Review", PURPLE, LPURPLE,
["FeNO ≥25 ppb → eosinophilic inflammation",
" → predicts ICS response",
"Blood eosinophils ≥300/μL → biologic eligibility",
"IgE level → omalizumab eligibility"]),
("⬇ Step-Down Protocol", GREEN, LGREEN,
["Step down only if well controlled ≥3 months",
"Do NOT step down during: pregnancy, allergy season,",
" active infection, recent exacerbation",
"Reduce ICS dose by 25–50% per step",
"Discontinue LABA before reducing ICS (if possible)"]),
]
card_w = Inches(4.1)
card_h = Inches(2.4)
gap_x = Inches(0.23)
start_x = Inches(0.25)
row1_y = Inches(1.2)
row2_y = Inches(3.8)
for i, (title, ac_m, lc_m, lines) in enumerate(mon_cards):
col_i = i % 3
row_i = i // 3
cx = start_x + col_i * (card_w + gap_x)
cy = row1_y if row_i == 0 else row2_y
info_card(sl, cx, cy, card_w, card_h, title, lines, ac_m, lc_m)
nav_btn(sl, Inches(0.25), Inches(6.6), "🏠 Home", S_TITLE, bg=NAVY, size=Pt(8))
nav_btn(sl, Inches(1.75), Inches(6.6), "📋 Assess", S_ASSESS,bg=TEAL, size=Pt(8))
nav_btn(sl, Inches(3.25), Inches(6.6), "💉 Biologics", S_BIO, bg=PURPLE, size=Pt(8))
nav_btn(sl, Inches(4.75), Inches(6.6), "🚨 Exacerb.", S_EXAC, bg=RED, size=Pt(8))
source_bar(sl)
add_nav_bar(sl, S_MON)
# ══════════════════════════════════════════════════════════════════
# SAVE
# ══════════════════════════════════════════════════════════════════
out = "/tmp/workspace/asthma-summary/Asthma_Stepwise_Management.pptx"
buf = BytesIO()
prs.save(buf)
with open(out, "wb") as f:
f.write(buf.getvalue())
import os
print(f"SAVED: {out}")
print(f"Size: {os.path.getsize(out):,} bytes")
print(f"Slides: {len(prs.slides)}")
cd /tmp/workspace/asthma-summary && python3 build_pptx.py 2>&1
python3 -c " from pptx import Presentation prs = Presentation('/tmp/workspace/asthma-summary/Asthma_Stepwise_Management.pptx') print(f'Slides: {len(prs.slides)}') for i, slide in enumerate(prs.slides): texts = [] for shape in slide.shapes: if shape.has_text_frame: t = shape.text_frame.text.strip()[:60] if t: texts.append(t) print(f' Slide {i}: {texts[0] if texts else \"(empty)\"}') "
Asthma Stepwise Management
Presentation · PPTX
| Slide | Title | Colour |
|---|---|---|
| 0 | Title / Navigation Hub | Navy — 9 clickable tiles jump to each section |
| 1 | Assessment & Control | Teal — GINA 4Q table + severity + risk factors |
| 2 | Step 1 — Intermittent | Teal — AIR strategy, ICS-formoterol preferred, new ICS-SABA option |
| 3 | Step 2 — Mild Persistent | Blue — Low-dose ICS, montelukast caveat |
| 4 | Step 3 — Moderate (MART) | Amber — MART explained, budesonide/formoterol |
| 5 | Step 4 — Severe | Orange — Medium/high ICS-LABA, add-on LAMA |
| 6 | Step 5 — Very Severe | Red — High-dose triple + biologics, minimize OCS |
| 7 | Acute Exacerbation | Red — 3-column severity algorithm, revised O₂ targets, ICU protocol |
| 8 | Biologic Selection Guide | Purple — All 6 agents (incl. Depemokimab new 2026), selection hint row |
| 9 | Monitoring & Step-Down | Green — 6 monitoring domain cards, step-down rules |