Can you write a module for PLS with index
Pediatric life support
| Age Group | Survival to Discharge |
|---|---|
| Infants (< 1 year) | 3.3% |
| Children (1-11 years) | 9.1% |
| Adolescents (12-19 years) | 8.9% |
Source: Miller's Anesthesia, 10e
| Age | Hand Placement | Depth |
|---|---|---|
| Infants (< 1 year) | 2 fingers just below inframammary line on sternum | ~1.5 inches (4 cm) |
| Children (1 year to puberty) | 1 or 2 hands on lower half of sternum (avoid xiphoid) | ~2 inches (5 cm) |
| Adolescents (post-puberty, average adult size) | Standard adult technique | At least 5 cm, max 6 cm |
| Scenario | Preferred Device |
|---|---|
| Child < 8 years, trained provider identifies shockable rhythm | Manual defibrillator |
| Child < 8 years, no manual defibrillator | AED with pediatric attenuator + pediatric pads |
| No pediatric equipment available | Adult AED - use without hesitation |
| Blade Type | Age / Use |
|---|---|
| Miller #00-1 (straight) | Premature - 2 months |
| Miller #1 (straight) | 3 months - 3 years |
| Miller #2 (straight) | > 3 years / difficult airway |
| Macintosh (curved) | Usually more effective > 2 years |
| H's | T's |
|---|---|
| Hypovolemia | Tension pneumothorax |
| Hypoxia | Tamponade (cardiac) |
| Hydrogen ion (acidosis) | Toxins |
| Hypo/Hyperkalemia | Thrombosis (pulmonary) |
| Hypothermia | Thrombosis (coronary) |

