anaesthesia machine checklist protocol
Please make one with flowcharts
mkdir -p /tmp/workspace/anaesthesia-checklist && ls /tmp/workspace/
~/anaesthesia-checklist/build_pptx.py
"""
Anaesthesia Machine Checklist Protocol - PowerPoint with Flowcharts
Based on ASA Recommendations for Pre-Anesthesia Checkout Procedures (2008)
Source: Miller's Anesthesia 10e & Barash Clinical Anesthesia 9e
"""
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.enum.shapes import MSO_SHAPE_TYPE
from pptx.oxml.ns import qn
from pptx.oxml import parse_xml
import lxml.etree as etree
from io import BytesIO
# ── Colour Palette ──────────────────────────────────────────────────────────
DARK_BLUE = RGBColor(0x1A, 0x3A, 0x5C) # headings / title bg
MED_BLUE = RGBColor(0x2E, 0x6D, 0xA8) # section headers
LIGHT_BLUE = RGBColor(0xD6, 0xE8, 0xF7) # step boxes bg
TEAL = RGBColor(0x00, 0x87, 0x8A) # daily items
GREEN = RGBColor(0x2A, 0x7A, 0x4B) # per-case items
AMBER = RGBColor(0xE8, 0x8B, 0x00) # warnings / critical
RED = RGBColor(0xC0, 0x39, 0x2B) # FAIL / stop
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
LIGHT_GRAY = RGBColor(0xF4, 0xF6, 0xF9)
MID_GRAY = RGBColor(0x88, 0x96, 0xA7)
DARK_GRAY = RGBColor(0x33, 0x33, 0x33)
YELLOW_BG = RGBColor(0xFF, 0xF3, 0xCD)
GREEN_BG = RGBColor(0xD4, 0xED, 0xDA)
RED_BG = RGBColor(0xF8, 0xD7, 0xDA)
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
blank = prs.slide_layouts[6]
# ─────────────────────────────────────────────────────────────────────────────
# Helper utilities
# ─────────────────────────────────────────────────────────────────────────────
def add_rect(slide, x, y, w, h, fill_rgb, border_rgb=None, border_pt=1.5, radius=None):
"""Add a filled rectangle (optionally with rounded corners)."""
shape = slide.shapes.add_shape(
1, # MSO_SHAPE_TYPE.RECTANGLE
Inches(x), Inches(y), Inches(w), Inches(h)
)
shape.fill.solid()
shape.fill.fore_color.rgb = fill_rgb
if border_rgb:
shape.line.color.rgb = border_rgb
shape.line.width = Pt(border_pt)
else:
shape.line.fill.background()
return shape
def add_text_box(slide, text, x, y, w, h, font_size=12, bold=False,
color=DARK_GRAY, align=PP_ALIGN.LEFT, wrap=True,
v_anchor=MSO_ANCHOR.MIDDLE, italic=False):
tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
tf = tb.text_frame
tf.word_wrap = wrap
tf.vertical_anchor = v_anchor
tf.margin_left = Pt(4)
tf.margin_right = Pt(4)
tf.margin_top = Pt(2)
tf.margin_bottom= Pt(2)
p = tf.paragraphs[0]
p.alignment = align
r = p.add_run()
r.text = text
r.font.size = Pt(font_size)
r.font.bold = bold
r.font.italic = italic
r.font.color.rgb = color
r.font.name = "Calibri"
return tb
def add_label_box(slide, text, x, y, w, h, fill, border=None,
font_size=11, bold=False, color=DARK_GRAY,
align=PP_ALIGN.CENTER, wrap=True):
"""A combined filled rect + centred text."""
add_rect(slide, x, y, w, h, fill, border_rgb=border)
add_text_box(slide, text, x, y, w, h,
font_size=font_size, bold=bold, color=color,
align=align, wrap=wrap)
def add_arrow_v(slide, x, y_top, length, color=MID_GRAY):
"""Vertical downward arrow."""
cx = Inches(x)
cy = Inches(y_top)
cw = Inches(0.02)
ch = Inches(length)
conn = slide.shapes.add_connector(1, cx, cy, cx, cy + ch) # straight
conn.line.color.rgb = color
conn.line.width = Pt(1.5)
def add_slide_header(slide, title, subtitle=None):
"""Standard slide header bar."""
add_rect(slide, 0, 0, 13.333, 0.9, DARK_BLUE)
add_text_box(slide, title, 0.3, 0.05, 10, 0.8,
font_size=22, bold=True, color=WHITE, align=PP_ALIGN.LEFT)
if subtitle:
add_text_box(slide, subtitle, 0.3, 0.6, 10, 0.35,
font_size=11, color=RGBColor(0xAA, 0xCC, 0xEE),
align=PP_ALIGN.LEFT)
def add_footer(slide, text="Source: Miller's Anesthesia 10e | ASA Pre-Anesthesia Checkout Recommendations 2008"):
add_rect(slide, 0, 7.2, 13.333, 0.3, RGBColor(0xEC, 0xF0, 0xF5))
add_text_box(slide, text, 0.3, 7.2, 12.7, 0.3,
font_size=8, color=MID_GRAY, align=PP_ALIGN.LEFT,
v_anchor=MSO_ANCHOR.MIDDLE)
# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 1 – Title Slide
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
# Dark gradient background
add_rect(slide, 0, 0, 13.333, 7.5, DARK_BLUE)
add_rect(slide, 0, 4.5, 13.333, 3.0, MED_BLUE)
# Accent bar
add_rect(slide, 0.4, 1.6, 0.08, 3.5, AMBER)
add_text_box(slide, "ANAESTHESIA MACHINE", 0.7, 1.3, 11, 1.0,
font_size=36, bold=True, color=WHITE, align=PP_ALIGN.LEFT)
add_text_box(slide, "CHECKLIST PROTOCOL", 0.7, 2.15, 11, 1.0,
font_size=36, bold=True, color=RGBColor(0xAA, 0xD4, 0xF5),
align=PP_ALIGN.LEFT)
add_text_box(slide, "Pre-Anaesthesia Checkout (PAC) Flowchart Guide", 0.7, 3.2, 11, 0.5,
font_size=16, color=RGBColor(0xCC, 0xE0, 0xF0), align=PP_ALIGN.LEFT)
add_text_box(slide,
"Based on: ASA Recommendations for Pre-Anesthesia Checkout Procedures (2008)\n"
"Miller's Anesthesia 10e | Barash Clinical Anesthesia 9e | FDA Checkout Guidelines",
0.7, 3.8, 11, 0.7, font_size=11, color=RGBColor(0x88, 0xAA, 0xCC),
align=PP_ALIGN.LEFT)
# Bottom legend
boxes = [
("DAILY", TEAL, "Perform once\nbefore first case"),
("EACH CASE", GREEN, "Repeat before\nevery case"),
("CRITICAL", AMBER, "Most safety-\ncritical items"),
]
for i, (lbl, col, desc) in enumerate(boxes):
bx = 1.0 + i * 3.8
add_rect(slide, bx, 5.1, 1.4, 0.5, col)
add_text_box(slide, lbl, bx, 5.1, 1.4, 0.5,
font_size=13, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_text_box(slide, desc, bx + 1.5, 5.1, 2.1, 0.5,
font_size=10, color=WHITE, align=PP_ALIGN.LEFT)
# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 2 – Overview Flowchart (Pre-Anaesthesia Checkout Overview)
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "Pre-Anaesthesia Checkout (PAC) – Overview Flowchart",
"Complete workflow from start of day to anaesthesia induction")
add_footer(slide)
# Central flowchart column (left half)
fc_items = [
("START OF DAY", DARK_BLUE, WHITE, True),
("Daily Checks\n(Items 1, 3, 5–10)", TEAL, WHITE, True),
("Per-Case Checks\n(Items 2, 4, 11–15)", GREEN, WHITE, True),
("ANESTHESIA TIME-OUT\n(Item 15)", AMBER, WHITE, True),
("COMMENCE ANESTHESIA", MED_BLUE, WHITE, True),
]
col_x = 1.0
box_w = 3.6
box_h = 0.65
gap = 0.22
start_y = 1.1
for i, (lbl, fill, tcol, bold) in enumerate(fc_items):
y = start_y + i * (box_h + gap)
add_rect(slide, col_x, y, box_w, box_h, fill, border_rgb=MED_BLUE, border_pt=1)
add_text_box(slide, lbl, col_x, y, box_w, box_h,
font_size=11, bold=bold, color=tcol, align=PP_ALIGN.CENTER)
if i < len(fc_items) - 1:
arr_y = y + box_h
add_rect(slide, col_x + box_w/2 - 0.01, arr_y, 0.02, gap * 0.8, MID_GRAY)
# Decision diamond after each group
def draw_diamond(slide, cx, cy, w, h, fill, border, text, font_size=9, text_color=WHITE):
"""Approximate diamond with a rotated rectangle textbox + border shape."""
shape = slide.shapes.add_shape(
4, # DIAMOND
Inches(cx - w/2), Inches(cy - h/2), Inches(w), Inches(h)
)
shape.fill.solid()
shape.fill.fore_color.rgb = fill
shape.line.color.rgb = border
shape.line.width = Pt(1.5)
add_text_box(slide, text, cx - w/2, cy - h/2, w, h,
font_size=font_size, bold=True, color=text_color,
align=PP_ALIGN.CENTER)
# Decision diamond 1: after daily checks
d1_y = start_y + 1*(box_h + gap) + box_h + 0.05
draw_diamond(slide, col_x + box_w/2, d1_y + 0.3, 2.0, 0.55,
AMBER, DARK_BLUE, "All daily checks\npassed?")
add_text_box(slide, "NO → Investigate & fix\nbefore proceeding",
col_x + box_w/2 + 1.1, d1_y + 0.1, 3.0, 0.6,
font_size=9, color=RED, bold=False)
# Decision diamond 2: after per-case checks
d2_y = start_y + 2*(box_h + gap) + box_h + 0.05
draw_diamond(slide, col_x + box_w/2, d2_y + 0.3, 2.0, 0.55,
AMBER, DARK_BLUE, "Per-case checks\npassed?")
add_text_box(slide, "NO → Do not proceed\nAlert attending",
col_x + box_w/2 + 1.1, d2_y + 0.1, 3.0, 0.6,
font_size=9, color=RED, bold=False)
# Right panel – key requirements
add_rect(slide, 5.2, 1.0, 7.7, 5.9, WHITE, border_rgb=MED_BLUE, border_pt=1)
add_rect(slide, 5.2, 1.0, 7.7, 0.45, MED_BLUE)
add_text_box(slide, "7 BASIC REQUIREMENTS FOR SAFE ANESTHESIA DELIVERY (ASA 2008)",
5.3, 1.0, 7.5, 0.45, font_size=10, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
reqs = [
("1", "Reliable delivery of O₂ at any appropriate concentration up to 100%"),
("2", "Reliable means of positive-pressure ventilation"),
("3", "Backup ventilation equipment available and functioning"),
("4", "Controlled release of positive pressure from breathing circuit"),
("5", "Anaesthetic vapour delivery (if part of the plan)"),
("6", "Adequate suction"),
("7", "Means to conform to standards for patient monitoring"),
]
for i, (num, txt) in enumerate(reqs):
ry = 1.55 + i * 0.7
add_rect(slide, 5.4, ry, 0.38, 0.5, MED_BLUE)
add_text_box(slide, num, 5.4, ry, 0.38, 0.5,
font_size=14, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_rect(slide, 5.82, ry, 6.85, 0.5, LIGHT_BLUE if i%2==0 else WHITE)
add_text_box(slide, txt, 5.85, ry, 6.8, 0.5,
font_size=10, color=DARK_GRAY, align=PP_ALIGN.LEFT)
# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 3 – Daily Checks Flowchart (Items 1, 3, 5–10)
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "DAILY CHECKS – Flowchart (Perform Once Before First Case)",
"Items 1, 3, 5, 6, 7, 8, 9, 10 of ASA PAC Recommendations")
add_footer(slide)
daily_steps = [
("ITEM 1", "Verify Auxiliary O₂\nCylinder & Self-Inflating\nResuscitation Bag (SIRB)",
"• Full O₂ cylinder with flowmeter & wrench\n• SIRB present & functional\n• Close valve after check",
TEAL),
("ITEM 3", "Turn on Workstation;\nConfirm AC Power",
"• Power on all machine modules\n• Verify green AC power indicator\n• Check battery backup status",
TEAL),
("ITEM 5", "Spare O₂ Cylinder\nPressure Check",
"• Cylinder gauge: FULL (≥ 2000 psi)\n• Open briefly to verify flow\n• Close valve after check",
TEAL),
("ITEM 6", "Verify Pipeline\nGas Pressure",
"• O₂: 50–55 psi\n• N₂O: 50–55 psi\n• Air: 50–55 psi\n• All connections secure (DISS fittings)",
TEAL),
("ITEM 7", "Vaporiser(s)\nCheck",
"• Vaporisers properly seated / interlocked\n• Adequate agent level in each\n• Check for leaks around filler cap",
TEAL),
("ITEM 8", "Verify CO₂\nAbsorbent",
"• Check colour – not exhausted\n• Replace if >50% colour-changed\n• Ensure no bypassing of absorber",
TEAL),
("ITEM 9", "Breathing System /\nCircle System Check",
"• Inspect hoses for damage\n• Verify unidirectional valves move freely\n• Perform low-pressure leak test (LPLT)",
TEAL),
("ITEM 10", "Oxygen Analyser\nCalibration",
"• Expose sensor to room air → calibrate to 21%\n• Reinstall; observe FiO₂ ≥ 90% on O₂ flush\n• Set low-O₂ alarm (≥ 18%)",
TEAL),
]
# Two-column layout
for i, (num, title, detail, fill) in enumerate(daily_steps):
col = i % 2
row = i // 2
bx = 0.35 + col * 6.5
by = 1.05 + row * 1.5
# Main box
add_rect(slide, bx, by, 6.15, 1.35, WHITE, border_rgb=TEAL, border_pt=1.5)
# Number tag
add_rect(slide, bx, by, 1.0, 1.35, fill)
add_text_box(slide, num, bx, by, 1.0, 1.35,
font_size=10, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
# Title
add_text_box(slide, title, bx + 1.05, by + 0.02, 2.1, 1.3,
font_size=10, bold=True, color=DARK_BLUE, align=PP_ALIGN.LEFT)
# Detail
add_rect(slide, bx + 3.2, by, 2.9, 1.35, LIGHT_BLUE)
add_text_box(slide, detail, bx + 3.25, by + 0.04, 2.8, 1.27,
font_size=8.5, color=DARK_GRAY, align=PP_ALIGN.LEFT)
# Arrow connectors between rows
for row in range(3):
for col in range(2):
ax = 0.35 + col * 6.5 + 3.075
ay = 1.05 + row * 1.5 + 1.35
add_rect(slide, ax, ay, 0.02, 0.15, MID_GRAY)
# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 4 – Per-Case Checks Flowchart (Items 2, 4, 11–15)
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "PER-CASE CHECKS – Flowchart (Repeat Before EVERY Case)",
"Items 2, 4, 11, 12, 13, 14, 15 of ASA PAC Recommendations")
add_footer(slide)
per_case_steps = [
("ITEM 2", "Verify Patient Suction\nIs Adequate",
"• Suction functional & at bedside\n• Yankauer tip connected\n• Tubing of appropriate length",
GREEN),
("ITEM 4", "Verify Monitors &\nCheck Alarms",
"• Pulse oximeter probe functional\n• Capnography waveform present\n• Alarms set to defaults",
GREEN),
("ITEM 11", "Verify Breathing System\nPatency & Function",
"• Visual inspection of circuit integrity\n• Confirm fresh gas flow connection\n• APL valve functional",
GREEN),
("ITEM 12", "Check Scavenging\nSystem",
"• Scavenging connected to breathing system\n• Positive/negative pressure relief open\n• Adequate scavenging flow",
GREEN),
("ITEM 13", "Check Airway\nEquipment",
"• Correct ETT sizes available\n• Laryngoscope blades light-tested\n• LMA / video laryngoscope ready",
GREEN),
("ITEM 14", "Check & Confirm\nDrugs / IV Access",
"• Emergency drugs drawn / labelled\n• IV access patent and secured\n• Syringe labels double-checked",
GREEN),
("ITEM 15", "ANESTHESIA TIME-OUT\n(Final Pre-Induction Check)",
"• Monitors functional?\n• Capnogram present?\n• SpO₂ measured?\n• Flowmeter/vent settings correct?\n• Manual/vent switch → MANUAL?\n• Vaporiser adequately filled?",
AMBER),
]
# Vertical flowchart left column (items 2,4,11,12,13,14) + right box for item 15
small_h = 0.78
gap2 = 0.12
for i, (num, title, detail, fill) in enumerate(per_case_steps[:6]):
by = 1.05 + i * (small_h + gap2)
bx = 0.35
add_rect(slide, bx, by, 0.9, small_h, fill)
add_text_box(slide, num, bx, by, 0.9, small_h,
font_size=9, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_rect(slide, bx + 0.95, by, 2.2, small_h, WHITE, border_rgb=GREEN, border_pt=1)
add_text_box(slide, title, bx + 1.0, by, 2.1, small_h,
font_size=9.5, bold=True, color=DARK_BLUE, align=PP_ALIGN.LEFT)
add_rect(slide, bx + 3.2, by, 3.1, small_h, LIGHT_BLUE)
add_text_box(slide, detail, bx + 3.25, by, 3.0, small_h,
font_size=8, color=DARK_GRAY, align=PP_ALIGN.LEFT)
# Arrow
if i < 5:
add_rect(slide, bx + 0.44, by + small_h, 0.02, gap2, MID_GRAY)
# Item 15 – big highlight box on right
add_rect(slide, 6.8, 1.0, 6.15, 5.8, WHITE, border_rgb=AMBER, border_pt=2.5)
add_rect(slide, 6.8, 1.0, 6.15, 0.5, AMBER)
add_text_box(slide, "⚑ ITEM 15 | ANAESTHESIA TIME-OUT", 6.9, 1.0, 6.0, 0.5,
font_size=13, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_text_box(slide, "Final Check Immediately Before Induction (Provider ONLY)",
6.9, 1.55, 6.0, 0.4,
font_size=10.5, bold=False, italic=True, color=DARK_BLUE, align=PP_ALIGN.CENTER)
timeout_items = [
("✔", "Monitors functional?", WHITE),
("✔", "Capnogram present?", WHITE),
("✔", "SpO₂ (pulse oximetry) measured?", WHITE),
("✔", "Flowmeter & ventilator settings correct?", WHITE),
("✔", "Manual/ventilator switch set to MANUAL?", WHITE),
("✔", "Vaporiser(s) adequately filled?", WHITE),
]
for j, (sym, text, bg) in enumerate(timeout_items):
ty = 2.05 + j * 0.65
add_rect(slide, 6.9, ty, 0.55, 0.55, AMBER if j == 0 else GREEN)
add_text_box(slide, sym, 6.9, ty, 0.55, 0.55,
font_size=14, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
bg_col = LIGHT_BLUE if j % 2 == 0 else WHITE
add_rect(slide, 7.5, ty, 5.2, 0.55, bg_col, border_rgb=MID_GRAY, border_pt=0.5)
add_text_box(slide, text, 7.55, ty, 5.1, 0.55,
font_size=11, color=DARK_GRAY, align=PP_ALIGN.LEFT)
add_rect(slide, 6.9, 6.0, 5.85, 0.65, GREEN_BG, border_rgb=GREEN, border_pt=1.5)
add_text_box(slide, "✅ ALL SIX ITEMS CONFIRMED → PROCEED TO INDUCTION",
6.95, 6.0, 5.75, 0.65,
font_size=11, bold=True, color=GREEN, align=PP_ALIGN.CENTER)
# MS MAIDS mnemonic
add_rect(slide, 6.8, 6.75, 6.15, 0.4, RGBColor(0xE8, 0xF4, 0xFD))
add_text_box(slide, "Mnemonic: MS MAIDS (Machine · Suction · Monitors · Airway · IV · Drugs · Special equipment)",
6.85, 6.75, 6.1, 0.4,
font_size=8.5, color=MED_BLUE, align=PP_ALIGN.CENTER, italic=True)
# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 5 – Low-Pressure Leak Test (LPLT) Flowchart
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "Low-Pressure Circuit Leak Test (LPLT) – Step-by-Step Flowchart",
"One of the 3 most critical daily checkout steps | Barash Clinical Anesthesia 9e, p.2050")
add_footer(slide)
lplt_steps = [
(TEAL, "STEP 1", "Turn off all vaporisers\nand flowmeters"),
(TEAL, "STEP 2", "Turn oxygen flowmeter on\nto minimum flow (100–250 mL/min)"),
(TEAL, "STEP 3", "Occlude the common\ngas outlet (Y-piece or patient port)"),
(TEAL, "STEP 4", "Observe flowmeter bobbin:\nshould fall to zero (or near zero)"),
(MED_BLUE,"STEP 5", "Turn each vaporiser on\none at a time; repeat occlude test"),
(MED_BLUE,"STEP 6", "Assess results"),
]
box_w2 = 3.0
box_h2 = 0.7
fc_x = 0.5
for i, (fill, num, text) in enumerate(lplt_steps):
fx = fc_x
fy = 1.1 + i * (box_h2 + 0.25)
if i == 5: # decision
draw_diamond(slide, fx + box_w2/2, fy + box_h2/2, box_w2, box_h2 + 0.1,
AMBER, DARK_BLUE, text, font_size=10)
else:
add_rect(slide, fx, fy, box_w2, box_h2, fill, border_rgb=DARK_BLUE, border_pt=1)
add_text_box(slide, num, fx, fy, 0.8, box_h2,
font_size=9, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_text_box(slide, text, fx + 0.85, fy, box_w2 - 0.9, box_h2,
font_size=9.5, color=WHITE, align=PP_ALIGN.LEFT)
# arrow
if i < 5:
add_rect(slide, fx + box_w2/2, fy + box_h2, 0.02, 0.25, MID_GRAY)
# PASS / FAIL branches
pass_x = 3.8
fail_x = 0.5
branch_y = 1.1 + 5 * (box_h2 + 0.25) + box_h2 + 0.15
add_rect(slide, pass_x, branch_y, 2.8, 0.7, GREEN_BG, border_rgb=GREEN, border_pt=1.5)
add_text_box(slide, "✅ PASS\nFlow falls to ≤ 50 mL/min\n→ Proceed with checkout",
pass_x + 0.05, branch_y, 2.7, 0.7,
font_size=9, color=GREEN, align=PP_ALIGN.CENTER, bold=True)
add_rect(slide, fail_x - 0.0, branch_y, 2.8, 0.7, RED_BG, border_rgb=RED, border_pt=1.5)
add_text_box(slide, "❌ FAIL\nFlow does not fall → LEAK\n→ Do NOT use machine; notify",
fail_x, branch_y, 2.75, 0.7,
font_size=9, color=RED, align=PP_ALIGN.CENTER, bold=True)
# Right side: circle system explanation
add_rect(slide, 7.2, 1.0, 5.8, 5.9, WHITE, border_rgb=MED_BLUE, border_pt=1)
add_rect(slide, 7.2, 1.0, 5.8, 0.5, MED_BLUE)
add_text_box(slide, "CIRCLE BREATHING SYSTEM FUNCTIONAL TEST", 7.3, 1.0, 5.6, 0.5,
font_size=11, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
circle_steps = [
("A", "Set APL (pop-off) valve to\nminimum (OPEN position)"),
("B", "Set fresh gas flow (FGF)\nto 5 L/min O₂"),
("C", "Occlude Y-piece with thumb;\nsqueeze reservoir bag"),
("D", "Pressure gauge should rise;\nAPL valve should open & vent"),
("E", "Release Y-piece;\nobserve bag refills freely"),
("F", "Perform 'To-and-Fro' test:\ncheck both one-way valves"),
("G", "FGF to minimal; observe\nno unintended pressure build-up"),
]
for k, (letter, desc) in enumerate(circle_steps):
ky = 1.6 + k * 0.67
add_rect(slide, 7.35, ky, 0.45, 0.58, GREEN)
add_text_box(slide, letter, 7.35, ky, 0.45, 0.58,
font_size=12, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_rect(slide, 7.85, ky, 4.95, 0.58, LIGHT_BLUE if k%2==0 else WHITE)
add_text_box(slide, desc, 7.9, ky, 4.85, 0.58,
font_size=9.5, color=DARK_GRAY, align=PP_ALIGN.LEFT)
# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 6 – Full 15-Item Summary Table
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "ASA Pre-Anaesthesia Checkout – All 15 Items Summary",
"Miller's Anesthesia 10e, Table 20.6 | * = Before each case; D = Daily")
add_footer(slide)
headers = ["#", "Item", "Frequency", "Responsible Party"]
col_widths = [0.5, 6.5, 1.5, 2.7]
col_x_starts = [0.25]
for w in col_widths[:-1]:
col_x_starts.append(col_x_starts[-1] + w)
# Header row
for j, (hdr, cw, cx) in enumerate(zip(headers, col_widths, col_x_starts)):
add_rect(slide, cx, 1.0, cw, 0.4, DARK_BLUE)
add_text_box(slide, hdr, cx, 1.0, cw, 0.4,
font_size=10, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
all_items = [
("1", "Verify auxiliary O₂ cylinder & SIRB present", "Daily", "Provider + Tech", TEAL),
("2", "Verify patient suction is adequate", "Each case", "Provider + Tech", GREEN),
("3", "Turn on workstation; confirm AC power", "Daily", "Provider or Tech", TEAL),
("4", "Verify availability of monitors & check alarms", "Each case", "Provider or Tech", GREEN),
("5", "Verify spare O₂ cylinder pressure is adequate", "Daily", "Provider or Tech", TEAL),
("6", "Verify pipeline gas pressures are ≥45 psi (O₂, N₂O, Air)", "Daily", "Provider or Tech", TEAL),
("7", "Verify vaporisers: filled, sealed, interlocked", "Daily", "Provider or Tech", TEAL),
("8", "Verify CO₂ absorbent is not exhausted", "Daily", "Provider or Tech", TEAL),
("9", "Calibrate O₂ monitor & check low-O₂ alarm", "Daily", "Provider only", TEAL),
("10", "Verify pressure is adequate on breathing system/vaporiser\n (Low-Pressure Leak Test)", "Daily", "Provider only", TEAL),
("11", "Confirm breathing circuit integrity & patency", "Each case", "Provider + Tech", GREEN),
("12", "Verify scavenging system is functional", "Each case", "Provider or Tech", GREEN),
("13", "Check and prepare airway management equipment", "Each case", "Provider only", GREEN),
("14", "Check and confirm medications & IV access", "Each case", "Provider only", GREEN),
("15", "Anaesthesia Time-Out (final pre-induction confirmation)", "Each case\n(immediately before)", "Provider only", AMBER),
]
row_h = 0.36
for r, (num, item, freq, party, color) in enumerate(all_items):
ry = 1.4 + r * row_h
bg = LIGHT_BLUE if r % 2 == 0 else WHITE
for j, (cw, cx) in enumerate(zip(col_widths, col_x_starts)):
add_rect(slide, cx, ry, cw, row_h, bg)
# Number colored
add_rect(slide, col_x_starts[0], ry, col_widths[0], row_h, color)
add_text_box(slide, num, col_x_starts[0], ry, col_widths[0], row_h,
font_size=9, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_text_box(slide, item, col_x_starts[1], ry, col_widths[1], row_h,
font_size=8.5, color=DARK_GRAY, align=PP_ALIGN.LEFT)
add_text_box(slide, freq, col_x_starts[2], ry, col_widths[2], row_h,
font_size=8.5, color=TEAL if "Daily" in freq else GREEN, bold=True,
align=PP_ALIGN.CENTER)
add_text_box(slide, party, col_x_starts[3], ry, col_widths[3], row_h,
font_size=8, color=DARK_GRAY, align=PP_ALIGN.CENTER)
# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 7 – Troubleshooting Flowchart
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "Intraoperative Anaesthesia Machine Failure – Troubleshooting Flowchart",
"General approach when equipment failure is suspected during anaesthesia")
add_footer(slide)
# Problem box at top
add_rect(slide, 3.5, 1.1, 6.3, 0.65, RED, border_rgb=DARK_BLUE, border_pt=2)
add_text_box(slide, "⚠ SUSPECTED ANAESTHESIA MACHINE FAILURE", 3.55, 1.1, 6.2, 0.65,
font_size=14, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
# Arrow down
add_rect(slide, 6.65, 1.75, 0.02, 0.3, MID_GRAY)
# Immediate action
add_rect(slide, 3.5, 2.05, 6.3, 0.65, AMBER, border_rgb=DARK_BLUE, border_pt=1.5)
add_text_box(slide, "IMMEDIATE ACTION:\nDISCONNECT patient from machine | Switch to SIRB (self-inflating bag) + O₂",
3.55, 2.05, 6.2, 0.65,
font_size=11, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
# Arrow down
add_rect(slide, 6.65, 2.70, 0.02, 0.28, MID_GRAY)
# Stabilise patient
add_rect(slide, 3.5, 2.98, 6.3, 0.6, MED_BLUE)
add_text_box(slide, "Stabilise Patient:\nManual ventilation | Maintain O₂ | Call for help",
3.55, 2.98, 6.2, 0.6,
font_size=11, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
# Arrow down
add_rect(slide, 6.65, 3.58, 0.02, 0.28, MID_GRAY)
# Decision
draw_diamond(slide, 6.667, 4.15, 4.5, 0.7,
AMBER, DARK_BLUE, "Can failure be\nidentified & corrected\nquickly?", font_size=10)
# YES branch
add_rect(slide, 8.5, 4.55, 0.02, 0.5, MID_GRAY)
add_rect(slide, 8.5, 5.1, 3.6, 0.65, GREEN_BG, border_rgb=GREEN, border_pt=1.5)
add_text_box(slide, "YES → Correct fault\n(e.g., reconnect circuit, replace tubing)\nResume machine ventilation",
8.55, 5.1, 3.5, 0.65, font_size=9, color=GREEN, align=PP_ALIGN.LEFT, bold=True)
# NO branch
add_rect(slide, 4.8, 4.55, 0.02, 0.5, MID_GRAY)
add_rect(slide, 3.2, 5.1, 3.6, 0.65, RED_BG, border_rgb=RED, border_pt=1.5)
add_text_box(slide, "NO → Do NOT return to machine\nUse alternative ventilation\nNotify senior & biomedical eng.",
3.25, 5.1, 3.5, 0.65, font_size=9, color=RED, align=PP_ALIGN.LEFT, bold=True)
# Key reminder
add_rect(slide, 2.0, 6.1, 9.3, 0.75, YELLOW_BG, border_rgb=AMBER, border_pt=2)
add_text_box(slide,
"KEY PRINCIPLE (Miller's Anesthesia 10e): "
'"When in doubt, ventilate and oxygenate the patient first via another method and troubleshoot later."',
2.1, 6.1, 9.1, 0.75, font_size=10.5, italic=True, color=DARK_BLUE,
align=PP_ALIGN.CENTER)
# ═════════════════════════════════════════════════════════════════════════════
# SAVE
# ═════════════════════════════════════════════════════════════════════════════
out_path = "/tmp/workspace/anaesthesia-checklist/Anaesthesia_Machine_Checklist_Protocol.pptx"
buf = BytesIO()
prs.save(buf)
with open(out_path, "wb") as f:
f.write(buf.getvalue())
print(f"Saved: {out_path}")
cd /tmp/workspace/anaesthesia-checklist && python build_pptx.py 2>&1
python -c " from pptx import Presentation prs = Presentation('/tmp/workspace/anaesthesia-checklist/Anaesthesia_Machine_Checklist_Protocol.pptx') print(f'Slides: {len(prs.slides)}') for i, slide in enumerate(prs.slides, 1): texts = [s.text_frame.text[:60] for s in slide.shapes if s.has_text_frame and s.text_frame.text.strip()] print(f' Slide {i}: {texts[0] if texts else \"(no text)\"}') import os; print(f'File size: {os.path.getsize(\"/tmp/workspace/anaesthesia-checklist/Anaesthesia_Machine_Checklist_Protocol.pptx\"):,} bytes') "
Anaesthesia Machine Checklist Protocol
Presentation · PPTX
| Slide | Content |
|---|---|
| 1 - Title | Cover slide with colour legend (Daily / Each Case / Critical) |
| 2 - Overview Flowchart | Top-level PAC flow: Start of Day → Daily Checks → Per-Case Checks → Time-Out → Induction, with decision diamonds and the 7 ASA Basic Requirements |
| 3 - Daily Checks | Items 1, 3, 5–10 in a two-column card layout with step details (O₂ cylinder, AC power, pipeline pressures, vaporisers, CO₂ absorbent, circle system, O₂ analyser) |
| 4 - Per-Case Checks | Items 2, 4, 11–15 in a vertical flowchart; Item 15 (Anaesthesia Time-Out) highlighted in a large amber panel with all 6 confirmation points and the MS MAIDS mnemonic |
| 5 - LPLT Flowchart | Step-by-step Low-Pressure Leak Test with PASS/FAIL branches + Circle System Functional Test (steps A–G) |
| 6 - Summary Table | All 15 ASA items in one colour-coded table (frequency + responsible party) |
| 7 - Troubleshooting | Intraoperative failure response flowchart: Failure detected → Disconnect → SIRB → Stabilise → Decision diamond → Yes/No branches, with the key Miller's principle quoted |
Oxygen failure protection device 5 marks short note pdf for MD anaesthesia with key points summarised
~/anaesthesia-checklist/build_ofpd_pdf.py
"""
Oxygen Failure Protection Device (OFPD)
5-Mark Short Note PDF for MD Anaesthesia
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon, Circle
from reportlab.graphics import renderPDF
from reportlab.platypus.flowables import Flowable
from reportlab.graphics.shapes import Group, Path
import io, os
# ─── Colour palette ───────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor('#1A3A5C')
MED_BLUE = colors.HexColor('#2E6DA8')
LIGHT_BLUE = colors.HexColor('#D6E8F7')
TEAL = colors.HexColor('#00878A')
GREEN = colors.HexColor('#2A7A4B')
GREEN_BG = colors.HexColor('#D4EDDA')
AMBER = colors.HexColor('#E88B00')
AMBER_BG = colors.HexColor('#FFF3CD')
RED = colors.HexColor('#C0392B')
RED_BG = colors.HexColor('#F8D7DA')
LIGHT_GRAY = colors.HexColor('#F4F6F9')
MID_GRAY = colors.HexColor('#88A0B4')
DARK_GRAY = colors.HexColor('#333333')
WHITE = colors.white
PAGE_W, PAGE_H = A4
MARGIN = 1.8 * cm
# ─── Document setup ──────────────────────────────────────────────────────────
out_path = "/tmp/workspace/anaesthesia-checklist/OFPD_MD_Anaesthesia_Short_Note.pdf"
doc = SimpleDocTemplate(
out_path,
pagesize=A4,
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=1.2*cm, bottomMargin=1.5*cm,
title="Oxygen Failure Protection Device – MD Anaesthesia Short Note",
)
# ─── Styles ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, parent=styles['Normal'], **kw)
title_style = S('title', fontSize=17, textColor=WHITE, alignment=TA_CENTER,
fontName='Helvetica-Bold', leading=22, spaceAfter=0)
subtitle_style = S('subtitle', fontSize=9.5, textColor=colors.HexColor('#AACCEE'),
alignment=TA_CENTER, fontName='Helvetica', leading=12)
section_hdr = S('sectionhdr', fontSize=11.5, textColor=WHITE,
fontName='Helvetica-Bold', leading=14, alignment=TA_LEFT)
body = S('body', fontSize=9.5, textColor=DARK_GRAY, leading=14, alignment=TA_JUSTIFY,
fontName='Helvetica', spaceAfter=3)
body_bold = S('bodybold', fontSize=9.5, textColor=DARK_GRAY, leading=14,
fontName='Helvetica-Bold', spaceAfter=2)
bullet_style = S('bullet', fontSize=9.5, textColor=DARK_GRAY, leading=14,
fontName='Helvetica', leftIndent=12, spaceAfter=2,
bulletIndent=0, firstLineIndent=0)
key_point = S('kp', fontSize=9.5, textColor=DARK_GRAY, leading=14,
fontName='Helvetica', leftIndent=16, spaceAfter=2)
small_gray = S('smgray', fontSize=8, textColor=MID_GRAY, alignment=TA_CENTER,
fontName='Helvetica-Oblique', leading=10)
ref_style = S('ref', fontSize=8, textColor=MID_GRAY, leading=11, alignment=TA_LEFT,
fontName='Helvetica-Oblique')
# ─── Custom Flowables ─────────────────────────────────────────────────────────
class ColorBox(Flowable):
"""Full-width coloured header box."""
def __init__(self, text, sub=None, bg=DARK_BLUE, h=1.55*cm):
self.text = text
self.sub = sub
self.bg = bg
self.h = h
self.width = PAGE_W - 2*MARGIN
Flowable.__init__(self)
def wrap(self, *args):
return (self.width, self.h)
def draw(self):
c = self.canv
c.setFillColor(self.bg)
c.rect(0, 0, self.width, self.h, fill=1, stroke=0)
# accent bar
c.setFillColor(AMBER)
c.rect(0, 0, 4, self.h, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 14)
c.drawString(12, self.h - 22, self.text)
if self.sub:
c.setFillColor(colors.HexColor('#AACCEE'))
c.setFont('Helvetica-Oblique', 8.5)
c.drawString(12, 5, self.sub)
class SectionHeader(Flowable):
"""Coloured section divider."""
def __init__(self, text, bg=MED_BLUE, h=0.55*cm):
self.text = text
self.bg = bg
self.h = h
self.width = PAGE_W - 2*MARGIN
Flowable.__init__(self)
def wrap(self, *args):
return (self.width, self.h)
def draw(self):
c = self.canv
c.setFillColor(self.bg)
c.rect(0, 0, self.width, self.h, fill=1, stroke=0)
c.setFillColor(AMBER)
c.rect(0, 0, 3.5, self.h, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 10)
c.drawString(10, (self.h - 10)/2 + 1, self.text)
class KeyPointBox(Flowable):
"""Amber-bordered key-point highlight box."""
def __init__(self, items, w=None, h=None):
self.items = items
self._w = w
self._h = h
Flowable.__init__(self)
def wrap(self, availW, availH):
self._w = availW
line_h = 15
self._h = 8 + len(self.items) * line_h + 8
return (self._w, self._h)
def draw(self):
c = self.canv
c.setFillColor(AMBER_BG)
c.setStrokeColor(AMBER)
c.setLineWidth(1.5)
c.roundRect(0, 0, self._w, self._h, 4, fill=1, stroke=1)
c.setFillColor(AMBER)
c.rect(0, 0, 4, self._h, fill=1, stroke=0)
c.setFillColor(DARK_GRAY)
c.setFont('Helvetica', 9)
for i, item in enumerate(reversed(self.items)):
y = 8 + i * 15
c.setFont('Helvetica-Bold', 9)
c.setFillColor(AMBER)
c.drawString(10, y, '★')
c.setFont('Helvetica', 9)
c.setFillColor(DARK_GRAY)
c.drawString(22, y, item)
class FlowchartOFPD(Flowable):
"""Simple OFPD mechanism flowchart."""
def __init__(self, w=None):
self._fw = w
Flowable.__init__(self)
def wrap(self, availW, availH):
self._fw = availW
self._fh = 7.5 * cm
return (self._fw, self._fh)
def _box(self, c, x, y, w, h, fill, text, font_size=8.5, text_color=WHITE, bold=False):
c.setFillColor(fill)
c.setStrokeColor(colors.HexColor('#AAAAAA'))
c.setLineWidth(0.5)
c.roundRect(x, y, w, h, 3, fill=1, stroke=1)
c.setFillColor(text_color)
fn = 'Helvetica-Bold' if bold else 'Helvetica'
c.setFont(fn, font_size)
lines = text.split('\n')
lh = font_size + 2
total = len(lines) * lh
start_y = y + (h - total)/2 + lh - 2
for ln in lines:
tw = c.stringWidth(ln, fn, font_size)
c.drawString(x + (w - tw)/2, start_y, ln)
start_y -= lh
def _arrow(self, c, x1, y1, x2, y2):
c.setStrokeColor(MID_GRAY)
c.setLineWidth(1.2)
c.line(x1, y1, x2, y2)
# arrowhead
c.setFillColor(MID_GRAY)
if y2 < y1: # going down
c.polygon([x2-4, y2+6, x2+4, y2+6, x2, y2], fill=1, stroke=0)
elif x2 > x1: # going right
c.polygon([x2-6, y2-4, x2-6, y2+4, x2, y2], fill=1, stroke=0)
def _diamond(self, c, cx, cy, w, h, fill, text, font_size=8):
pts = [cx, cy+h/2, cx+w/2, cy, cx, cy-h/2, cx-w/2, cy]
c.setFillColor(fill)
c.setStrokeColor(DARK_BLUE)
c.setLineWidth(1)
c.polygon(pts, fill=1, stroke=1)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', font_size)
lines = text.split('\n')
lh = font_size + 2
start_y = cy + (len(lines)-1)*lh/2 - 1
for ln in lines:
tw = c.stringWidth(ln, 'Helvetica-Bold', font_size)
c.drawString(cx - tw/2, start_y, ln)
start_y -= lh
def draw(self):
c = self.canv
fw = self._fw
fh = self._fh
# Background
c.setFillColor(LIGHT_GRAY)
c.rect(0, 0, fw, fh, fill=1, stroke=0)
# Title strip
c.setFillColor(DARK_BLUE)
c.rect(0, fh - 0.6*cm, fw, 0.6*cm, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont('Helvetica-Bold', 9)
title_txt = "OFPD / Fail-Safe Valve – Mechanism Flowchart"
c.drawString((fw - c.stringWidth(title_txt, 'Helvetica-Bold', 9))/2,
fh - 0.42*cm, title_txt)
# Node dimensions
bw = fw * 0.28
bh = 0.55 * cm
col1_x = 0.3 * cm
col2_x = fw/2 - bw/2
col3_x = fw - bw - 0.3*cm
# ── Left column: O₂ OK path ──────────────────────
y_start = fh - 1.3*cm
self._box(c, col2_x, y_start, bw, bh, DARK_BLUE,
'O₂ Pipeline\nSupply (50–55 psi)', font_size=7.5, bold=True)
# Arrow down
self._arrow(c, col2_x + bw/2, y_start, col2_x + bw/2, y_start - 0.4*cm)
# Intermediate pressure
self._box(c, col2_x, y_start - 0.4*cm - bh, bw, bh, MED_BLUE,
'Intermediate-Pressure\nSection (14–55 psi)', font_size=7.5)
self._arrow(c, col2_x + bw/2, y_start - 0.4*cm - bh,
col2_x + bw/2, y_start - 0.8*cm - 2*bh)
# Piloting pressure line
pilot_y = y_start - 0.8*cm - 2*bh - bh/2
c.setStrokeColor(TEAL)
c.setLineWidth(1)
c.setDash([3, 3])
c.line(col2_x + bw/2, pilot_y, col2_x + bw + 0.2*cm, pilot_y)
c.setDash([])
c.setFillColor(TEAL)
c.setFont('Helvetica-Oblique', 7)
c.drawString(col2_x + bw + 0.25*cm, pilot_y - 3, 'Piloting\npressure')
# Decision diamond
d_cx = col2_x + bw/2
d_cy = y_start - 1.15*cm - 2.5*bh
self._diamond(c, d_cx, d_cy, bw + 0.3*cm, bh + 0.25*cm,
AMBER, 'O₂ pressure\n≥ threshold?', font_size=8)
# YES branch (right) → valve open
yes_y = d_cy
yes_x = col3_x
c.setStrokeColor(MID_GRAY)
c.setLineWidth(1.2)
c.line(d_cx + (bw + 0.3*cm)/2, d_cy, yes_x, d_cy)
self._arrow(c, yes_x, d_cy, yes_x, d_cy - 0.35*cm)
c.setFillColor(GREEN)
c.setFont('Helvetica-Bold', 7.5)
c.drawString(d_cx + (bw + 0.3*cm)/2 + 2, d_cy + 3, 'YES (≥20–30 psi)')
self._box(c, yes_x, d_cy - 0.35*cm - bh, bw, bh, GREEN,
'Valve OPEN\nN₂O flows freely', font_size=7.5, bold=True)
self._arrow(c, yes_x + bw/2, d_cy - 0.35*cm - bh,
yes_x + bw/2, d_cy - 0.7*cm - 2*bh)
self._box(c, yes_x, d_cy - 0.7*cm - 2*bh, bw, bh,
colors.HexColor('#2A7A4B'),
'Normal gas mix\ndelivered', font_size=7.5)
# NO branch (left) → valve closed
no_x = col1_x
c.setStrokeColor(MID_GRAY)
c.setLineWidth(1.2)
c.line(d_cx - (bw + 0.3*cm)/2, d_cy, no_x + bw, d_cy)
self._arrow(c, no_x + bw, d_cy, no_x + bw, d_cy - 0.35*cm)
c.setFillColor(RED)
c.setFont('Helvetica-Bold', 7.5)
c.drawString(no_x, d_cy + 3, 'NO (< threshold)')
self._box(c, no_x, d_cy - 0.35*cm - bh, bw, bh, RED,
'Valve CLOSED\nN₂O cut off', font_size=7.5, bold=True)
self._arrow(c, no_x + bw/2, d_cy - 0.35*cm - bh,
no_x + bw/2, d_cy - 0.7*cm - 2*bh)
self._box(c, no_x, d_cy - 0.7*cm - 2*bh, bw, bh,
colors.HexColor('#8B0000'),
'Alarm sounds\nO₂ only flows', font_size=7.5, bold=False, text_color=WHITE)
# Down arrow from diamond
self._arrow(c, d_cx, d_cy - (bh + 0.25*cm)/2,
d_cx, d_cy - (bh + 0.25*cm)/2 - 0.3*cm)
# Warning note
note_y = 0.08 * cm
c.setFillColor(RED_BG)
c.setStrokeColor(RED)
c.setLineWidth(0.8)
c.roundRect(0.3*cm, note_y, fw - 0.6*cm, 0.55*cm, 3, fill=1, stroke=1)
c.setFillColor(RED)
c.setFont('Helvetica-Bold', 8)
warn = '⚠ Limitation: Does NOT protect if O₂ pipeline is contaminated/crossed with another gas'
c.drawString((fw - c.stringWidth(warn, 'Helvetica-Bold', 8))/2,
note_y + 5, warn)
class ComparisonTable(Flowable):
"""Binary vs Proportional device comparison."""
def __init__(self, w=None):
self._fw = w
Flowable.__init__(self)
def wrap(self, availW, availH):
self._fw = availW
self._fh = 4.8 * cm
return (self._fw, self._fh)
def draw(self):
c = self.canv
fw = self._fw
rows = [
('Feature', 'Binary (Threshold)\nShutoff Valve', 'Proportional Valve\n(OFPD / Balance Regulator)'),
('Also called', '"Fail-safe valve"\n"Nitrous cut-off"',
'Oxygen Failure Protection Device\n(OFPD), Balance regulator'),
('Mechanism', 'Open or closed (all-or-nothing)\nbased on O₂ pressure threshold',
'Proportionally reduces N₂O\nas O₂ pressure falls'),
('O₂ threshold', '20–30 psig (e.g. GE machines)',
'Completely shuts N₂O at 0.5 psig\n(other gases at 10 psig)'),
('Used on', 'Older GE/Datex-Ohmeda machines',
'Modern machines – ISO standard'),
('Limitation', 'All gases off if O₂ drops (sudden)',
'Does NOT prevent hypoxia if\npipeline crossover/contamination'),
]
col_w = [fw * 0.22, fw * 0.38, fw * 0.38]
row_h = self._fh / len(rows)
y_top = self._fh
for r, row in enumerate(rows):
y = y_top - (r + 1) * row_h
for ci, (cell, cw) in enumerate(zip(row, col_w)):
x = sum(col_w[:ci])
# Background
if r == 0:
bg = DARK_BLUE
elif ci == 0:
bg = LIGHT_BLUE
elif ci == 1:
bg = colors.HexColor('#FFF3CD') if r % 2 == 0 else WHITE
else:
bg = LIGHT_BLUE if r % 2 == 0 else colors.HexColor('#E8F4FD')
c.setFillColor(bg)
c.setStrokeColor(MID_GRAY)
c.setLineWidth(0.5)
c.rect(x, y, cw, row_h, fill=1, stroke=1)
# Text
fc = WHITE if r == 0 else DARK_GRAY
fn = 'Helvetica-Bold' if r == 0 or ci == 0 else 'Helvetica'
fs = 7.5
c.setFillColor(fc)
c.setFont(fn, fs)
lines = cell.split('\n')
lh = fs + 1.5
start_y = y + row_h/2 + (len(lines)-1)*lh/2
for ln in lines:
tw = c.stringWidth(ln, fn, fs)
c.drawString(x + (cw - tw)/2, start_y, ln)
start_y -= lh
# ─── Build content ────────────────────────────────────────────────────────────
def bullet(text, indent=12):
return Paragraph(f'<bullet>•</bullet> {text}', bullet_style)
def sub_bullet(text):
s = S('subb', parent=bullet_style, leftIndent=24, fontSize=9)
return Paragraph(f' – {text}', s)
story = []
# ── Title block ──────────────────────────────────────────────────────────────
story.append(ColorBox(
"OXYGEN FAILURE PROTECTION DEVICE (OFPD)",
sub="MD Anaesthesia | 5-Mark Short Note | Anaesthesia Machine Safety",
bg=DARK_BLUE, h=1.6*cm
))
story.append(Spacer(1, 4*mm))
# ── Introduction ─────────────────────────────────────────────────────────────
story.append(SectionHeader("1. DEFINITION & INTRODUCTION", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"The <b>Oxygen Failure Protection Device (OFPD)</b>, also known as the <b>fail-safe valve</b>, "
"<b>pressure-sensor shutoff valve</b>, or <b>balance regulator</b>, is a safety mechanism "
"incorporated in the <b>intermediate-pressure section</b> of the anaesthesia workstation. "
"Its primary function is to <b>reduce or cut off the supply of nitrous oxide</b> (and other "
"gases except oxygen) when the oxygen supply pressure falls below a critical threshold, "
"thereby minimising the risk of delivering a <b>hypoxic gas mixture</b> to the patient. "
"It is an <b>ISO standard</b> on all modern anaesthesia machines.",
body))
story.append(Spacer(1, 3*mm))
# ── Location ─────────────────────────────────────────────────────────────────
story.append(SectionHeader("2. LOCATION IN THE ANAESTHESIA MACHINE", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"The OFPD is situated in the <b>intermediate-pressure section</b> of the anaesthesia machine "
"(pipeline pressure reduced to 45–55 psi). One device is present in the gas line supplying "
"<b>each flowmeter except oxygen</b>. It receives a <b>'piloting pressure' signal</b> derived "
"from the oxygen supply line (or secondary regulator).",
body))
story.append(Spacer(1, 2*mm))
table_loc = Table(
[
[Paragraph('<b>Section</b>', body_bold),
Paragraph('<b>Pressure Range</b>', body_bold),
Paragraph('<b>Key Components</b>', body_bold)],
['High-pressure', '1900–2200 psi (cylinder)', 'Cylinder valves, reducing valves'],
['Intermediate-pressure', '45–55 psi (pipeline)', 'OFPD, flush valve, proportioning system'],
['Low-pressure', '≤ 16 psi (flow control)', 'Flowmeters, vaporisers, common gas outlet'],
],
colWidths=[(PAGE_W - 2*MARGIN) * x for x in [0.22, 0.25, 0.53]],
)
table_loc.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), DARK_BLUE),
('TEXTCOLOR', (0, 0), (-1, 0), WHITE),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 0), (-1, -1), 8.5),
('BACKGROUND', (0, 2), (-1, 2), LIGHT_BLUE),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [WHITE, LIGHT_BLUE, WHITE]),
('ROWHEIGHT', (0, 0), (-1, -1), 0.55*cm),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('GRID', (0, 0), (-1, -1), 0.5, MID_GRAY),
('LEFTPADDING',(0, 0), (-1, -1), 5),
]))
story.append(table_loc)
story.append(Spacer(1, 3*mm))
# ── Types ─────────────────────────────────────────────────────────────────────
story.append(SectionHeader("3. TYPES OF OFPD", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph("<b>A. Binary (Threshold) Shutoff Valve</b>", body_bold))
story.append(bullet("Acts in an <b>all-or-nothing</b> fashion based on a threshold O₂ pressure"))
story.append(bullet("Examples: GE/Datex-Ohmeda <i>pressure-sensor shutoff valve</i>"))
story.append(bullet("Threshold: <b>20–30 psig</b>; valve opens when O₂ pressure exceeds threshold"))
story.append(bullet("Mechanism: oxygen pressure acts on a mobile diaphragm → moves piston "
"upward → pin opens the N₂O valve; spring closes it when O₂ pressure is low"))
story.append(bullet("Limitation: <b>abrupt</b> cutoff; N₂O completely stops if O₂ drops even transiently"))
story.append(Spacer(1, 2*mm))
story.append(Paragraph("<b>B. Proportional Valve (Balance Regulator / OFPD)</b>", body_bold))
story.append(bullet("Modern design – <b>proportionately reduces</b> N₂O pressure as O₂ pressure falls"))
story.append(bullet("Completely shuts off N₂O only below a <b>minimum O₂ pressure</b> (e.g. <b>0.5 psig</b> "
"for N₂O; <b>10 psig</b> for other gases)"))
story.append(bullet("ISO standard on all contemporary workstations"))
story.append(bullet("Air flow may bypass the OFPD on some machines, allowing air administration "
"even with O₂ failure"))
story.append(Spacer(1, 3*mm))
# ── Flowchart ─────────────────────────────────────────────────────────────────
story.append(SectionHeader("4. MECHANISM FLOWCHART", bg=TEAL))
story.append(Spacer(1, 2*mm))
story.append(FlowchartOFPD())
story.append(Spacer(1, 3*mm))
# ── Comparison table ──────────────────────────────────────────────────────────
story.append(SectionHeader("5. COMPARISON: BINARY vs PROPORTIONAL DEVICE", bg=TEAL))
story.append(Spacer(1, 2*mm))
story.append(ComparisonTable())
story.append(Spacer(1, 3*mm))
# ── Proportioning systems ─────────────────────────────────────────────────────
story.append(SectionHeader("6. RELATIONSHIP WITH PROPORTIONING SYSTEMS", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"The OFPD works in conjunction with — but is <b>distinct from</b> — the proportioning system. "
"While the OFPD acts upstream (at the piloting pressure level), proportioning systems control "
"the N₂O:O₂ <b>flow ratio</b> at the flow control valve level to prevent a hypoxic mixture.",
body))
story.append(Spacer(1, 2*mm))
prop_data = [
[Paragraph('<b>Device</b>', body_bold), Paragraph('<b>Mechanism</b>', body_bold),
Paragraph('<b>Minimum FiO₂ ensured</b>', body_bold)],
['Dräger S-ORC\n(Sensitive O₂ Ratio Controller)',
'Pneumatic-mechanical; diaphragm-driven N₂O\nproportioning valve; needs ≥ 200 mL/min O₂',
'≥ 25% O₂\n(max 75% N₂O)'],
['GE/Datex-Ohmeda\nLink-25 System',
'Mechanical chain-link coupling of O₂\nand N₂O flow knobs',
'≥ 25% O₂\n(3:1 max N₂O:O₂ ratio)'],
]
prop_table = Table(prop_data,
colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.28, 0.44, 0.28]])
prop_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), DARK_BLUE),
('TEXTCOLOR', (0, 0), (-1, 0), WHITE),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 0), (-1, -1), 8.5),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, WHITE]),
('ROWHEIGHT', (0, 0), (-1, -1), 0.6*cm),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('GRID', (0, 0), (-1, -1), 0.5, MID_GRAY),
('LEFTPADDING', (0, 0), (-1, -1), 5),
]))
story.append(prop_table)
story.append(Spacer(1, 3*mm))
# ── Limitations ───────────────────────────────────────────────────────────────
story.append(SectionHeader("7. LIMITATIONS – WHY 'FAIL-SAFE' IS A MISNOMER", bg=RED))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"The term <b>'fail-safe'</b> is widely regarded as a <b>misnomer</b> because the OFPD "
"<i>only</i> responds to <b>oxygen pressure</b>, not to oxygen <b>concentration</b>. "
"The following situations can cause a hypoxic mixture <b>despite the OFPD being open</b>:",
body))
story.append(Spacer(1, 2*mm))
lim_data = [
['Situation', 'Why OFPD Fails to Protect'],
['Pipeline gas crossover / contamination\n(N₂O in O₂ pipeline)',
'O₂ pipeline pressure is maintained by N₂O → OFPD stays OPEN\nO₂ analyser is the only safeguard'],
['Hypoxic gas mixture at adequate pressure\n(e.g. <21% O₂ but pressure ≥ threshold)',
'OFPD cannot distinguish O₂ from any other gas at the\npiloting line — it only senses pressure'],
['Air bypass (some machines)',
'Air can be delivered even with O₂ failure if its\nline bypasses the OFPD'],
['Downstream leaks (low-pressure circuit)',
'OFPD is upstream; leaks after flowmeters are\nnot detected by this device'],
['Flowmeter bobbin float leak\n(cracked or stuck float)',
'N₂O may continue to flow into the common outlet\neven if OFPD is closed (gas escapes leak)'],
]
lim_table = Table(lim_data,
colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.42, 0.58]])
lim_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), RED),
('TEXTCOLOR', (0, 0), (-1, 0), WHITE),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
('FONTSIZE', (0, 0), (-1, -1), 8.5),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [RED_BG, WHITE]),
('ROWHEIGHT', (0, 0), (-1, -1), 0.65*cm),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('ALIGN', (0, 0), (0, -1), 'LEFT'),
('ALIGN', (1, 0), (1, -1), 'LEFT'),
('GRID', (0, 0), (-1, -1), 0.5, MID_GRAY),
('LEFTPADDING', (0, 0), (-1, -1), 6),
]))
story.append(lim_table)
story.append(Spacer(1, 3*mm))
# ── Additional safeguards ─────────────────────────────────────────────────────
story.append(SectionHeader("8. ADDITIONAL SAFEGUARDS (Work with OFPD)", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(bullet("<b>Oxygen supply low-pressure alarm</b> – audible alarm when inlet O₂ "
"pressure drops below 20–30 psig"))
story.append(bullet("<b>Oxygen analyser (FiO₂ monitor)</b> – <i>only</i> monitor that detects "
"downstream problems incl. pipeline crossover; most important safeguard"))
story.append(bullet("<b>Proportioning systems</b> (SORC / Link-25) – prevent hypoxic N₂O:O₂ ratio "
"at flow control valve level (min FiO₂ 25%)"))
story.append(bullet("<b>Oxygen flush valve</b> – delivers 100% O₂ at 35–75 L/min directly, bypassing vaporisers"))
story.append(bullet("<b>Minimum O₂ flow requirement</b> – modern machines mandate a minimum O₂ "
"flow (often 200 mL/min) that cannot be turned off"))
story.append(Spacer(1, 3*mm))
# ── Key points summary ────────────────────────────────────────────────────────
story.append(SectionHeader("★ KEY POINTS SUMMARY (For Exam)", bg=AMBER))
story.append(Spacer(1, 2*mm))
story.append(KeyPointBox([
"OFPD is in the INTERMEDIATE-pressure section; present in every gas line EXCEPT oxygen",
"Two types: Binary (threshold shutoff, e.g. 20 psig GE) vs Proportional (balance regulator, modern machines)",
"Modern OFPD completely shuts N₂O only at ≤ 0.5 psig O₂; proportionally reduces above that",
"'Fail-safe' is a MISNOMER – does NOT protect against pipeline crossover or contamination",
"O₂ ANALYSER (FiO₂ monitor) is the ONLY safeguard against pipeline contamination/crossover",
"OFPD ≠ Proportioning system: OFPD works at piloting pressure; proportioning at flow knob level",
"Air may bypass OFPD on some machines (air delivery possible even in O₂ failure)",
"Low-pressure circuit leaks are NOT detected by OFPD (it is an upstream device)",
"Dräger S-ORC: minimum 200 mL/min O₂ required for N₂O proportioning valve to open",
"GE Link-25: mechanical chain-link; max N₂O:O₂ ratio = 3:1 (ensures ≥25% FiO₂)",
]))
story.append(Spacer(1, 3*mm))
# ── References ────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=MID_GRAY, spaceAfter=3))
story.append(Paragraph(
"<b>References:</b> "
"Miller's Anesthesia 10e (Pardo & Miller), Chapter 20, pp. 2241–2258 | "
"Morgan & Mikhail's Clinical Anesthesiology 7e, Chapter 4, pp. 114–115 | "
"Barash Clinical Anesthesia 9e, Chapter 25, pp. 1943–1945 | "
"ISO 80601-2-13: Medical Electrical Equipment – Anaesthetic Workstations",
ref_style))
# ── Build PDF ─────────────────────────────────────────────────────────────────
def on_first_page(canvas, doc):
canvas.saveState()
canvas.setFillColor(colors.HexColor('#ECF0F5'))
canvas.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
canvas.setFont('Helvetica', 7.5)
canvas.setFillColor(MID_GRAY)
canvas.drawString(MARGIN, 0.22*cm,
"Oxygen Failure Protection Device – MD Anaesthesia Short Note")
canvas.drawRightString(PAGE_W - MARGIN, 0.22*cm, "Page 1")
canvas.restoreState()
def on_later_pages(canvas, doc):
canvas.saveState()
canvas.setFillColor(colors.HexColor('#ECF0F5'))
canvas.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
canvas.setFont('Helvetica', 7.5)
canvas.setFillColor(MID_GRAY)
canvas.drawString(MARGIN, 0.22*cm,
"Oxygen Failure Protection Device – MD Anaesthesia Short Note")
canvas.drawRightString(PAGE_W - MARGIN, 0.22*cm, f"Page {doc.page}")
canvas.restoreState()
doc.build(story, onFirstPage=on_first_page, onLaterPages=on_later_pages)
size = os.path.getsize(out_path)
print(f"Saved: {out_path} ({size:,} bytes)")
cd /tmp/workspace/anaesthesia-checklist && python build_ofpd_pdf.py 2>&1
~/anaesthesia-checklist/build_ofpd_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_ofpd_pdf.py'
~/anaesthesia-checklist/build_ofpd_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_ofpd_pdf.py'
cd /tmp/workspace/anaesthesia-checklist && python build_ofpd_pdf.py 2>&1
OFPD MD Anaesthesia Short Note
PDF Document · PDF
| Type | Mechanism | Threshold | Used in |
|---|---|---|---|
| Binary (threshold) shutoff | All-or-nothing; spring closes valve | 20–30 psig | Older GE/Datex-Ohmeda |
| Proportional (balance regulator / OFPD) | Proportionally reduces N₂O; fully shuts at 0.5 psig | 0.5 psig (N₂O) / 10 psig (others) | All modern machines (ISO std) |
O₂ pipeline (50–55 psi)
↓
Intermediate-pressure section
↓ [piloting pressure line] ────────→ OFPD
↓
O₂ ≥ threshold?
/ \
YES NO
↓ ↓
Valve OPEN Valve CLOSED
N₂O flows freely N₂O cut off + alarm