~/abg-ventilator/generate_pdf.py
#!/usr/bin/env python3
"""
ABG & Ventilator Settings - Detailed Medical Reference PDF
Using ReportLab for professional PDF generation
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import (
HexColor, white, black, Color
)
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.lib import colors
# Color palette - medical/clinical theme
DARK_BLUE = HexColor('#1A3C5E')
MED_BLUE = HexColor('#2E6DA4')
LIGHT_BLUE = HexColor('#D6E8F7')
ACCENT_GREEN = HexColor('#1E7E4B')
LIGHT_GREEN = HexColor('#D4EDDA')
ACCENT_RED = HexColor('#C0392B')
LIGHT_RED = HexColor('#FDECEA')
ACCENT_AMBER = HexColor('#D68910')
LIGHT_AMBER = HexColor('#FEF9E7')
ACCENT_PURPLE= HexColor('#6C3483')
LIGHT_PURPLE = HexColor('#EAD6F5')
DARK_GREY = HexColor('#2C3E50')
MID_GREY = HexColor('#566573')
LIGHT_GREY = HexColor('#F2F3F4')
TABLE_ALT = HexColor('#EBF5FB')
PAGE_W, PAGE_H = A4
MARGIN = 2 * cm
doc = SimpleDocTemplate(
'/home/daytona/workspace/abg-ventilator/ABG_Ventilator_Settings.pdf',
pagesize=A4,
rightMargin=MARGIN, leftMargin=MARGIN,
topMargin=2.5*cm, bottomMargin=2*cm,
title='ABG & Ventilator Settings - Complete Medical Reference',
author='Orris Medical AI'
)
styles = getSampleStyleSheet()
# ── Custom paragraph styles ────────────────────────────────────────────────
def make_style(name, parent='Normal', **kwargs):
return ParagraphStyle(name, parent=styles[parent], **kwargs)
cover_title = make_style('CoverTitle', fontSize=28, textColor=white,
alignment=TA_CENTER, fontName='Helvetica-Bold',
spaceAfter=8)
cover_sub = make_style('CoverSub', fontSize=14, textColor=HexColor('#AED6F1'),
alignment=TA_CENTER, spaceAfter=4)
cover_note = make_style('CoverNote', fontSize=10, textColor=HexColor('#D5DBDB'),
alignment=TA_CENTER)
h1 = make_style('H1', fontSize=16, textColor=white,
fontName='Helvetica-Bold',
spaceBefore=6, spaceAfter=4, leading=20)
h2 = make_style('H2', fontSize=13, textColor=DARK_BLUE,
fontName='Helvetica-Bold',
spaceBefore=10, spaceAfter=4, leading=17)
h3 = make_style('H3', fontSize=11, textColor=MED_BLUE,
fontName='Helvetica-Bold',
spaceBefore=6, spaceAfter=3)
body = make_style('Body', fontSize=9.5, leading=14,
textColor=DARK_GREY, spaceAfter=4,
alignment=TA_JUSTIFY)
body_sm = make_style('BodySm', fontSize=8.5, leading=13,
textColor=DARK_GREY, spaceAfter=3)
bullet = make_style('Bullet', fontSize=9.5, leading=14,
textColor=DARK_GREY, leftIndent=14,
bulletIndent=4, spaceAfter=2)
bullet_sm = make_style('BulletSm', fontSize=8.5, leading=13,
textColor=DARK_GREY, leftIndent=18,
bulletIndent=6, spaceAfter=2)
box_title = make_style('BoxTitle', fontSize=10, textColor=white,
fontName='Helvetica-Bold', spaceAfter=4)
box_body = make_style('BoxBody', fontSize=9, leading=13,
textColor=DARK_GREY)
formula_style = make_style('Formula', fontSize=10, leading=14,
textColor=ACCENT_GREEN, fontName='Helvetica-Bold',
alignment=TA_CENTER, spaceBefore=4, spaceAfter=4)
warn_style = make_style('Warn', fontSize=9, leading=13,
textColor=ACCENT_RED, fontName='Helvetica-Bold')
ref_style = make_style('Ref', fontSize=7.5, textColor=MID_GREY,
leading=11, spaceAfter=2)
# ── Helper builders ────────────────────────────────────────────────────────
def section_header(title, color=DARK_BLUE, icon=''):
"""Colored section banner with title."""
tbl = Table([[Paragraph(f'{icon} {title}' if icon else title, h1)]],
colWidths=[PAGE_W - 2*MARGIN])
tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), color),
('ROUNDEDCORNERS', [6]),
('TOPPADDING', (0,0), (-1,-1), 10),
('BOTTOMPADDING', (0,0), (-1,-1), 10),
('LEFTPADDING', (0,0), (-1,-1), 14),
('RIGHTPADDING', (0,0), (-1,-1), 14),
]))
return tbl
def color_box(title, content_paras, bg=LIGHT_BLUE, border=MED_BLUE, title_bg=MED_BLUE):
"""Colored box with header strip and content."""
rows = [[Paragraph(title, box_title)]]
for p in content_paras:
rows.append([p])
tbl = Table(rows, colWidths=[PAGE_W - 2*MARGIN])
style_cmds = [
('BACKGROUND', (0,0), (0,0), title_bg),
('BACKGROUND', (0,1), (-1,-1), bg),
('TOPPADDING', (0,0), (-1,-1), 7),
('BOTTOMPADDING', (0,0), (-1,-1), 7),
('LEFTPADDING', (0,0), (-1,-1), 12),
('RIGHTPADDING', (0,0), (-1,-1), 12),
('BOX', (0,0), (-1,-1), 1, border),
('GRID', (0,0), (-1,-1), 0.3, HexColor('#AAAAAA')),
]
tbl.setStyle(TableStyle(style_cmds))
return tbl
def two_col_box(left_title, left_items, right_title, right_items,
lbg=LIGHT_GREEN, lborder=ACCENT_GREEN,
rbg=LIGHT_RED, rborder=ACCENT_RED):
"""Side-by-side two-column colored boxes."""
half = (PAGE_W - 2*MARGIN - 0.4*cm) / 2
def build_cell_content(title, items, tbg, ibg, border_color):
inner_rows = [[Paragraph(title, box_title)]]
for it in items:
inner_rows.append([Paragraph(f'• {it}', bullet_sm)])
t = Table(inner_rows, colWidths=[half])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (0,0), border_color),
('BACKGROUND', (0,1), (-1,-1), ibg),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 10),
('RIGHTPADDING', (0,0), (-1,-1), 10),
('BOX', (0,0), (-1,-1), 1, border_color),
]))
return t
left_t = build_cell_content(left_title, left_items, lborder, lbg, lborder)
right_t = build_cell_content(right_title, right_items, rborder, rbg, rborder)
outer = Table([[left_t, right_t]], colWidths=[half, half],
hAlign='LEFT')
outer.setStyle(TableStyle([
('VALIGN', (0,0), (-1,-1), 'TOP'),
('LEFTPADDING', (0,0), (-1,-1), 0),
('RIGHTPADDING', (0,0), (-1,-1), 0),
('TOPPADDING', (0,0), (-1,-1), 0),
('BOTTOMPADDING', (0,0), (-1,-1), 0),
]))
return outer
def data_table(headers, rows, col_widths=None, stripe_color=TABLE_ALT,
header_color=DARK_BLUE):
"""Formatted data table with striped rows."""
if col_widths is None:
n = len(headers)
col_widths = [(PAGE_W - 2*MARGIN) / n] * n
header_row = [Paragraph(f'<b>{h}</b>', make_style(f'TH_{i}',
fontSize=9, textColor=white, fontName='Helvetica-Bold',
alignment=TA_CENTER)) for i, h in enumerate(headers)]
all_rows = [header_row]
for i, row in enumerate(rows):
styled_row = [Paragraph(str(cell), make_style(f'TD_{i}_{j}',
fontSize=9, textColor=DARK_GREY,
alignment=TA_CENTER, leading=13))
for j, cell in enumerate(row)]
all_rows.append(styled_row)
tbl = Table(all_rows, colWidths=col_widths, repeatRows=1)
style_cmds = [
('BACKGROUND', (0,0), (-1,0), header_color),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
('GRID', (0,0), (-1,-1), 0.5, HexColor('#CCCCCC')),
('ROWBACKGROUNDS',(0,1), (-1,-1), [white, stripe_color]),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]
tbl.setStyle(TableStyle(style_cmds))
return tbl
# ── Build story ────────────────────────────────────────────────────────────
story = []
S = Spacer
# ════════════════════════════════════════════════════════
# COVER PAGE
# ════════════════════════════════════════════════════════
cover_bg = Table(
[[Paragraph('ABG & VENTILATOR SETTINGS', cover_title)],
[Paragraph('Complete Medical Reference Guide', cover_sub)],
[Spacer(1, 0.3*cm)],
[Paragraph('Arterial Blood Gas Interpretation • Acid-Base Disorders • Mechanical Ventilation', cover_note)],
[Spacer(1, 0.2*cm)],
[Paragraph('ICU | Emergency Medicine | Pulmonology | Anaesthesia', cover_note)],
[Spacer(1, 1*cm)],
[Paragraph('Sources: Murray & Nadel\'s Respiratory Medicine • Barash Clinical Anaesthesia • Goldman-Cecil Medicine • Rosen\'s Emergency Medicine • Miller\'s Anaesthesia', ref_style)],
],
colWidths=[PAGE_W - 2*MARGIN]
)
cover_bg.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), DARK_BLUE),
('TOPPADDING', (0,0), (-1,-1), 30),
('BOTTOMPADDING', (0,0), (-1,-1), 10),
('LEFTPADDING', (0,0), (-1,-1), 20),
('RIGHTPADDING', (0,0), (-1,-1), 20),
('ROUNDEDCORNERS',[8]),
]))
story.append(Spacer(1, 1*cm))
story.append(cover_bg)
story.append(Spacer(1, 0.6*cm))
# Quick-Reference boxes on cover
qr_headers = ['Parameter', 'Normal Range']
qr_rows = [
['pH', '7.35 – 7.45'],
['PaO₂', '80 – 100 mmHg'],
['PaCO₂', '35 – 45 mmHg'],
['HCO₃⁻', '22 – 26 mEq/L'],
['BE', '-2 to +2 mEq/L'],
['SaO₂', '95 – 99%'],
['SpO₂ target', '≥ 94%'],
['P/F Ratio', '> 300 (normal)'],
]
story.append(Paragraph('ABG Normal Reference Values', h2))
story.append(data_table(qr_headers, qr_rows,
col_widths=[9*cm, 8*cm]))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 1 — ABG BASICS & NORMAL VALUES
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 1: ABG — Basics & Normal Values', DARK_BLUE))
story.append(S(1, 0.4*cm))
story.append(Paragraph('What is an ABG?', h2))
story.append(Paragraph(
'Arterial Blood Gas (ABG) analysis measures pH, PaO₂, PaCO₂, HCO₃⁻, base excess, and oxygen saturation '
'from an arterial blood sample. It is the <b>gold standard</b> for assessing oxygenation, ventilation, and '
'acid-base status. ABG is superior to pulse oximetry because it directly measures PaO₂ and can detect '
'abnormal haemoglobins (carboxyhemoglobin, methemoglobin) that falsely elevate SpO₂.',
body))
story.append(Paragraph('Key Parameters', h2))
param_headers = ['Parameter', 'Full Name', 'Normal Range', 'Clinical Significance']
param_rows = [
['pH', 'Acidity/Alkalinity', '7.35 – 7.45', 'Reflects overall acid-base balance. <7.2 = life-threatening acidaemia'],
['PaO₂', 'Partial Pressure of O₂', '80 – 100 mmHg', 'Direct measure of oxygenation in arterial blood'],
['PaCO₂', 'Partial Pressure of CO₂', '35 – 45 mmHg', 'Ventilatory parameter; raised = hypoventilation'],
['HCO₃⁻', 'Bicarbonate', '22 – 26 mEq/L', 'Metabolic component; regulated by kidneys'],
['BE/BD', 'Base Excess / Deficit', '-2 to +2 mEq/L', 'Positive = alkalosis; Negative = acidosis'],
['SaO₂', 'Arterial O₂ Saturation', '95 – 99%', 'Haemoglobin saturation; SpO₂ is non-invasive estimate'],
['A-aDO₂', 'Alveolar-arterial O₂ diff.', '<15 mmHg (<30 yr)','Elevated = V/Q mismatch, shunt, diffusion defect'],
['P/F Ratio','PaO₂/FiO₂ Ratio', '>400 (ideal)', '<300=mild ARDS; <200=moderate; <100=severe'],
]
story.append(data_table(param_headers, param_rows,
col_widths=[2.2*cm, 4*cm, 3.5*cm, 7.5*cm]))
story.append(S(1, 0.4*cm))
story.append(Paragraph('Alveolar-Arterial (A-a) Gradient Formula', h3))
story.append(Paragraph(
'<b>PAO₂ = (FiO₂ × [Patm - PH₂O]) - (PaCO₂ / RQ)</b><br/>'
'On room air (sea level): <b>PAO₂ = 150 - (PaCO₂ / 0.8)</b><br/>'
'<b>A-aDO₂ = PAO₂ - PaO₂</b> | Normal: <15 mmHg in young, increases with age (approx. age/4 + 4)',
formula_style))
story.append(S(1, 0.4*cm))
# ════════════════════════════════════════════════════════
# SECTION 2 — STEPWISE ABG INTERPRETATION
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 2: Stepwise ABG Interpretation', MED_BLUE))
story.append(S(1, 0.4*cm))
story.append(Paragraph(
'Use this 6-step systematic approach for every ABG (Barash Clinical Anaesthesia, 9e). '
'Draw ABG and venous electrolytes simultaneously — calculated HCO₃⁻ on ABG should match '
'measured serum HCO₃⁻ within 2–3 mEq/L; discrepancy = lab error or timing issue.',
body))
steps_data = [
['STEP', 'ACTION', 'CRITERIA / DETAILS'],
['Step 1', 'Identify Acidaemia vs. Alkalemia',
'pH < 7.35 = Acidaemia\npH > 7.45 = Alkalemia\npH 7.35–7.45 = Normal (may still have mixed disorder)'],
['Step 2', 'Determine Primary Disorder',
'pH↓ + PaCO₂↑ = Respiratory Acidosis\npH↓ + HCO₃↓ = Metabolic Acidosis\npH↑ + PaCO₂↓ = Respiratory Alkalosis\npH↑ + HCO₃↑ = Metabolic Alkalosis'],
['Step 3', 'Assess Compensation\n(Acute vs. Chronic)',
'Respiratory Acidosis — Acute: HCO₃ rises 1 per 10↑CO₂ | Chronic: rises 3.5 per 10↑CO₂\nRespiratory Alkalosis — Acute: HCO₃ falls 2 per 10↓CO₂ | Chronic: falls 5 per 10↓CO₂\nMetabolic Acidosis — Expected PaCO₂ = 1.5×HCO₃ + 8 ± 2 (Winter\'s formula)\nMetabolic Alkalosis — Expected PaCO₂ = 40 + 0.7×(HCO₃ - 24)'],
['Step 4', 'Calculate Anion Gap\n(Always — even if no met. acidosis)',
'AG = Na⁺ - (Cl⁻ + HCO₃⁻) Normal = 8–12 mEq/L (some sources ≤13)\nCorrect for albumin: Corrected AG = AG + 2.5 × (4 - albumin g/dL)\nHigh AG (>13) = MUDPILES causes\nNormal AG = hyperchloraemic acidosis'],
['Step 5', 'Assess Oxygenation',
'PaO₂ < 60 mmHg = Hypoxaemia\nPaO₂/FiO₂ ratio — <300 mild ARDS, <200 moderate, <100 severe\nCalculate A-a gradient to identify shunt vs. hypoventilation'],
['Step 6', 'Delta-Delta (Δ/Δ) Ratio\n(If AG elevated)',
'Δ/Δ = (AG - 12) / (24 - HCO₃)\nRatio 1–2 = Pure AG acidosis\nRatio < 1 = Concurrent non-AG metabolic acidosis\nRatio > 2 = Concurrent metabolic alkalosis or chronic resp. acidosis'],
]
step_tbl = Table(steps_data,
colWidths=[2.2*cm, 4.5*cm, (PAGE_W - 2*MARGIN - 6.7*cm)])
step_tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('TEXTCOLOR', (0,0), (-1,0), white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 9),
('ROWBACKGROUNDS',(0,1), (-1,-1), [white, LIGHT_BLUE]),
('BACKGROUND', (0,1), (0,-1), MED_BLUE),
('TEXTCOLOR', (0,1), (0,-1), white),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('ALIGN', (0,0), (0,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('TOPPADDING', (0,0), (-1,-1), 7),
('BOTTOMPADDING', (0,0), (-1,-1), 7),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
('GRID', (0,0), (-1,-1), 0.5, HexColor('#CCCCCC')),
]))
story.append(step_tbl)
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 3 — ACID-BASE DISORDERS
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 3: Acid-Base Disorders — Causes & Management', ACCENT_GREEN))
story.append(S(1, 0.3*cm))
story.append(Paragraph(
'Acid-base disorders are found in 51–56% of hospitalised patients. Prevalence: Respiratory alkalosis '
'(29–42%) > Metabolic alkalosis (16–28%) > Respiratory acidosis (26–27%) > Metabolic acidosis (10–12%). '
'Mixed disorders account for ~6% (Barash Clinical Anaesthesia, 9e).',
body))
story.append(S(1, 0.3*cm))
# 3a — Metabolic Acidosis
story.append(KeepTogether([
Paragraph('3a. Metabolic Acidosis', h2),
Paragraph(
'<b>Definition:</b> pH↓, HCO₃⁻↓, PaCO₂↓ (respiratory compensation). '
'Physiologic consequence: pH <7.2 causes impaired myocardial contractility, '
'pulmonary hypertension, arrhythmia threshold↓, insulin resistance, hyperkalemia, '
'decreased responsiveness to catecholamines.',
body),
]))
ma_left = ['Ketoacidosis (DKA, alcoholic, starvation)',
'Lactic acidosis (sepsis, shock, tissue hypoxia)',
'Uraemia / Advanced CKD',
'Toxins: Methanol, Ethylene glycol, Salicylates',
'Paraldehyde, Propylene glycol, Metformin']
ma_right = ['Renal Tubular Acidosis (type 1, 2, 4)',
'Diarrhoea (GI HCO₃⁻ loss)',
'Carbonic anhydrase inhibitors (acetazolamide)',
'0.9% Saline (hyperchloraemic)',
'Ureteroenteric diversion, Biliary fistula']
story.append(two_col_box('HIGH AG Causes (AG > 13) — MUDPILES', ma_left,
'NORMAL AG Causes (Hyperchloraemic)', ma_right,
lbg=LIGHT_AMBER, lborder=ACCENT_AMBER,
rbg=LIGHT_BLUE, rborder=MED_BLUE))
story.append(S(1, 0.3*cm))
story.append(Paragraph(
'<b>Management:</b> Treat the underlying cause. Ventilatory compensation must be maintained if '
'patient is on MV. No strong evidence for routine NaHCO₃ in critically ill patients unless pH <7.2 '
'in septic/hypovolemic shock (BICAR-ICU trial — no mortality benefit at 28 days with bicarbonate '
'vs. placebo in mixed organ failure). For patients on MV, adjust ventilator to maintain compensatory '
'hyperventilation; correct shock, hypoxia, and metabolic causes.',
body))
story.append(S(1, 0.3*cm))
# 3b — Metabolic Alkalosis
story.append(Paragraph('3b. Metabolic Alkalosis', h2))
story.append(Paragraph(
'<b>Definition:</b> pH↑, HCO₃⁻↑, PaCO₂↑ (respiratory compensation, hypoventilation). '
'Expected PaCO₂ = 40 + 0.7 × (HCO₃measured − 24). '
'Compensation is limited by hypoxic drive; PaCO₂ rarely exceeds 55 mmHg.',
body))
malk_left = ['Vomiting / NG suction (Cl⁻ and H⁺ loss)',
'Diuretics (loop/thiazide — K⁺ and Cl⁻ loss)',
'Contraction alkalosis',
'Post-hypercapnic alkalosis']
malk_right = ['Primary hyperaldosteronism / Cushing\'s',
'Excess NaHCO₃ or antacid ingestion',
'Milk-alkali syndrome',
'Hypomagnesaemia, Hypokalaemia']
story.append(two_col_box('Saline-Responsive (UCl⁻ < 20 mEq/L)', malk_left,
'Saline-Resistant (UCl⁻ > 20 mEq/L)', malk_right,
lbg=LIGHT_GREEN, lborder=ACCENT_GREEN,
rbg=LIGHT_PURPLE, rborder=ACCENT_PURPLE))
story.append(S(1, 0.3*cm))
# 3c — Respiratory Acidosis
story.append(Paragraph('3c. Respiratory Acidosis', h2))
story.append(Paragraph(
'<b>Definition:</b> pH↓, PaCO₂↑ (>45 mmHg), HCO₃⁻↑ (compensation). '
'<b>Cause = alveolar hypoventilation.</b> '
'Acute: HCO₃ +1 per 10↑PaCO₂. Chronic: HCO₃ +3.5 per 10↑PaCO₂.',
body))
story.append(two_col_box('Central Causes', [
'CNS depression (opioids, benzos, sedatives)',
'Brainstem lesion / CVA',
'Obesity hypoventilation (OHS)',
'Central sleep apnoea'],
'Peripheral / Mechanical Causes', [
'COPD exacerbation, severe asthma',
'Neuromuscular disease (GBS, MG, SCI)',
'Pneumothorax, Haemothorax, ARDS',
'Chest wall deformity, Massive obesity'],
lbg=LIGHT_RED, lborder=ACCENT_RED,
rbg=LIGHT_AMBER, rborder=ACCENT_AMBER))
story.append(S(1, 0.3*cm))
story.append(Paragraph(
'<b>Management:</b> Treat underlying cause. If patient cannot compensate, NIV (BiPAP) is first-line '
'for COPD (reduces mortality, avoids intubation). Intubate if: pH <7.25, declining mentation, '
'haemodynamic instability, mask intolerance, failure of NIV in 30–120 min.',
body))
story.append(S(1, 0.3*cm))
# 3d — Respiratory Alkalosis
story.append(Paragraph('3d. Respiratory Alkalosis', h2))
story.append(Paragraph(
'<b>Definition:</b> pH↑, PaCO₂↓ (<35 mmHg), HCO₃⁻↓ (compensation). Most common acid-base '
'disorder in hospitalised patients. Acute: HCO₃ -2 per 10↓PaCO₂. Chronic: HCO₃ -5 per 10↓PaCO₂.',
body))
ra_causes = ['Anxiety / Pain / Psychogenic hyperventilation',
'Fever, Sepsis (early), Pregnancy',
'Pulmonary embolism (V/Q mismatch triggers hyperventilation)',
'Liver failure (central hyperventilation)',
'Salicylate toxicity (early phase)',
'Hypoxia-driven hyperventilation (altitude, pneumonia)',
'Iatrogenic (excessive MV settings)']
story.append(color_box('Common Causes of Respiratory Alkalosis',
[Paragraph(f'• {c}', bullet_sm) for c in ra_causes],
bg=LIGHT_BLUE, border=MED_BLUE, title_bg=MED_BLUE))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 4 — OXYGENATION & HYPOXAEMIA
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 4: Oxygenation & Hypoxaemia', ACCENT_PURPLE))
story.append(S(1, 0.4*cm))
story.append(Paragraph('Causes of Hypoxaemia — 5 Mechanisms', h2))
hypox_headers = ['Mechanism', 'A-aDO₂', 'PaCO₂', 'Response to O₂', 'Examples']
hypox_rows = [
['Hypoventilation', 'Normal', '↑', 'Good', 'Sedation, OHS, NMD'],
['V/Q Mismatch', '↑', 'N or ↓', 'Good', 'COPD, asthma, pneumonia, PE'],
['Diffusion Defect', '↑', 'N or ↓', 'Good', 'ILD, emphysema, early ARDS'],
['Right-to-Left Shunt', '↑', 'N or ↓', 'Poor (<10% response)', 'ARDS, ASD/VSD, hepatopulmonary'],
['Low FiO₂ / Altitude', 'Normal', 'N or ↓', 'Good', 'High altitude, confined space'],
]
story.append(data_table(hypox_headers, hypox_rows,
col_widths=[3.5*cm, 2.5*cm, 2*cm, 4.5*cm, 4.7*cm],
header_color=ACCENT_PURPLE))
story.append(S(1, 0.4*cm))
story.append(Paragraph('PaO₂/FiO₂ Ratio (P/F Ratio) — ARDS Classification (Berlin 2012)', h2))
pf_headers = ['P/F Ratio (mmHg)', 'ARDS Category', 'PEEP Requirement', 'Mortality']
pf_rows = [
['>400', 'Normal', '—', '—'],
['300–400', 'Borderline / Early ALI', '≥5 cmH₂O', 'Low'],
['200–300', 'Mild ARDS', '≥5 cmH₂O', '27%'],
['100–200', 'Moderate ARDS', '≥5 cmH₂O', '32%'],
['<100', 'Severe ARDS', '≥5 cmH₂O', '45%'],
]
story.append(data_table(pf_headers, pf_rows,
col_widths=[4*cm, 5*cm, 4.5*cm, 3.7*cm],
header_color=ACCENT_RED))
story.append(S(1, 0.4*cm))
story.append(color_box('SpO₂ vs. ABG — Key Limitation',
[Paragraph(
'Pulse oximetry CANNOT detect hypoventilation in patients on supplemental oxygen. '
'The sigmoid shape of the O₂-Hb dissociation curve means PaO₂ can fall significantly '
'before SpO₂ drops. PaCO₂ can rise to dangerous levels while SpO₂ appears normal. '
'<b>Always use ABG or capnography when hypercarbia is suspected</b> — especially in patients '
'receiving supplemental O₂. Errors also occur with carboxyhemoglobin, methemoglobin, '
'dark nail polish, poor perfusion, or jaundice (Murray & Nadel, 2022).',
body_sm)],
bg=LIGHT_AMBER, border=ACCENT_AMBER, title_bg=ACCENT_AMBER))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 5 — MECHANICAL VENTILATION MODES
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 5: Mechanical Ventilation — Modes', DARK_GREY))
story.append(S(1, 0.4*cm))
story.append(Paragraph(
'Mechanical ventilation replaces or supplements the work of breathing. Understanding '
'ventilator modes is essential for appropriate management of critically ill patients.',
body))
story.append(Paragraph('Ventilator Modes Overview', h2))
modes_headers = ['Mode', 'Abbreviation', 'Trigger', 'Control Variable', 'Clinical Use']
modes_rows = [
['Volume Control – Assist Control', 'VC-AC / A/C', 'Patient or time', 'Fixed tidal volume', 'Standard first-line mode in ICU; guarantees VT'],
['Pressure Control – Assist Control','PC-AC', 'Patient or time', 'Fixed PIP', 'ARDS (limits barotrauma); variable VT'],
['Synchronised Intermittent Mandatory Ventilation', 'SIMV', 'Patient or time', 'Volume or Pressure', 'Weaning (controversial — now less used)'],
['Pressure Support Ventilation', 'PSV / PS', 'Patient', 'Pressure support level', 'Spontaneous breathing trials; weaning'],
['High-Frequency Oscillatory Ventilation', 'HFOV', 'Machine', 'Pressure amplitude', 'Severe ARDS rescue; paediatric'],
['Airway Pressure Release Ventilation', 'APRV', 'Patient', 'CPAP + release time', 'ARDS — open-lung approach'],
['Continuous Positive Airway Pressure', 'CPAP', 'Patient fully', 'Constant pressure', 'Spontaneous breathing + PEEP only; NIV/post-extubation'],
['BiLevel Positive Airway Pressure', 'BiPAP', 'Patient / timed', 'Two pressure levels (IPAP/EPAP)', 'NIV for COPD, OHS, CHF; avoids intubation'],
]
story.append(data_table(modes_headers, modes_rows,
col_widths=[3.5*cm, 2.2*cm, 2.8*cm, 3.2*cm, 5.5*cm],
header_color=DARK_GREY))
story.append(S(1, 0.4*cm))
story.append(Paragraph('Volume Control vs. Pressure Control — Key Differences', h3))
vpc_headers = ['Feature', 'Volume Control (VC)', 'Pressure Control (PC)']
vpc_rows = [
['VT delivery', 'Fixed, guaranteed', 'Variable (depends on compliance & resistance)'],
['Peak pressure', 'Variable — can be dangerously high', 'Fixed — safer from barotrauma perspective'],
['Flow pattern', 'Square (decelerating optional)', 'Decelerating (more physiological)'],
['Risk', 'Volutrauma/barotrauma if compliance↓', 'Inadequate VT if compliance changes'],
['Alarm priority', 'Monitor peak pressure', 'Monitor tidal volume'],
['Best for', 'Obstructive (asthma, COPD)', 'ARDS, restrictive lung disease'],
]
story.append(data_table(vpc_headers, vpc_rows,
col_widths=[3.5*cm, 6.5*cm, 7.2*cm],
header_color=MED_BLUE))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 6 — VENTILATOR SETTINGS
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 6: Initial Ventilator Settings', MED_BLUE))
story.append(S(1, 0.4*cm))
story.append(Paragraph(
'Settings below are for a typical adult patient on Volume Assist-Control. Adjust for clinical '
'context (ARDS, COPD, asthma, neuromuscular disease, post-op). Always reassess within 30–60 '
'minutes with ABG and clinical response.',
body))
settings_headers = ['Parameter', 'Standard Setting', 'ARDS Protocol', 'COPD/Asthma', 'Rationale']
settings_rows = [
['FiO₂', 'Start 1.0 (100%)\ntitrate to SpO₂ 94–98%',
'Titrate to SpO₂ 88–95%\n(permissive hypoxaemia)',
'Titrate to SpO₂ ≥92%',
'Avoid O₂ toxicity; target minimum FiO₂ to achieve adequate SpO₂'],
['Tidal Volume (VT)', '6–8 mL/kg IBW\n(max 10 mL/kg)',
'<b>6 mL/kg IBW</b> (lung-protective; ARDSnet)',
'6–8 mL/kg IBW',
'Low VT reduces VILI (ventilator-induced lung injury). IBW = ideal body weight'],
['Respiratory Rate', '12–16 breaths/min',
'16–25 (adjust to pH ≥7.25)',
'<b>≤10 breaths/min</b> (allow expiration time)',
'Low RR in obstructive disease prevents air trapping (auto-PEEP)'],
['PEEP', '5 cmH₂O',
'<b>Higher PEEP ≥8–15 cmH₂O</b>\n(per ARDSnet PEEP/FiO₂ table)',
'Low: 0–5 cmH₂O\n(match to intrinsic PEEP)',
'Recruits atelectatic alveoli; prevents cyclical collapse. Risk: barotrauma if excessive'],
['Peak Inspiratory Pressure (PIP)', '<35 cmH₂O preferred',
'<b><30 cmH₂O</b>\n(limit plateau P)',
'<40 cmH₂O acceptable in severe obstruction',
'High PIP = barotrauma risk. Difference (PIP – Plateau P) = airway resistance'],
['Plateau Pressure (Pplat)', 'Monitor; <30 cmH₂O ideal',
'<b>≤30 cmH₂O</b> (ARDSnet protocol)',
'May be elevated due to air trapping; true Pplat may be lower',
'Reflects alveolar distension. Calculate: pause inspiratory hold for 0.5 sec'],
['I:E Ratio', '1:2 (default)',
'1:2 to 1:3',
'<b>1:3 to 1:4</b> (prolonged expiration to prevent air trapping)',
'Obstructive disease needs long expiratory time. Inverse I:E ratio used in ARDS rescue'],
['Inspiratory Flow Rate', '40–60 L/min',
'40–60 L/min',
'<b>>60 L/min</b> (allows more expiratory time)',
'High flow rate in COPD/asthma shortens inspiratory time, prolongs expiration'],
['Trigger Sensitivity', '-1 to -2 cmH₂O (pressure)\nor 1–2 L/min (flow)',
'-1 to -2 cmH₂O',
'-1 to -2 cmH₂O',
'Too sensitive = auto-triggering. Too insensitive = patient-ventilator dyssynchrony'],
]
story.append(data_table(settings_headers, settings_rows,
col_widths=[3.2*cm, 3.8*cm, 3.8*cm, 3.8*cm, 5.6*cm],
header_color=MED_BLUE))
story.append(S(1, 0.4*cm))
story.append(Paragraph('Ideal Body Weight (IBW) Calculator', h3))
story.append(Paragraph(
'<b>Males:</b> IBW (kg) = 50 + 2.3 × (height in inches – 60)<br/>'
'<b>Females:</b> IBW (kg) = 45.5 + 2.3 × (height in inches – 60)<br/>'
'Or: <b>Males:</b> 50 + 0.91 × (height cm – 152.4) | <b>Females:</b> 45.5 + 0.91 × (height cm – 152.4)',
formula_style))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 7 — LUNG-PROTECTIVE VENTILATION (ARDSnet)
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 7: Lung-Protective Ventilation & ARDS Protocol', ACCENT_RED))
story.append(S(1, 0.4*cm))
story.append(Paragraph(
'The ARDSNet ARMA trial (2000) demonstrated a 22% relative reduction in mortality '
'using low-tidal-volume ventilation (6 mL/kg IBW vs. 12 mL/kg IBW). '
'Goldman-Cecil Medicine describes ARDS as diffuse lung injury with severe hypoxaemia (P/F ratio-based), '
'bilateral infiltrates, not explained by cardiac failure.',
body))
story.append(Paragraph('ARDSnet Low-Tidal-Volume Protocol — Step by Step', h2))
ardsnet = [
'1. Set VT = 8 mL/kg IBW initially, then reduce by 1 mL/kg every 2 hours to target 6 mL/kg IBW',
'2. Set initial RR to maintain minute ventilation; adjust to pH target (not CO₂)',
'3. Maintain Plateau Pressure ≤30 cmH₂O (measure every 4 hours via inspiratory hold)',
'4. Use PEEP/FiO₂ table to optimise oxygenation (higher PEEP in moderate-severe ARDS)',
'5. Target SpO₂ 88–95% or PaO₂ 55–80 mmHg (permissive hypoxaemia acceptable)',
'6. Target pH 7.25–7.45. If pH <7.25 despite max RR (35/min): consider NaHCO₃ infusion',
'7. If plateau pressure >30 cmH₂O: reduce VT by 1 mL/kg (minimum 4 mL/kg IBW)',
]
for step in ardsnet:
story.append(Paragraph(step, bullet))
story.append(S(1, 0.3*cm))
story.append(Paragraph('PEEP / FiO₂ Table (ARDSnet — Higher PEEP Strategy)', h3))
peep_headers = ['FiO₂', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '1.0']
peep_lower = ['Lower PEEP', '5', '5–8', '8–10', '10', '10–12', '14', '14–18', '18–24']
peep_higher = ['Higher PEEP', '5–14', '14–16', '16–18', '20', '20', '20–22', '22', '22–24']
pf_tbl = Table([peep_headers, peep_lower, peep_higher],
colWidths=[(PAGE_W - 2*MARGIN)/9] * 9)
pf_tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('TEXTCOLOR', (0,0), (-1,0), white),
('FONTNAME', (0,0), (-1,-1), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 8.5),
('ROWBACKGROUNDS',(0,1), (-1,-1), [LIGHT_BLUE, LIGHT_GREEN]),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('GRID', (0,0), (-1,-1), 0.5, HexColor('#CCCCCC')),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
]))
story.append(pf_tbl)
story.append(S(1, 0.3*cm))
story.append(Paragraph('Rescue Therapies in Severe ARDS', h3))
rescue_therapies = [
'Prone positioning (≥16 hrs/day) — reduces mortality in severe ARDS (PROSEVA trial, P/F <150)',
'Neuromuscular blockade (cisatracurium 48 hrs) — considered in P/F <150 (ACURASYS trial); ROSE trial showed no benefit at 90 days — use selectively',
'Inhaled pulmonary vasodilators (NO, prostacyclin) — improves oxygenation; no mortality benefit',
'High-frequency oscillatory ventilation (HFOV) — rescue only (OSCAR/OSCILLATE trials showed no benefit)',
'Extracorporeal membrane oxygenation (ECMO) — for refractory ARDS unresponsive to all measures',
'Recruitment manoeuvres — sustained inflation (40 cmH₂O × 40 sec) or stepwise; use cautiously',
]
for th in rescue_therapies:
story.append(Paragraph(f'• {th}', bullet))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 8 — VENT SETTINGS FOR SPECIFIC CONDITIONS
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 8: Disease-Specific Ventilator Strategies', ACCENT_AMBER))
story.append(S(1, 0.4*cm))
# COPD
story.append(Paragraph('COPD Exacerbation', h2))
story.append(color_box('COPD — Ventilation Principles',
[Paragraph(p, body_sm) for p in [
'• NIV (BiPAP) is PREFERRED over invasive MV for acute COPD exacerbation when patient is cooperative, haemodynamically stable, '
'and SpO₂ responsive. Intubate if pH <7.25 after 30–120 min NIV trial, deteriorating mentation, or haemodynamic instability '
'(Goldman-Cecil Medicine).',
'• Goal: Prevent auto-PEEP (breath stacking). Set low RR (8–10/min), high inspiratory flow (>60 L/min), prolonged I:E ratio (1:3 to 1:4).',
'• PEEP: Match extrinsic PEEP to ~80% of intrinsic (auto) PEEP measured on expiratory hold. This reduces work of breathing without '
'causing overdistension.',
'• Permissive hypercapnia is acceptable — pH target ≥7.25. Do not normalise CO₂ rapidly (risks post-hypercapnic alkalosis).',
'• Oxygen target SpO₂ 88–92% to prevent suppression of hypoxic drive in chronic CO₂ retainers.',
]],
bg=LIGHT_BLUE, border=MED_BLUE, title_bg=DARK_BLUE))
story.append(S(1, 0.3*cm))
# Asthma
story.append(Paragraph('Severe Asthma (Status Asthmaticus)', h2))
story.append(color_box('Asthma — Ventilation Principles',
[Paragraph(p, body_sm) for p in [
'• Intubation indications: Coma, cardiac/respiratory arrest, paradoxical breathing, refractory hypoxaemia, failed NIV. '
'Use RSI with Ketamine (1–2 mg/kg preferred — bronchodilatory) or Propofol (1.5–2 mg/kg — caution: hypotension). '
'Succinylcholine 1.5 mg/kg or Rocuronium 1 mg/kg for paralysis (Rosen\'s EM).',
'• ETT size ≥8.0 mm (facilitates suctioning, mucous plug removal, bronchoscopy).',
'• Permissive hypercapnia strategy: Decrease hyperinflation is priority, not normalising CO₂. '
'PaCO₂ can rise but avoid >100 mmHg (cerebral vasodilation risk, max CBF at PaCO₂ 120 mmHg).',
'• VT: 6–8 mL/kg IBW. RR: <10/min. High inspiratory flow >60 L/min. Low PEEP initially (0–5 cmH₂O).',
'• Extrinsic PEEP may be titrated to match intrinsic PEEP to improve triggering and reduce WOB — do not exceed intrinsic PEEP.',
'• Target SpO₂ >92%. Inline bronchodilators, IV corticosteroids, IV magnesium, aggressive sedation (avoid histamine-releasing agents like morphine — use fentanyl/remifentanil).',
'• Monitor: Auto-PEEP (expiratory hold), PIP, plateau pressure, capnography continuously.',
]],
bg=LIGHT_GREEN, border=ACCENT_GREEN, title_bg=ACCENT_GREEN))
story.append(S(1, 0.3*cm))
# Post-op
story.append(Paragraph('Post-Operative Ventilation', h2))
post_op_items = [
'Standard: VC-AC, VT 6–8 mL/kg, RR 12–14, PEEP 5, FiO₂ to wean as tolerated',
'Wean FiO₂ to ≤0.40 as SpO₂ allows; then wean to PSV',
'Extubate when: Alert, follows commands, intact cough/gag, SpO₂ ≥95% on FiO₂ ≤0.40, PEEP ≤5',
'Spontaneous Breathing Trial (SBT): T-piece or PSV 5–8 + PEEP 5 for 30–120 min',
'Rapid Shallow Breathing Index (RSBI = RR/VT) <105 breaths/min/L predicts extubation success',
]
for it in post_op_items:
story.append(Paragraph(f'• {it}', bullet))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 9 — COMPLICATIONS & TROUBLESHOOTING
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 9: Complications & Troubleshooting on MV', ACCENT_RED))
story.append(S(1, 0.4*cm))
story.append(Paragraph('Ventilator Alarms — Common Causes & Actions', h2))
alarm_headers = ['Alarm', 'Likely Causes', 'Immediate Action']
alarm_rows = [
['High Peak Pressure',
'Secretions/mucous plug, Bronchospasm, Kinking of ETT, Pneumothorax, Biting on tube',
'Suction airway; check ETT position; listen for bilateral BS; check for auto-PEEP; stat CXR'],
['High Plateau Pressure\n(>30 cmH₂O)',
'Decreased compliance (ARDS, pulmonary oedema, pneumothorax), Auto-PEEP, Pneumothorax',
'Reduce VT; check for auto-PEEP; inspiratory hold to measure plateau; consider needle decompression if PTX suspected'],
['Low Tidal Volume',
'Patient-vent dyssynchrony, Leak (circuit, ETT cuff), Worsening compliance, Sedation inadequate',
'Check circuit connections; check ETT cuff pressure (target 20–30 cmH₂O); increase sedation if dysynchrony'],
['Low SpO₂ / Desaturation',
'ETT displacement, Mucus plug, Pneumothorax, ARDS worsening, Sputum plugging',
'Manual ventilate with 100% O₂ first; suction; verify ETT position; auscultate; urgent CXR'],
['Apnoea Alarm',
'Apnoea (patient asleep/sedated), ETT disconnection, Trigger threshold too insensitive',
'Check patient and connections; adjust trigger sensitivity; increase mandatory breath rate if apnoeic'],
['Auto-PEEP\n(Intrinsic PEEP)',
'Obstructive disease (COPD/asthma), High RR, Short expiratory time, High minute ventilation',
'Decrease RR; decrease VT; increase I:E ratio (more expiratory time); disconnect and allow full exhalation'],
]
story.append(data_table(alarm_headers, alarm_rows,
col_widths=[3.5*cm, 6.5*cm, 7.2*cm],
header_color=ACCENT_RED))
story.append(S(1, 0.4*cm))
story.append(Paragraph('Ventilator-Associated Complications', h2))
complic_left = [
'Ventilator-Induced Lung Injury (VILI)',
'Barotrauma: pneumothorax, pneumomediastinum',
'Volutrauma: alveolar overdistension',
'Atelectrauma: cyclical opening/closing',
'Biotrauma: inflammatory mediator release',
]
complic_right = [
'Ventilator-Associated Pneumonia (VAP)',
'Haemodynamic compromise (↓ preload)',
'Diaphragm atrophy (ventilator-induced)',
'Oxygen toxicity (FiO₂ >0.6 prolonged)',
'Tracheal injury from ETT/suctioning',
]
story.append(two_col_box('Lung Complications', complic_left,
'Systemic Complications', complic_right,
lbg=LIGHT_RED, lborder=ACCENT_RED,
rbg=LIGHT_AMBER, rborder=ACCENT_AMBER))
story.append(S(1, 0.4*cm))
story.append(color_box('VAP Prevention Bundle (Standard ICU Protocol)',
[Paragraph(f'• {item}', bullet_sm) for item in [
'Head of bed elevation 30–45°',
'Daily sedation interruption + spontaneous breathing trial',
'Oral care with chlorhexidine every 6–8 hours',
'Subglottic secretion drainage (if available)',
'Minimise unnecessary circuit changes',
'DVT prophylaxis and stress ulcer prophylaxis',
]],
bg=LIGHT_GREEN, border=ACCENT_GREEN, title_bg=ACCENT_GREEN))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 10 — WEANING FROM MV
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 10: Weaning from Mechanical Ventilation', MED_BLUE))
story.append(S(1, 0.4*cm))
story.append(Paragraph('Readiness Criteria for Weaning', h2))
readiness = [
'Underlying cause of respiratory failure is resolving or resolved',
'Patient is awake, alert, and able to follow simple commands',
'Haemodynamically stable (vasopressors low-dose or off)',
'FiO₂ ≤0.40–0.50 and SpO₂ ≥92–95% on those settings',
'PEEP ≤5–8 cmH₂O',
'Adequate cough reflex (for secretion clearance)',
'Absence of excessive secretions',
'No unresolved pneumothorax or significant fluid overload',
]
for r in readiness:
story.append(Paragraph(f'• {r}', bullet))
story.append(S(1, 0.3*cm))
story.append(Paragraph('Weaning Predictors', h2))
wean_headers = ['Predictor', 'Formula / Threshold', 'Interpretation']
wean_rows = [
['RSBI (Rapid Shallow Breathing Index)',
'RSBI = RR / VT (L)\nMeasure on minimal support for 1 min',
'<105 = likely to succeed\n>105 = likely to fail'],
['Negative Inspiratory Force (NIF / MIP)',
'Maximum inspiratory pressure\n(20-sec valve occlusion)',
'More negative than -20 to -30 cmH₂O = adequate\n<-20 cmH₂O = weak; poor prognosis'],
['Minute Ventilation (Ve)',
'VT × RR\nMeasure on no support',
'<10–12 L/min suggests adequate ventilatory reserve'],
['P0.1 (Airway Occlusion Pressure)',
'Inspiratory pressure generated in first 0.1 sec against occluded airway',
'<6 cmH₂O = low drive (easy wean)\n>6 cmH₂O = high drive (difficult wean)'],
['Spontaneous Breathing Trial (SBT)',
'30–120 min on T-piece or\nPSV 5–7 + PEEP 5',
'Pass = extubate if no signs of failure (RR >35, SpO₂ <90%, use of accessory muscles, agitation, diaphoresis)'],
]
story.append(data_table(wean_headers, wean_rows,
col_widths=[4.5*cm, 5*cm, 7.7*cm]))
story.append(S(1, 0.4*cm))
story.append(Paragraph('Post-Extubation Management', h3))
post_ext = [
'High-flow nasal cannula (HFNC) — preferred for hypoxaemic patients post-extubation (reduces reintubation risk)',
'NIV (BiPAP) — particularly useful in post-extubation respiratory failure in COPD, cardiac patients',
'Criteria for reintubation: RR >35, SpO₂ <88% on max O₂, PaCO₂ rising + pH falling, haemodynamic instability, GCS decline',
'Reintubation within 48 hours associated with higher ICU and hospital mortality — extubate carefully',
]
for item in post_ext:
story.append(Paragraph(f'• {item}', bullet))
story.append(PageBreak())
# ════════════════════════════════════════════════════════
# SECTION 11 — QUICK REFERENCE SUMMARY
# ════════════════════════════════════════════════════════
story.append(section_header('SECTION 11: Quick Reference Summary Cards', DARK_BLUE))
story.append(S(1, 0.3*cm))
# ABG quick card
abg_qr = [
['pH', '7.35–7.45', '<7.35 = Acidaemia', '>7.45 = Alkalemia'],
['PaCO₂', '35–45 mmHg', '↑ = Resp Acidosis / Met Comp', '↓ = Resp Alkalosis / Met Comp'],
['HCO₃⁻', '22–26 mEq/L', '↓ = Met Acidosis / Resp Comp', '↑ = Met Alkalosis / Resp Comp'],
['BE', '-2 to +2', '< -2 = Base Deficit (acidosis)', '> +2 = Base Excess (alkalosis)'],
['PaO₂', '80–100 mmHg', '<60 = Significant hypoxaemia', '<40 = Severe/life-threatening'],
['P/F ratio', '>300', '<200 = Moderate ARDS', '<100 = Severe ARDS'],
]
qr_tbl = Table([['Parameter','Normal','Low Value Means','High Value Means']] + abg_qr,
colWidths=[3*cm, 3.5*cm, 5.5*cm, 5.2*cm])
qr_tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), DARK_BLUE),
('TEXTCOLOR', (0,0), (-1,0), white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 9),
('ROWBACKGROUNDS',(0,1), (-1,-1), [white, LIGHT_BLUE]),
('GRID', (0,0), (-1,-1), 0.5, HexColor('#CCCCCC')),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))
story.append(Paragraph('ABG Parameters at a Glance', h2))
story.append(qr_tbl)
story.append(S(1, 0.4*cm))
story.append(Paragraph('Compensation Formulas — Quick Reference', h2))
comp_rows = [
['Metabolic Acidosis', 'PaCO₂ = 1.5 × HCO₃ + 8 ± 2 (Winter\'s formula)'],
['Metabolic Alkalosis', 'PaCO₂ = 40 + 0.7 × (HCO₃ − 24)'],
['Resp. Acidosis Acute', 'HCO₃ ↑ 1 mEq/L per 10 mmHg ↑ PaCO₂'],
['Resp. Acidosis Chronic', 'HCO₃ ↑ 3.5 mEq/L per 10 mmHg ↑ PaCO₂'],
['Resp. Alkalosis Acute', 'HCO₃ ↓ 2 mEq/L per 10 mmHg ↓ PaCO₂'],
['Resp. Alkalosis Chronic','HCO₃ ↓ 5 mEq/L per 10 mmHg ↓ PaCO₂'],
]
comp_tbl = Table([['Primary Disorder','Expected Compensation']] + comp_rows,
colWidths=[6*cm, (PAGE_W - 2*MARGIN - 6*cm)])
comp_tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), ACCENT_GREEN),
('TEXTCOLOR', (0,0), (-1,0), white),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,-1), 9.5),
('ROWBACKGROUNDS',(0,1), (-1,-1), [LIGHT_GREEN, white]),
('GRID', (0,0), (-1,-1), 0.5, HexColor('#CCCCCC')),
('FONTNAME', (0,1), (-1,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1), (0,-1), ACCENT_GREEN),
('TOPPADDING', (0,0), (-1,-1), 7),
('BOTTOMPADDING', (0,0), (-1,-1), 7),
('LEFTPADDING', (0,0), (-1,-1), 10),
]))
story.append(comp_tbl)
story.append(S(1, 0.4*cm))
# Initial vent settings quick-ref
story.append(Paragraph('Ventilator Initial Settings — Quick Card', h2))
vent_qr = [
['FiO₂', '1.0 (100%)', 'Titrate SpO₂ 94–98%', '≤0.40 goal to avoid O₂ toxicity'],
['VT', '6–8 mL/kg IBW', '6 mL/kg (ARDS)', 'Never >10 mL/kg'],
['RR', '12–16 /min', '≤10 (COPD/asthma)', '16–25 (ARDS)'],
['PEEP', '5 cmH₂O', '8–15+ (ARDS)', '0–5 (obstructive disease)'],
['I:E', '1:2', '1:3 to 1:4 (COPD)', 'Inverse 2:1 in ARDS rescue'],
['Flow Rate', '40–60 L/min', '>60 L/min (COPD/asthma)', 'Standard for most others'],
['PIP target', '<35 cmH₂O', '<40 acceptable (asthma)', 'Alarm at 40 cmH₂O'],
['Plateau P', '<30 cmH₂O', 'Strict <30 in ARDS', 'Measure via inspiratory hold'],
['Trigger', '-1 to -2 cmH₂O', 'Flow trigger 1–2 L/min', 'Avoid auto-triggering'],
]
story.append(data_table(['Parameter','Standard','COPD/Asthma','ARDS / Note'],
vent_qr,
col_widths=[3.5*cm, 4*cm, 4.5*cm, 5.2*cm]))
story.append(S(1, 0.4*cm))
# Footer references
story.append(HRFlowable(width='100%', thickness=1, color=DARK_BLUE))
story.append(S(1, 0.2*cm))
refs = [
'Murray & Nadel\'s Textbook of Respiratory Medicine (2022) — Oxygenation detection, ABG, pulmonary oedema',
'Barash, Cullen & Stoelting\'s Clinical Anaesthesia, 9th Ed — Acid-base interpretation, ABG stepwise approach',
'Goldman-Cecil Medicine, International Ed — COPD acute respiratory failure, ARDS definition & management',
'Rosen\'s Emergency Medicine, 9th Ed — Mechanical ventilation in asthma, RSI, permissive hypercapnia',
'Miller\'s Anaesthesia, 10th Ed — Ventilator modes, pressure support, PEEP titration',
'ARDSNet ARMA Trial (2000) — Low tidal volume ventilation protocol',
]
story.append(Paragraph('References & Sources', h3))
for r in refs:
story.append(Paragraph(f'• {r}', ref_style))
# Build PDF
doc.build(story)
print("PDF generated successfully!")
print("File: /home/daytona/workspace/abg-ventilator/ABG_Ventilator_Settings.pdf")