| Drug | Indication | Dose | Key Notes |
|---|---|---|---|
| Epinephrine | Asystole, PEA, VF, pVT, bradycardia, diastolic hypotension | 0.01 mg/kg IV/IO (0.1 mg/mL, max 1 mg); 0.1 mg/kg ET (1 mg/mL, max 2.5 mg); repeat every 3-5 min; infusion: 0.05-2 mcg/kg/min | Alpha-agonism increases aortic diastolic pressure and coronary perfusion pressure |
| Adenosine | SVT (AV node reentry or accessory pathway) | 1st: 0.1 mg/kg IV (max 6 mg); 2nd: 0.2 mg/kg IV (max 12 mg); 3rd: 0.3 mg/kg IV (max 12 mg) - wait 2 min between doses; rapid push with 3-way stopcock | Blocks AV node conduction; causes brief asystole (10-15 sec) |
| Amiodarone | Shock-refractory VF/pVT, refractory SVT | 5 mg/kg IV/IO (max 300 mg); no pulse: push undiluted; pulse: dilute, run over 20-60 min; repeat up to 15 mg/kg total | Potassium-channel blockade; prolongs QT/QRS; risk of polymorphic VT, hypotension |
| Lidocaine | Shock-refractory VF/pVT (2nd line after amiodarone) | 1 mg/kg IV/IO bolus; infusion: 20-50 mcg/kg/min | Sodium-channel blockade; alternative to amiodarone |
| Atropine | Bradycardia from vagal tone, cholinergic toxicity, 2nd/3rd degree AV block | 0.02 mg/kg IV/IO/IM (min 0.1 mg, max 0.5 mg); repeat once after 5 min; max total 1 mg (child) | Minimum dose 0.1 mg - paradoxical bradycardia can occur with lower doses |
| Calcium chloride | Hypocalcemia, hyperkalemia, hypermagnesemia, Ca-channel blocker OD | 20 mg/kg IV/IO (max 1 g) over 5 min; use calcium gluconate peripherally | Risk of myocardial necrosis and severe tissue injury with peripheral infiltration |
| Dextrose | Documented hypoglycemia | Newborns: 5-10 mL/kg D10W; Infants/children: 2-4 mL/kg D25W; Adolescents: 1-2 mL/kg D50W (0.5-1 g/kg) | Hyperglycemia associated with poor neurologic outcomes - treat hypoglycemia only |
ET = endotracheal route; IO = intraosseous; IV = intravenous
| Situation | Action |
|---|---|
| Child is coughing or making sounds | Monitor closely; do NOT intervene |
| Choking with ineffective cough | Abdominal thrusts (Heimlich) or back blows until relieved |
| Infant choking | Back blows + chest thrusts (NOT abdominal thrusts) |
| Child becomes unresponsive | Begin CPR with 30 compressions; look in mouth before each breath attempt; remove visible foreign body |
| Foreign body visible above vocal cords on laryngoscopy | Extract with Magill forceps |
| Foreign body below vocal cords | Attempt to push distally to restore airway; use ECMO if needed |
| Parameter | Formula |
|---|---|
| ETT size (cuffed, age 2-10 yr) | (Age / 4) + 3.5 |
| ETT size (uncuffed, age 2-10 yr) | (Age / 4) + 4.0 |
| Depth of ETT insertion (cm) | ETT size × 3 |
| Initial defibrillation | 2 J/kg |
| Second defibrillation | 4 J/kg |
| Maximum defibrillation | 10 J/kg or adult maximum |
| Epinephrine dose | 0.01 mg/kg IV/IO (max 1 mg) |
| Adenosine (first dose) | 0.1 mg/kg IV (max 6 mg) |
| Amiodarone | 5 mg/kg IV/IO (max 300 mg) |
| Atropine | 0.02 mg/kg IV/IO (min 0.1 mg, max 0.5 mg) |
| Compression rate | 100-120/min |
| Compression depth (infants) | ~4 cm (1.5 inches) |
| Compression depth (children) | ~5 cm (2 inches) |
| Ventilation rate (with advanced airway) | 1 breath every 6-8 sec (8-10/min) |
| Ventilation rate (no advanced airway, with CPR) | 30:2 (1 rescuer), 15:2 (2+ rescuers) |
Answer: 15:2
Answer: (6/4) + 3.5 = 5.0 mm; insertion depth = 5.0 × 3 = 15 cm
Answer: 2 J/kg; immediately resume CPR for 2 minutes before checking rhythm.
Answer: Yes. IO and IV routes produce equivalent circulating drug levels.
Answer: Minimum 0.1 mg. Doses below this threshold can cause paradoxical bradycardia.
Answer: Monitor carefully but do not intervene - the child's own cough is the most effective mechanism to clear the obstruction.
Answer: 94-99% (normoxia - avoid both hypoxia and hyperoxia).
Answer: Most pediatric arrests are asphyxial in etiology, so effective airway management and ventilation are more important. Conventional CPR (compressions + rescue breaths) is preferred over compression-only CPR.
Can you convert it to a PDF and some photos with page numbers
ls /home/daytona/workspace/ && mkdir -p /home/daytona/workspace/pls-module && ec…
~/pls-module/generate_pls_pdf.py
#!/usr/bin/env python3
"""
Pediatric Life Support (PLS) Training Module - PDF Generator
Uses ReportLab for rich PDF output with images, tables, and page numbers.
"""
import os
import urllib.request
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak, HRFlowable, KeepTogether, Image
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus.flowables import Flowable
from reportlab.pdfgen import canvas
import io
OUTPUT = "/home/daytona/workspace/pls-module/PediatricLifeSupport_Module.pdf"
IMG_DIR = "/home/daytona/workspace/pls-module/images"
os.makedirs(IMG_DIR, exist_ok=True)
# ── Download images ──────────────────────────────────────────────────────────
IMAGES = {
"bradycardia_algo.png": "https://cdn.orris.care/cdss_images/737fb51cb1e570fc7544f3d8c97df59b607212da7ccc8ac196ff0c0f3ec71bae.png",
"cardiac_arrest_algo.png": "https://cdn.orris.care/cdss_images/34725493279b743d306f091409a2415f3103d051f4944e6138c8b6b01c8f25d5.png",
}
for fname, url in IMAGES.items():
fpath = os.path.join(IMG_DIR, fname)
if not os.path.exists(fpath):
print(f"Downloading {fname}...")
try:
urllib.request.urlretrieve(url, fpath)
print(f" Saved to {fpath}")
except Exception as e:
print(f" FAILED: {e}")
# ── Colour palette ────────────────────────────────────────────────────────────
C_NAVY = colors.HexColor("#1A3A5C")
C_RED = colors.HexColor("#C0392B")
C_TEAL = colors.HexColor("#1A7A7A")
C_GOLD = colors.HexColor("#E67E22")
C_LGRAY = colors.HexColor("#F4F6F8")
C_MGRAY = colors.HexColor("#BDC3C7")
C_WHITE = colors.white
C_BLACK = colors.HexColor("#1C2833")
# ── Styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def make_style(name, parent="Normal", **kwargs):
s = ParagraphStyle(name, parent=styles[parent], **kwargs)
return s
S_COVER_TITLE = make_style("CoverTitle", fontSize=34, textColor=C_WHITE, alignment=TA_CENTER, leading=42, fontName="Helvetica-Bold")
S_COVER_SUB = make_style("CoverSub", fontSize=14, textColor=C_GOLD, alignment=TA_CENTER, leading=20, fontName="Helvetica")
S_COVER_INFO = make_style("CoverInfo", fontSize=11, textColor=C_WHITE, alignment=TA_CENTER, leading=16, fontName="Helvetica")
S_H1 = make_style("H1", fontSize=17, textColor=C_WHITE, fontName="Helvetica-Bold", leading=22, spaceAfter=4, spaceBefore=6)
S_H2 = make_style("H2", fontSize=13, textColor=C_NAVY, fontName="Helvetica-Bold", leading=18, spaceAfter=4, spaceBefore=10)
S_H3 = make_style("H3", fontSize=11, textColor=C_TEAL, fontName="Helvetica-Bold", leading=15, spaceAfter=3, spaceBefore=7)
S_BODY = make_style("Body", fontSize=10, textColor=C_BLACK, leading=15, spaceAfter=5, alignment=TA_JUSTIFY)
S_BULLET= make_style("Bullet", fontSize=10, textColor=C_BLACK, leading=14, spaceAfter=3, leftIndent=14, bulletIndent=4)
S_NOTE = make_style("Note", fontSize=9, textColor=C_TEAL, leading=13, spaceAfter=4, leftIndent=10, fontName="Helvetica-Oblique")
S_WARN = make_style("Warn", fontSize=10, textColor=C_RED, leading=14, spaceAfter=5, fontName="Helvetica-Bold")
S_INDEX = make_style("Index", fontSize=10, textColor=C_NAVY, leading=16, leftIndent=10)
S_INDEX_HEAD = make_style("IndexHead", fontSize=11, textColor=C_NAVY, fontName="Helvetica-Bold", leading=18)
S_CAPTION = make_style("Caption", fontSize=8, textColor=colors.gray, alignment=TA_CENTER, leading=12, spaceAfter=8)
S_FORMULA = make_style("Formula", fontSize=10, textColor=C_NAVY, leading=14, leftIndent=20, fontName="Courier-Bold")
# ── Header banner flowable ────────────────────────────────────────────────────
class SectionBanner(Flowable):
"""Coloured full-width banner used as section headers."""
def __init__(self, text, color=C_NAVY, width=None, height=1.0*cm):
super().__init__()
self.text = text
self.color = color
self._width = width or (A4[0] - 4*cm)
self._height = height
def wrap(self, avail_w, avail_h):
self.width = self._width
self.height = self._height
return self.width, self.height
def draw(self):
c = self.canv
c.setFillColor(self.color)
c.rect(0, 0, self.width, self.height, fill=1, stroke=0)
c.setFillColor(C_WHITE)
c.setFont("Helvetica-Bold", 13)
c.drawString(10, self.height / 2 - 5, self.text)
# ── Page template (header/footer) ─────────────────────────────────────────────
PAGE_W, PAGE_H = A4
def on_page(canvas, doc):
canvas.saveState()
# Footer bar
canvas.setFillColor(C_NAVY)
canvas.rect(0, 0, PAGE_W, 1.2*cm, fill=1, stroke=0)
# Footer text
canvas.setFillColor(C_WHITE)
canvas.setFont("Helvetica", 8)
canvas.drawString(2*cm, 0.45*cm, "Pediatric Life Support (PLS) Training Module")
canvas.drawRightString(PAGE_W - 2*cm, 0.45*cm, f"Page {doc.page}")
# Top rule
canvas.setStrokeColor(C_NAVY)
canvas.setLineWidth(2)
canvas.line(2*cm, PAGE_H - 1.5*cm, PAGE_W - 2*cm, PAGE_H - 1.5*cm)
canvas.setFillColor(C_NAVY)
canvas.setFont("Helvetica-Bold", 9)
canvas.drawString(2*cm, PAGE_H - 1.3*cm, "PLS Module")
canvas.drawRightString(PAGE_W - 2*cm, PAGE_H - 1.3*cm, "Based on AHA 2020 Guidelines")
canvas.restoreState()
def on_first_page(canvas, doc):
# Cover page - no header/footer
pass
# ── Helper builders ───────────────────────────────────────────────────────────
def bullet(text, style=S_BULLET):
return Paragraph(f"• {text}", style)
def body(text, style=S_BODY):
return Paragraph(text, style)
def h2(text):
return SectionBanner(text, C_NAVY)
def h3(text):
return Paragraph(text, S_H3)
def note(text):
return Paragraph(f"ℹ {text}", S_NOTE)
def warning(text):
return Paragraph(f"⚠ {text}", S_WARN)
def spacer(h=0.3):
return Spacer(1, h*cm)
def hr():
return HRFlowable(width="100%", thickness=0.5, color=C_MGRAY, spaceAfter=4)
def formula(text):
return Paragraph(text, S_FORMULA)
def make_table(headers, rows, col_widths=None):
data = [headers] + rows
if col_widths is None:
n = len(headers)
total = PAGE_W - 4*cm
col_widths = [total / n] * n
tbl = Table(data, colWidths=col_widths, repeatRows=1)
style = TableStyle([
# Header
("BACKGROUND", (0, 0), (-1, 0), C_NAVY),
("TEXTCOLOR", (0, 0), (-1, 0), C_WHITE),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 9),
("ALIGN", (0, 0), (-1, 0), "CENTER"),
("BOTTOMPADDING",(0, 0), (-1, 0), 6),
("TOPPADDING", (0, 0), (-1, 0), 6),
# Body rows
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 9),
("ALIGN", (0, 1), (-1, -1), "LEFT"),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("TOPPADDING", (0, 1), (-1, -1), 5),
("BOTTOMPADDING",(0, 1), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("RIGHTPADDING", (0, 0), (-1, -1), 6),
("ROWBACKGROUNDS",(0,1),(-1,-1),[C_LGRAY, C_WHITE]),
# Grid
("GRID", (0, 0), (-1, -1), 0.4, C_MGRAY),
("LINEBELOW", (0, 0), (-1, 0), 1.5, C_TEAL),
])
tbl.setStyle(style)
return tbl
def img_flowable(fname, width_cm=14, caption=""):
fpath = os.path.join(IMG_DIR, fname)
items = []
if os.path.exists(fpath):
try:
im = Image(fpath, width=width_cm*cm, height=None)
im.hAlign = "CENTER"
items.append(im)
if caption:
items.append(Paragraph(caption, S_CAPTION))
except Exception as e:
items.append(body(f"[Image could not be loaded: {e}]"))
else:
items.append(body(f"[Image not available: {fname}]"))
return items
# ── Cover page ────────────────────────────────────────────────────────────────
def cover_page():
elements = []
# Full-page navy background trick: tall coloured table
cover_data = [[""]]
cover_tbl = Table(cover_data, colWidths=[PAGE_W - 4*cm], rowHeights=[3*cm])
cover_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("GRID", (0,0), (-1,-1), 0, C_NAVY),
]))
# Title block
title_block = [
Spacer(1, 4*cm),
Paragraph("PEDIATRIC", S_COVER_TITLE),
Paragraph("LIFE SUPPORT", S_COVER_TITLE),
Spacer(1, 0.5*cm),
Paragraph("Training Module", S_COVER_SUB),
Spacer(1, 1*cm),
HRFlowable(width="60%", thickness=1, color=C_GOLD, hAlign="CENTER", spaceAfter=10),
Spacer(1, 0.5*cm),
Paragraph("Based on AHA 2020 Guidelines for CPR & ECC", S_COVER_INFO),
Spacer(1, 0.3*cm),
Paragraph("For University Students in Health Sciences", S_COVER_INFO),
Spacer(1, 0.3*cm),
Paragraph("Sources: Miller's Anesthesia 10e · Harriet Lane Handbook 23e · Tintinalli's Emergency Medicine", S_COVER_INFO),
Spacer(1, 3*cm),
Paragraph("July 2026", S_COVER_INFO),
]
# Wrap in a dark background table
cover_outer = Table([[item] for item in title_block],
colWidths=[PAGE_W - 4*cm])
cover_outer.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_NAVY),
("LEFTPADDING", (0,0), (-1,-1), 20),
("RIGHTPADDING", (0,0), (-1,-1), 20),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("GRID", (0,0), (-1,-1), 0, C_NAVY),
]))
elements.append(cover_outer)
elements.append(PageBreak())
return elements
# ── INDEX ─────────────────────────────────────────────────────────────────────
def index_section():
elements = []
elements.append(SectionBanner("TABLE OF CONTENTS", C_NAVY, height=1.2*cm))
elements.append(spacer(0.4))
toc = [
("1.", "Introduction & Epidemiology"),
("2.", "Recognition of Pediatric Deterioration"),
("3.", "Pediatric Basic Life Support (PBLS)"),
(" 3.1", "C-A-B Sequence"),
(" 3.2", "High-Quality CPR: 5 Components"),
(" 3.3", "Chest Compression Technique by Age"),
(" 3.4", "Ventilation in Pediatric Arrest"),
(" 3.5", "Defibrillation"),
("4.", "Airway Management"),
(" 4.1", "Airway Assessment"),
(" 4.2", "Airway Adjuncts (OPA / NPA)"),
(" 4.3", "Bag-Valve-Mask Ventilation"),
(" 4.4", "Supraglottic Airways (LMA)"),
(" 4.5", "Endotracheal Intubation"),
("5.", "Pediatric Advanced Life Support (PALS)"),
(" 5.1", "Vascular Access: IV vs. IO"),
(" 5.2", "Shockable Rhythms (VF / pVT)"),
(" 5.3", "Non-Shockable Rhythms (PEA / Asystole)"),
(" 5.4", "Bradycardia Algorithm"),
(" 5.5", "Tachycardia Algorithm"),
("6.", "Resuscitation Medications"),
("7.", "Foreign Body Airway Obstruction (FBAO)"),
("8.", "Special Circumstances"),
(" 8.1", "Drowning"),
(" 8.2", "Septic Shock"),
("9.", "Post-Resuscitation Care"),
("10.", "Termination of Resuscitation"),
("11.", "Key Formulas & Quick Reference"),
("12.", "Self-Assessment Questions"),
]
toc_data = [[Paragraph(num, S_INDEX), Paragraph(title, S_INDEX)] for num, title in toc]
toc_tbl = Table(toc_data, colWidths=[1.5*cm, PAGE_W - 4*cm - 1.5*cm])
toc_tbl.setStyle(TableStyle([
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("ROWBACKGROUNDS", (0,0), (-1,-1), [C_LGRAY, C_WHITE]),
("GRID", (0,0), (-1,-1), 0.3, C_MGRAY),
]))
elements.append(toc_tbl)
elements.append(PageBreak())
return elements
# ── SECTION 1 ─────────────────────────────────────────────────────────────────
def section1():
e = []
e.append(h2("1. INTRODUCTION & EPIDEMIOLOGY"))
e.append(spacer(0.3))
e.append(body("Pediatric cardiac arrest carries a serious but improving prognosis. The leading cause in infants and children is <b>asphyxiation</b> (respiratory failure), NOT primary cardiac events. This has major implications for resuscitation priorities."))
e.append(spacer(0.2))
e.append(h3("Survival Rates (Resuscitation Outcomes Consortium, 2005–2007)"))
tbl = make_table(
["Age Group", "Survival to Hospital Discharge"],
[["Infants (< 1 year)", "3.3%"],
["Children (1–11 years)", "9.1%"],
["Adolescents (12–19 years)", "8.9%"],
["All ages (more recent data)", "8.3%"]],
col_widths=[9*cm, 8*cm]
)
e.append(tbl)
e.append(spacer(0.3))
e.append(body("In-hospital cardiac arrest (IHCA) survival improved from <b>24% to 39%</b> between 2001 and 2009. Prolonged CPR is not always futile: <b>12% of patients receiving CPR for >35 minutes</b> survived to discharge, and <b>60% of those survivors</b> had a favourable neurologic outcome."))
e.append(spacer(0.2))
e.append(note("Source: Miller's Anesthesia, 10e"))
e.append(PageBreak())
return e
# ── SECTION 2 ─────────────────────────────────────────────────────────────────
def section2():
e = []
e.append(h2("2. RECOGNITION OF PEDIATRIC DETERIORATION"))
e.append(spacer(0.3))
e.append(body("Early recognition prevents progression to full cardiac arrest. Use the <b>Pediatric Assessment Triangle (PAT)</b>:"))
e.append(spacer(0.2))
e.append(make_table(
["PAT Component", "What to Assess"],
[["Appearance", "Tone, interaction, consolability, gaze, speech/cry"],
["Work of Breathing", "Abnormal sounds (stridor, wheeze, grunting), retractions, nasal flaring, positioning"],
["Circulation to Skin", "Pallor, mottling, cyanosis"]],
col_widths=[5*cm, 12*cm]
))
e.append(spacer(0.3))
e.append(h3("Warning Signs Requiring Immediate Action"))
for item in [
"Tachypnea, grunting, nasal flaring, intercostal/subcostal retractions",
"Stridor or wheeze",
"Head bobbing in infants",
"Altered level of consciousness",
"Bradycardia — ominous pre-arrest sign in children",
]:
e.append(bullet(item))
e.append(spacer(0.2))
e.append(PageBreak())
return e
# ── SECTION 3 ─────────────────────────────────────────────────────────────────
def section3():
e = []
e.append(h2("3. PEDIATRIC BASIC LIFE SUPPORT (PBLS)"))
e.append(spacer(0.3))
e.append(h3("3.1 C-A-B Sequence"))
e.append(body("The <b>2020 AHA Guidelines</b> maintain the <b>C-A-B (Compressions → Airway → Breathing)</b> sequence for all ages. Although asphyxia dominates pediatric arrests, the universal C-A-B approach was retained to:"))
for t in ["Reduce complexity and improve training retention",
"Decrease time to initiation of chest compressions",
'Reduce "no blood flow" time']: e.append(bullet(t))
e.append(spacer(0.2))
e.append(body("<b>Single rescuer:</b> Begin CPR immediately; use speakerphone to activate emergency services simultaneously."))
e.append(body("<b>Sudden witnessed collapse:</b> Obtain AED quickly before CPR (likely cardiac etiology)."))
e.append(spacer(0.3))
e.append(h3("3.2 High-Quality CPR: 5 Components"))
e.append(make_table(
["#", "Component", "Target"],
[["1", "Compression rate", "100–120 / min"],
["2", "Compression depth", "≥ 1/3 A-P diameter of chest"],
["3", "Chest recoil", "Full recoil between compressions — do not lean"],
["4", "Minimize interruptions", "Pre/post-shock pauses < 10 seconds"],
["5", "Avoid excessive ventilation", "Do not hyperventilate — raises intrathoracic pressure"]],
col_widths=[1*cm, 6*cm, 10*cm]
))
e.append(spacer(0.3))
e.append(h3("3.3 Chest Compression Technique by Age"))
e.append(make_table(
["Age", "Hand Placement", "Depth"],
[["Infants (< 1 yr)", "2 fingers just below inframammary line", "~4 cm (1.5 in)"],
["Children (1 yr–puberty)", "1 or 2 hands, lower half of sternum (avoid xiphoid)", "~5 cm (2 in)"],
["Adolescents (post-puberty)", "Standard adult technique", "5–6 cm"]],
col_widths=[4*cm, 8*cm, 5*cm]
))
e.append(spacer(0.2))
e.append(body("<b>Compression-to-ventilation ratio:</b> 30:2 (1 rescuer) · 15:2 (≥ 2 rescuers) · With advanced airway: continuous compressions, 1 breath every 6–8 sec (8–10/min)"))
e.append(spacer(0.3))
e.append(h3("3.4 Ventilation in Pediatric Arrest"))
e.append(body("Conventional CPR (compressions + rescue breaths) is <b>preferred</b> over compression-only CPR in children. If rescuers are unable/unwilling to give breaths, compression-only CPR is still better than none."))
e.append(spacer(0.3))
e.append(h3("3.5 Defibrillation"))
e.append(make_table(
["Scenario", "Device"],
[["Child < 8 yr, trained provider identifies shockable rhythm", "Manual defibrillator (preferred)"],
["Child < 8 yr, no manual defibrillator", "AED with pediatric attenuator + pediatric pads"],
["No pediatric equipment available", "Adult AED — use without hesitation"]],
col_widths=[10*cm, 7*cm]
))
e.append(spacer(0.2))
e.append(body("<b>Pad placement:</b> Anteroposterior position."))
e.append(body("<b>Energy doses:</b> Initial 2 J/kg → Second 4 J/kg → Subsequent 4 J/kg (max 10 J/kg or adult maximum)."))
e.append(PageBreak())
return e
# ── SECTION 4 ─────────────────────────────────────────────────────────────────
def section4():
e = []
e.append(h2("4. AIRWAY MANAGEMENT"))
e.append(spacer(0.3))
e.append(h3("4.1 Airway Assessment"))
for t in ["Check patency: head tilt-chin lift or jaw thrust. Avoid overextension in infants (neutral/sniffing position).",
"Assess spontaneous respirations and chest rise symmetry.",
"Distress signs: tachypnea, grunting, flaring, retractions, stridor, wheeze, head bobbing."]:
e.append(bullet(t))
e.append(spacer(0.3))
e.append(h3("4.2 Airway Adjuncts"))
e.append(make_table(
["Device", "Patient State", "Sizing"],
[["OPA (Oropharyngeal Airway)", "Unconscious, no gag reflex", "Corner of mouth → mandibular angle"],
["NPA (Nasopharyngeal Airway)", "Conscious or unconscious, gag reflex intact", "Tip of nose → tragus of ear"]],
col_widths=[5*cm, 6*cm, 6*cm]
))
e.append(spacer(0.3))
e.append(h3("4.3 Bag-Valve-Mask (BVM) Ventilation"))
for t in ["May be used indefinitely if ventilating effectively — do not rush to intubate.",
"Bring the face INTO the mask (E-C clamp technique); do not push mask down onto face.",
"Use cricoid pressure (Sellick maneuver) cautiously — avoid excess pressure."]:
e.append(bullet(t))
e.append(spacer(0.3))
e.append(h3("4.4 Laryngeal Mask Airway (LMA)"))
for t in ["Supraglottic; placed blindly. Useful for emergent difficult airway.",
"Size: combined width of patient's index + middle + ring fingers (or weight-based chart).",
"Chest compressions can continue once LMA is placed."]:
e.append(bullet(t))
e.append(spacer(0.3))
e.append(h3("4.5 Endotracheal Intubation"))
e.append(warning("Always have a secondary airway plan before starting intubation."))
e.append(body("<b>Preoxygenation:</b> 100% O₂ via non-rebreather mask for ≥ 3 minutes. Children desaturate rapidly."))
e.append(spacer(0.2))
e.append(body("<b>ETT Size Formulas (age 2–10 years):</b>"))
e.append(formula("Cuffed ETT (mm ID) = (Age / 4) + 3.5"))
e.append(formula("Uncuffed ETT (mm ID) = (Age / 4) + 4.0"))
e.append(formula("Insertion depth (cm) = ETT size × 3"))
e.append(spacer(0.2))
e.append(note("Use a Broselow tape when available for length-based estimation."))
e.append(spacer(0.2))
e.append(make_table(
["Blade", "Type", "Age / Use"],
[["Miller #00–1", "Straight", "Premature – 2 months"],
["Miller #1", "Straight", "3 months – 3 years"],
["Miller #2", "Straight", "> 3 years / difficult airway"],
["Macintosh", "Curved", "Usually more effective > 2 years"]],
col_widths=[4*cm, 3.5*cm, 9.5*cm]
))
e.append(spacer(0.2))
e.append(body("<b>Cuff inflation pressure:</b> Keep < 20–25 cm H₂O."))
e.append(body("<b>Confirmation:</b> Quantitative ETCO₂ (primary), bilateral breath sounds."))
e.append(PageBreak())
return e
# ── SECTION 5 ─────────────────────────────────────────────────────────────────
def section5():
e = []
e.append(h2("5. PEDIATRIC ADVANCED LIFE SUPPORT (PALS)"))
e.append(spacer(0.3))
e.append(h3("5.1 Vascular Access: IV vs. IO"))
e.append(body("IO access is recommended when IV access fails. Circulating drug levels are <b>equivalent</b> between IV and IO routes. <b>All</b> resuscitation medications and blood products can be given IO. Fluids must be pressurised for IO delivery."))
e.append(spacer(0.3))
e.append(h3("5.2 Shockable Rhythms — VF / pVT Algorithm (2020 AHA)"))
e.append(make_table(
["Step", "Action"],
[["1", "Start CPR · Give oxygen · Attach monitor/defibrillator"],
["2", "Confirm shockable rhythm (VF / pVT)"],
["3", "SHOCK 2 J/kg → resume CPR immediately × 2 min"],
["4", "Recheck rhythm → if still shockable: SHOCK 4 J/kg"],
["5", "Resume CPR → Epinephrine 0.01 mg/kg IV/IO every 3–5 min"],
["6", "Recheck rhythm → if still shockable: SHOCK 4 J/kg"],
["7", "Resume CPR → Amiodarone 5 mg/kg IV/IO OR Lidocaine 1 mg/kg IV/IO"],
["8", "Treat reversible causes (H's and T's)"]],
col_widths=[1.5*cm, 15.5*cm]
))
e.append(spacer(0.3))
# Cardiac arrest algorithm image
e.extend(img_flowable("cardiac_arrest_algo.png", width_cm=14,
caption="Fig 1. 2020 AHA Pediatric Cardiac Arrest Algorithm (Miller's Anesthesia, 10e)"))
e.append(spacer(0.3))
e.append(h3("5.3 Non-Shockable Rhythms — PEA / Asystole"))
e.append(make_table(
["Step", "Action"],
[["1", "CPR · Give oxygen · Attach monitor"],
["2", "Confirm non-shockable rhythm (PEA or Asystole)"],
["3", "Resume CPR × 2 min"],
["4", "Epinephrine 0.01 mg/kg IV/IO every 3–5 min"],
["5", "Search for and treat reversible causes (H's and T's)"]],
col_widths=[1.5*cm, 15.5*cm]
))
e.append(spacer(0.3))
e.append(h3("The H's and T's — Reversible Causes"))
e.append(make_table(
["H's", "T's"],
[["Hypovolemia", "Tension pneumothorax"],
["Hypoxia", "Tamponade (cardiac)"],
["Hydrogen ion (acidosis)", "Toxins"],
["Hypo/Hyperkalemia", "Thrombosis (pulmonary)"],
["Hypothermia", "Thrombosis (coronary)"]],
col_widths=[8.5*cm, 8.5*cm]
))
e.append(spacer(0.3))
e.append(h3("5.4 Bradycardia with Pulse Algorithm"))
e.append(body("Bradycardia with poor perfusion is a <b>pre-arrest warning sign</b> requiring immediate action."))
e.append(spacer(0.2))
e.extend(img_flowable("bradycardia_algo.png", width_cm=14,
caption="Fig 2. 2020 AHA Pediatric Bradycardia with a Pulse and Poor Perfusion Algorithm (Miller's Anesthesia, 10e)"))
e.append(spacer(0.2))
for t in ["Identify and support ABC, give oxygen, attach monitor.",
"If HR < 60/min with poor perfusion despite oxygenation → start CPR.",
"Epinephrine 0.01 mg/kg IV/IO every 3–5 min.",
"Atropine 0.02 mg/kg IV/IO for vagal tone or AV block (min 0.1 mg, max 0.5 mg per dose).",
"Consider cardiac pacing if not responding to medications."]:
e.append(bullet(t))
e.append(spacer(0.3))
e.append(h3("5.5 Tachycardia Algorithm"))
e.append(make_table(
["Rhythm", "Hemodynamically Stable", "Hemodynamically Unstable"],
[["Narrow complex (SVT)", "Vagal maneuvers → Adenosine 0.1 mg/kg IV",
"Synchronized cardioversion 0.5–1 J/kg (increase to 2 J/kg if needed)"],
["Wide complex (VT)", "Amiodarone or Lidocaine", "Synchronized cardioversion"]],
col_widths=[4*cm, 7*cm, 6*cm]
))
e.append(PageBreak())
return e
# ── SECTION 6 ─────────────────────────────────────────────────────────────────
def section6():
e = []
e.append(h2("6. RESUSCITATION MEDICATIONS"))
e.append(spacer(0.2))
e.append(note("Source: Harriet Lane Handbook, 23e — The Johns Hopkins Hospital"))
e.append(spacer(0.3))
e.append(make_table(
["Drug", "Indication", "Dose", "Key Notes"],
[
["Epinephrine",
"Asystole, PEA, VF, pVT, bradycardia",
"0.01 mg/kg IV/IO (max 1 mg)\nEvery 3–5 min\nInfusion: 0.05–2 mcg/kg/min",
"α-agonism raises aortic diastolic pressure; ET dose: 0.1 mg/kg"],
["Adenosine",
"SVT (AV node reentry or accessory pathway)",
"1st: 0.1 mg/kg (max 6 mg)\n2nd: 0.2 mg/kg (max 12 mg)\n3rd: 0.3 mg/kg (max 12 mg)",
"Rapid push via 3-way stopcock; brief asystole 10–15 sec expected"],
["Amiodarone",
"Shock-refractory VF/pVT; refractory SVT",
"5 mg/kg IV/IO (max 300 mg)\nRepeat up to 15 mg/kg total",
"No pulse: push undiluted\nWith pulse: dilute, run over 20–60 min\nMonitor for polymorphic VT"],
["Lidocaine",
"Shock-refractory VF/pVT (2nd line)",
"1 mg/kg IV/IO bolus\nInfusion: 20–50 mcg/kg/min",
"Sodium-channel blockade; alternative to amiodarone"],
["Atropine",
"Bradycardia (vagal/cholinergic); 2nd/3rd degree AV block",
"0.02 mg/kg IV/IO\n(min 0.1 mg; max 0.5 mg)\nMax total: 1 mg (child)",
"Min dose 0.1 mg — lower doses cause paradoxical bradycardia"],
["Calcium Chloride",
"Hypocalcemia, hyperkalemia, Ca-channel blocker OD",
"20 mg/kg IV/IO (max 1 g) over 5 min",
"Use calcium gluconate peripherally\nRisk of tissue necrosis with extravasation"],
["Dextrose",
"Documented hypoglycemia only",
"0.5–1 g/kg IV/IO\nNewborns: 5–10 mL/kg D10W\nInfants/children: 2–4 mL/kg D25W\nAdolescents: 1–2 mL/kg D50W",
"Hyperglycemia is associated with poor neurologic outcomes"],
],
col_widths=[3.2*cm, 4*cm, 5.3*cm, 4.5*cm]
))
e.append(PageBreak())
return e
# ── SECTION 7 ─────────────────────────────────────────────────────────────────
def section7():
e = []
e.append(h2("7. FOREIGN BODY AIRWAY OBSTRUCTION (FBAO)"))
e.append(spacer(0.3))
e.append(body("Most common in children < 4 years (peak incidence 1–2 years). Liquids are the most common cause in infants; small objects (balloons, food) in older children."))
e.append(spacer(0.2))
e.append(make_table(
["Situation", "Action"],
[["Child coughing or making sounds", "Monitor closely — do NOT intervene"],
["Choking with ineffective cough (child)", "Abdominal thrusts (Heimlich) or back blows until relieved"],
["Choking infant", "5 back blows + 5 chest thrusts (NOT abdominal thrusts)"],
["Child becomes unresponsive", "Begin CPR with 30 compressions; look in mouth before each breath attempt; remove visible foreign body"],
["FB visible above vocal cords on laryngoscopy", "Extract with Magill forceps"],
["FB below vocal cords", "Push distally to restore airway; consider ECMO if refractory"]],
col_widths=[7*cm, 10*cm]
))
e.append(spacer(0.2))
e.append(warning("Do NOT perform Heimlich in drowned patients — delays intubation and increases aspiration risk."))
e.append(PageBreak())
return e
# ── SECTION 8 ─────────────────────────────────────────────────────────────────
def section8():
e = []
e.append(h2("8. SPECIAL CIRCUMSTANCES"))
e.append(spacer(0.3))
e.append(h3("8.1 Drowning"))
for t in ["Respiratory arrest is the primary etiology — provide oxygen and ventilation earliest.",
"Single rescuer: 2 minutes of 30:2 CPR before calling for help.",
"Two or more rescuers: summon help immediately.",
"Prefer cuffed ETTs (submersion-induced lung injury reduces compliance).",
"Aggressive resuscitation until ROSC or arrival at ED; consider ECMO.",
"Outcomes often better than other respiratory arrest etiologies — do not give up early."]:
e.append(bullet(t))
e.append(spacer(0.3))
e.append(h3("8.2 Septic Shock"))
e.append(body("<b>Recognition:</b> Fever or hypothermia, tachycardia, altered mental status, poor perfusion (prolonged capillary refill, decreased urine output, cool extremities)."))
e.append(spacer(0.2))
e.append(make_table(
["Management Step", "Detail"],
[["1. Fluid resuscitation", "20 mL/kg isotonic fluid boluses; reassess after each bolus"],
["2. Vasopressors", "Norepinephrine or epinephrine for refractory shock"],
["3. Antibiotics", "Blood cultures first; antibiotics within 1 hour of recognition"],
["4. Corticosteroids", "For catecholamine-refractory septic shock"],
["5. Monitor", "Urine output, lactate, mental status, capillary refill"]],
col_widths=[6*cm, 11*cm]
))
e.append(PageBreak())
return e
# ── SECTION 9 ─────────────────────────────────────────────────────────────────
def section9():
e = []
e.append(h2("9. POST-RESUSCITATION CARE"))
e.append(spacer(0.3))
e.append(body("Following Return of Spontaneous Circulation (ROSC), optimise perfusion and prevent secondary injury."))
e.append(spacer(0.2))
e.append(make_table(
["Priority", "Action / Target"],
[["Chest X-ray", "Verify ETT placement"],
["Arterial blood gas", "Correct acid-base disturbances"],
["Hb / Hct", "Assess transfusion need"],
["SpO₂ target", "94–99% (avoid both hypoxia and hyperoxia)"],
["PaCO₂ target", "35–45 mmHg (normocapnia) or per underlying condition"],
["Blood pressure", "Systolic > 5th percentile for age; IV fluids + inotropes/vasoactive agents; continuous arterial BP monitoring recommended"],
["Temperature", "Continuously monitor; prevent/treat fever\nFor comatose post-OHCA: normothermia OR therapeutic hypothermia — both reasonable"],
["EEG monitoring", "Recommended for patients with persistent encephalopathy to detect seizures"],
["Rehabilitation", "All survivors should be evaluated for rehabilitation services"]],
col_widths=[5*cm, 12*cm]
))
e.append(PageBreak())
return e
# ── SECTION 10 ────────────────────────────────────────────────────────────────
def section10():
e = []
e.append(h2("10. TERMINATION OF RESUSCITATION"))
e.append(spacer(0.3))
e.append(body("No single factor reliably predicts outcome with sufficient accuracy to recommend termination of CPR. Factors studied as post-ROSC predictors include:"))
for t in ["Presence of hypotension",
"Serum neurologic biomarkers",
"Serum lactate levels"]:
e.append(bullet(t))
e.append(spacer(0.2))
e.append(warning("AHA 2020: Cessation of resuscitation should NOT be offered if there is potential for good recovery. Consider clinical context holistically."))
e.append(PageBreak())
return e
# ── SECTION 11 ────────────────────────────────────────────────────────────────
def section11():
e = []
e.append(h2("11. KEY FORMULAS & QUICK REFERENCE"))
e.append(spacer(0.3))
e.append(make_table(
["Parameter", "Formula / Value"],
[["ETT size — cuffed (age 2–10 yr)", "(Age / 4) + 3.5 mm ID"],
["ETT size — uncuffed (age 2–10 yr)", "(Age / 4) + 4.0 mm ID"],
["ETT insertion depth (cm)", "ETT size × 3"],
["Compression rate", "100–120 / min"],
["Compression depth — infants", "~4 cm (1.5 in)"],
["Compression depth — children", "~5 cm (2 in)"],
["Compression depth — adolescents", "5–6 cm"],
["Initial defibrillation", "2 J/kg"],
["Second defibrillation", "4 J/kg"],
["Maximum defibrillation", "10 J/kg (or adult maximum)"],
["Epinephrine dose", "0.01 mg/kg IV/IO (max 1 mg)"],
["Adenosine — first dose", "0.1 mg/kg IV (max 6 mg)"],
["Amiodarone", "5 mg/kg IV/IO (max 300 mg)"],
["Atropine", "0.02 mg/kg IV/IO (min 0.1 mg, max 0.5 mg)"],
["Ventilation rate — with advanced airway", "1 breath every 6–8 sec (8–10/min)"],
["Ventilation rate — CPR without advanced airway", "30:2 (1 rescuer) · 15:2 (≥2 rescuers)"]],
col_widths=[9*cm, 8*cm]
))
e.append(PageBreak())
return e
# ── SECTION 12 ────────────────────────────────────────────────────────────────
def section12():
e = []
e.append(h2("12. SELF-ASSESSMENT QUESTIONS"))
e.append(spacer(0.3))
qa = [
("Q1", "A 3-year-old is found unresponsive and not breathing. There are two rescuers. What compression-to-ventilation ratio should be used before an advanced airway is placed?",
"15:2"),
("Q2", "What is the correct ETT size for a 6-year-old using a cuffed tube? What is the insertion depth?",
"Size: (6/4) + 3.5 = 5.0 mm ID. Depth: 5.0 × 3 = 15 cm."),
("Q3", "An infant in VF receives a first shock. What energy dose is used, and what do you do immediately after?",
"2 J/kg. Immediately resume CPR for 2 minutes — do not check rhythm first."),
("Q4", "A 5-year-old in cardiac arrest has IO access. Is it appropriate to give epinephrine via this route?",
"Yes. IO and IV routes produce equivalent circulating drug levels."),
("Q5", "What is the minimum dose of atropine in a child, and why does this minimum exist?",
"Minimum 0.1 mg. Doses below this threshold can cause paradoxical bradycardia."),
("Q6", "A child is choking but coughing vigorously and able to make sounds. What should you do?",
"Monitor carefully — do NOT intervene. The child's own cough is the most effective clearance mechanism."),
("Q7", "Following ROSC in an 8-year-old, what SpO₂ target should be maintained?",
"94–99% (normoxia — avoid both hypoxia and hyperoxia)."),
("Q8", "What is the key difference in CPR approach between pediatric and adult cardiac arrest?",
"Most pediatric arrests are asphyxial in etiology, so effective airway management and ventilation are more important. Conventional CPR (compressions + rescue breaths) is preferred over compression-only CPR."),
]
for num, question, answer in qa:
e.append(KeepTogether([
Paragraph(f"<b>{num}.</b> {question}", S_BODY),
spacer(0.1),
Paragraph(f"<font color='#1A7A7A'><b>Answer:</b></font> {answer}", S_BODY),
hr(),
spacer(0.1),
]))
e.append(spacer(0.3))
e.append(note("Sources: Miller's Anesthesia 10e | Harriet Lane Handbook 23e | Tintinalli's Emergency Medicine | AHA 2020 Guidelines for CPR & ECC"))
return e
# ── Assemble & build ──────────────────────────────────────────────────────────
def build_pdf():
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=2*cm,
rightMargin=2*cm,
topMargin=2.5*cm,
bottomMargin=2*cm,
title="Pediatric Life Support Training Module",
author="Orris AI",
subject="PLS - AHA 2020 Guidelines",
)
story = []
story += cover_page()
story += index_section()
story += section1()
story += section2()
story += section3()
story += section4()
story += section5()
story += section6()
story += section7()
story += section8()
story += section9()
story += section10()
story += section11()
story += section12()
doc.build(story, onFirstPage=on_first_page, onLaterPages=on_page)
print(f"PDF saved to: {OUTPUT}")
if __name__ == "__main__":
build_pdf()
cd /home/daytona/workspace/pls-module && python3 generate_pls_pdf.py 2>&1
python3 -c " from PIL import Image import os for f in ['bradycardia_algo.png','c…
~/pls-module/generate_pls_pdf.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/pls-module/generate_pls_pdf.py'
cd /home/daytona/workspace/pls-module && python3 generate_pls_pdf.py 2>&1
ls -lh /home/daytona/workspace/pls-module/PediatricLifeSupport_Module.pdf
PediatricLifeSupport Module
PDF Document · PDF
Thank you 🙏