~/toxidrome-flashcards/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.platypus import PageBreak
OUTPUT = "/tmp/workspace/toxidrome-flashcards/Toxidrome_Mnemonics_Flashcard.pdf"
# ββ Colour palette ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
C_BG = colors.HexColor("#0D1B2A") # deep navy
C_CARD1 = colors.HexColor("#1A2E45") # card bg
C_ACCENT1 = colors.HexColor("#00BCD4") # cyan (cholinergic)
C_ACCENT2 = colors.HexColor("#FF7043") # orange (anticholinergic)
C_ACCENT3 = colors.HexColor("#66BB6A") # green (opioid)
C_ACCENT4 = colors.HexColor("#AB47BC") # purple (sympathomimetic)
C_ACCENT5 = colors.HexColor("#FFA726") # amber (sedative-hypnotic)
C_ACCENT6 = colors.HexColor("#EF5350") # red (bradycardia/PACED)
C_ACCENT7 = colors.HexColor("#29B6F6") # blue (miosis/COPS)
C_ACCENT8 = colors.HexColor("#EC407A") # pink (seizures)
C_ACCENT9 = colors.HexColor("#8D6E63") # brown (coma)
C_WHITE = colors.white
C_LIGHT = colors.HexColor("#E0F7FA")
C_YELLOW = colors.HexColor("#FFF9C4")
C_DARK_TEXT = colors.HexColor("#1A1A2E")
# ββ Styles ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
title_style = S("title",
fontSize=22, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_CENTER,
spaceAfter=4)
subtitle_style = S("subtitle",
fontSize=11, fontName="Helvetica",
textColor=C_ACCENT1, alignment=TA_CENTER,
spaceAfter=2)
card_header_style = S("card_header",
fontSize=13, fontName="Helvetica-Bold",
textColor=C_WHITE, alignment=TA_LEFT,
spaceAfter=2, leading=16)
mnemonic_style = S("mnemonic",
fontSize=16, fontName="Helvetica-Bold",
textColor=C_YELLOW, alignment=TA_CENTER,
spaceAfter=4, leading=20)
poem_style = S("poem",
fontSize=9, fontName="Helvetica-Oblique",
textColor=C_LIGHT, alignment=TA_CENTER,
leading=14, spaceAfter=2)
body_style = S("body",
fontSize=9, fontName="Helvetica",
textColor=C_WHITE, leading=13)
letter_style = S("letter",
fontSize=11, fontName="Helvetica-Bold",
textColor=C_YELLOW, leading=14)
table_body = S("table_body",
fontSize=8.5, fontName="Helvetica",
textColor=C_DARK_TEXT, leading=12)
table_header = S("table_header",
fontSize=9, fontName="Helvetica-Bold",
textColor=C_WHITE, leading=12)
section_title = S("section_title",
fontSize=14, fontName="Helvetica-Bold",
textColor=C_YELLOW, alignment=TA_CENTER,
spaceAfter=6, spaceBefore=8)
footer_style = S("footer",
fontSize=7, fontName="Helvetica",
textColor=colors.HexColor("#607D8B"),
alignment=TA_CENTER)
# ββ Helper: make a coloured card ββββββββββββββββββββββββββββββββββββββββββββ
def card(content_rows, accent, col_widths, title_row=None):
"""Wrap a list of [col1, col2] rows in a styled table."""
data = []
if title_row:
data.append(title_row)
data.extend(content_rows)
ts = TableStyle([
("BACKGROUND", (0, 0), (-1, -1), C_CARD1),
("ROWBACKGROUNDS",(0, 0), (-1, -1), [C_CARD1, colors.HexColor("#162235")]),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING",(0, 0), (-1, -1), 5),
("ROUNDEDCORNERS", (0, 0), (-1, -1), 4),
("BOX", (0, 0), (-1, -1), 1.5, accent),
("LINEBELOW", (0, 0), (-1, 0), 1, accent),
("VALIGN", (0, 0), (-1, -1), "TOP"),
])
if title_row:
ts.add("BACKGROUND", (0, 0), (-1, 0), accent)
ts.add("SPAN", (0, 0), (-1, 0))
t = Table(data, colWidths=col_widths)
t.setStyle(ts)
return t
def make_letter_table(letter_data, accent, col_widths=(18*mm, 145*mm)):
"""letter_data: list of (letter, description)"""
rows = []
for ltr, desc in letter_data:
rows.append([
Paragraph(ltr, letter_style),
Paragraph(desc, body_style),
])
ts = TableStyle([
("BACKGROUND", (0, 0), (-1, -1), C_CARD1),
("ROWBACKGROUNDS",(0, 0), (-1, -1), [C_CARD1, colors.HexColor("#162235")]),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("RIGHTPADDING", (0, 0), (-1, -1), 6),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
("BOX", (0, 0), (-1, -1), 1.5, accent),
("LINEBEFORE", (0, 0), (0, -1), 3, accent),
("VALIGN", (0, 0), (-1, -1), "TOP"),
])
t = Table(rows, colWidths=list(col_widths))
t.setStyle(ts)
return t
def header_banner(text, accent):
data = [[Paragraph(text, card_header_style)]]
ts = TableStyle([
("BACKGROUND", (0, 0), (-1, -1), accent),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("RIGHTPADDING", (0, 0), (-1, -1), 10),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING",(0, 0), (-1, -1), 6),
("ROUNDEDCORNERS",(0, 0),(-1, -1), 3),
])
t = Table(data, colWidths=[168*mm])
t.setStyle(ts)
return t
# ββ Page background ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def on_page(canvas, doc):
canvas.saveState()
canvas.setFillColor(C_BG)
canvas.rect(0, 0, A4[0], A4[1], fill=1, stroke=0)
# footer
canvas.setFont("Helvetica", 7)
canvas.setFillColor(colors.HexColor("#455A64"))
canvas.drawCentredString(A4[0]/2, 10*mm,
"Toxidrome Mnemonics Quick Reference β’ Sources: Goldman-Cecil Medicine, LITFL")
canvas.restoreState()
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# BUILD DOCUMENT
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=20*mm, rightMargin=20*mm,
topMargin=18*mm, bottomMargin=18*mm,
)
story = []
# ββ Cover banner ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
cover_data = [[
Paragraph("β TOXIDROME MNEMONICS", title_style),
Paragraph("Quick Reference Flashcard", subtitle_style),
Paragraph("Bradycardia Β· Tachycardia Β· Miosis Β· Mydriasis Β· Seizures Β· Coma Β· Cholinergic Β· Anticholinergic Β· Opioid Β· Sympathomimetic", subtitle_style),
]]
cover_ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), colors.HexColor("#0A1628")),
("BOX", (0,0),(-1,-1), 2, C_ACCENT1),
("LEFTPADDING", (0,0),(-1,-1), 12),
("RIGHTPADDING", (0,0),(-1,-1), 12),
("TOPPADDING", (0,0),(-1,-1), 10),
("BOTTOMPADDING",(0,0),(-1,-1), 10),
("ALIGN", (0,0),(-1,-1), "CENTER"),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
])
cover_t = Table(cover_data, colWidths=[168*mm])
cover_t.setStyle(cover_ts)
story.append(cover_t)
story.append(Spacer(1, 6*mm))
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# SECTION 1 β SYMPTOM-BASED MNEMONICS (2-column layout)
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(Paragraph("βΈ SYMPTOM-BASED MNEMONICS", section_title))
# Left card: BRADYCARDIA (PACED) | Right card: TACHYCARDIA (FAST)
paced_rows = [
("P", "Propranolol (beta-blockers), Poppies (opiates), Propoxyphene, Physostigmine"),
("A", "Anticholinesterases (cholinergics), Antiarrhythmics"),
("C", "Clonidine (alpha-2 agonist), Calcium channel blockers"),
("E", "Ethanol and other alcohols"),
("D", "Digoxin, Digitalis"),
]
fast_rows = [
("F", "Free base cocaine, Freon"),
("A", "Anticholinergics, Amphetamines"),
("S", "Sympathomimetics, Salicylates"),
("T", "Theophylline, TCAs (tricyclics)"),
]
def mini_letter_table(rows, accent, lw=12*mm, dw=60*mm):
data = [[Paragraph(l, letter_style), Paragraph(d, S("d", fontSize=8, fontName="Helvetica", textColor=C_WHITE, leading=11))]
for l,d in rows]
ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("ROWBACKGROUNDS",(0,0),(-1,-1), [C_CARD1, colors.HexColor("#162235")]),
("LEFTPADDING", (0,0),(-1,-1), 4),
("RIGHTPADDING", (0,0),(-1,-1), 4),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LINEBEFORE", (0,0),(0,-1), 3, accent),
("VALIGN", (0,0),(-1,-1), "TOP"),
])
t = Table(data, colWidths=[lw, dw])
t.setStyle(ts)
return t
def symptom_card(title, mnemonic, rows, accent, width=80*mm):
lw = 10*mm; dw = width - lw - 6*mm
inner = mini_letter_table(rows, accent, lw, dw)
data = [
[Paragraph(f"<b>{title}</b>", S("ch", fontSize=10, fontName="Helvetica-Bold", textColor=C_WHITE, leading=13))],
[Paragraph(mnemonic, S("mn", fontSize=18, fontName="Helvetica-Bold", textColor=C_YELLOW, alignment=TA_CENTER, leading=22))],
[inner],
]
ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("BACKGROUND", (0,0),(-1,0), accent),
("BOX", (0,0),(-1,-1), 1.5, accent),
("LEFTPADDING", (0,0),(-1,-1), 6),
("RIGHTPADDING", (0,0),(-1,-1), 6),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("ALIGN", (0,1),(-1,1), "CENTER"),
("VALIGN", (0,0),(-1,-1), "TOP"),
])
t = Table(data, colWidths=[width - 2*mm])
t.setStyle(ts)
return t
brad_card = symptom_card("π« BRADYCARDIA", "PACED", paced_rows, C_ACCENT6, 82*mm)
tach_card = symptom_card("π TACHYCARDIA", "FAST", fast_rows, C_ACCENT5, 82*mm)
row1 = Table([[brad_card, tach_card]], colWidths=[84*mm, 84*mm])
row1.setStyle(TableStyle([
("LEFTPADDING", (0,0),(-1,-1), 2),
("RIGHTPADDING", (0,0),(-1,-1), 2),
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(row1)
story.append(Spacer(1, 4*mm))
# MIOSIS (COPS) | MYDRIASIS (SAW)
cops_rows = [
("C", "Cholinergics, Clonidine, Carbamates"),
("O", "Opioids, Organophosphates"),
("P", "Phenothiazines, Pilocarpine, Pontine bleed"),
("S", "Sedative-hypnotics"),
]
saw_rows = [
("S", "Sympathomimetics (cocaine, amphetamines)"),
("A", "Anticholinergics (atropine, antihistamines)"),
("W", "Withdrawal (opioid, alcohol, benzo)"),
]
miosis_card = symptom_card("π MIOSIS (Pinpoint)", "COPS", cops_rows, C_ACCENT7, 82*mm)
mydrias_card = symptom_card("π MYDRIASIS (Dilated)", "SAW", saw_rows, C_ACCENT4, 82*mm)
row2 = Table([[miosis_card, mydrias_card]], colWidths=[84*mm, 84*mm])
row2.setStyle(TableStyle([
("LEFTPADDING", (0,0),(-1,-1), 2),
("RIGHTPADDING", (0,0),(-1,-1), 2),
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(row2)
story.append(Spacer(1, 4*mm))
# SEIZURES β OTIS CAMPBELL (full width)
story.append(header_banner("β‘ SEIZURES β OTIS CAMPBELL", C_ACCENT8))
otis_rows = [
("O", "Organophosphates, Oral hypoglycemics"),
("T", "Tricyclic antidepressants (TCAs)"),
("I", "Isoniazid (INH), Insulin"),
("S", "Sympathomimetics, Strychnine, Salicylates"),
("C", "Camphor, Cocaine, CO, Cyanide, Chlorinated HCs"),
("A", "Amphetamines, Anticholinergics"),
("M", "Methylxanthines (theophylline/caffeine), Methanol"),
("P", "Phencyclidine (PCP), Propranolol"),
("B", "Benzo withdrawal, Botanicals, Bupropion, GHB"),
("E", "Ethanol withdrawal, Ethylene glycol"),
("L", "Lithium, Lidocaine"),
("L", "Lead, Lindane"),
]
# 3-column layout for OTIS
otis_col1 = otis_rows[:4]
otis_col2 = otis_rows[4:8]
otis_col3 = otis_rows[8:]
def otis_col(rows, accent=C_ACCENT8):
data = [[Paragraph(l, S("ol",fontSize=11,fontName="Helvetica-Bold",textColor=C_YELLOW,leading=14)),
Paragraph(d, S("od",fontSize=8,fontName="Helvetica",textColor=C_WHITE,leading=11))]
for l,d in rows]
ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("ROWBACKGROUNDS",(0,0),(-1,-1), [C_CARD1, colors.HexColor("#162235")]),
("LEFTPADDING", (0,0),(-1,-1), 4),
("RIGHTPADDING", (0,0),(-1,-1), 4),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LINEBEFORE", (0,0),(0,-1), 3, accent),
("VALIGN", (0,0),(-1,-1), "TOP"),
])
t = Table(data, colWidths=[10*mm, 44*mm])
t.setStyle(ts)
return t
otis_table = Table(
[[otis_col(otis_col1), otis_col(otis_col2), otis_col(otis_col3)]],
colWidths=[56*mm, 56*mm, 56*mm]
)
otis_table.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("BOX", (0,0),(-1,-1), 1.5, C_ACCENT8),
("LEFTPADDING",(0,0),(-1,-1), 2),
("RIGHTPADDING",(0,0),(-1,-1), 2),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(otis_table)
story.append(Spacer(1, 4*mm))
# COMA β LETHARGIC
story.append(header_banner("π€ COMA β LETHARGIC", C_ACCENT9))
lethargic_rows = [
("L", "Lead, Lithium"),
("E", "Ethanol, Ethylene glycol"),
("T", "TCAs, Thallium, Toluene"),
("H", "Heroin, Hemlock, Heavy metals, Hydrogen sulfide, Hypoglycemics"),
("A", "Arsenic, Antidepressants, Anticonvulsants, Antipsychotics, Antihistamines"),
("R", "Rohypnol (sedative-hypnotics), Risperidone"),
("G", "GHB"),
("I", "Isoniazid, Insulin"),
("C", "Carbon monoxide, Cyanide, Clonidine"),
]
leth_col1 = lethargic_rows[:5]
leth_col2 = lethargic_rows[5:]
def leth_col(rows, w1=10*mm, w2=68*mm):
data = [[Paragraph(l, S("ll",fontSize=11,fontName="Helvetica-Bold",textColor=C_YELLOW,leading=14)),
Paragraph(d, S("ld",fontSize=8,fontName="Helvetica",textColor=C_WHITE,leading=11))]
for l,d in rows]
ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("ROWBACKGROUNDS",(0,0),(-1,-1), [C_CARD1, colors.HexColor("#162235")]),
("LEFTPADDING", (0,0),(-1,-1), 4),
("RIGHTPADDING", (0,0),(-1,-1), 4),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LINEBEFORE", (0,0),(0,-1), 3, C_ACCENT9),
("VALIGN", (0,0),(-1,-1), "TOP"),
])
t = Table(data, colWidths=[w1, w2])
t.setStyle(ts)
return t
leth_table = Table(
[[leth_col(leth_col1), leth_col(leth_col2)]],
colWidths=[84*mm, 84*mm]
)
leth_table.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("BOX", (0,0),(-1,-1), 1.5, C_ACCENT9),
("LEFTPADDING", (0,0),(-1,-1), 2),
("RIGHTPADDING",(0,0),(-1,-1), 2),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(leth_table)
story.append(Spacer(1, 4*mm))
# DIALYZABLE β PLASMA TV
story.append(header_banner("π©Έ DIALYZABLE TOXINS β PLASMA TV", colors.HexColor("#26C6DA")))
plasma_rows = [
("P","Phenobarbital"), ("L","Lithium"), ("A","Acidosis (methanol, ethylene glycol)"),
("S","Salicylates"), ("M","Metformin"), ("A","Alcohols"),
("T","Theophylline"), ("V","Valproic acid"),
]
plasma_data = [[Paragraph(l, S("pl",fontSize=11,fontName="Helvetica-Bold",textColor=C_YELLOW,leading=14)),
Paragraph(d, S("pd",fontSize=9,fontName="Helvetica",textColor=C_WHITE,leading=12))]
for l,d in plasma_rows]
plasma_ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("ROWBACKGROUNDS",(0,0),(-1,-1), [C_CARD1, colors.HexColor("#162235")]),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LINEBEFORE", (0,0),(0,-1), 3, colors.HexColor("#26C6DA")),
("BOX", (0,0),(-1,-1), 1.5, colors.HexColor("#26C6DA")),
("VALIGN", (0,0),(-1,-1), "TOP"),
])
# Split into 2 columns of 4
plasma_left = plasma_rows[:4]
plasma_right = plasma_rows[4:]
def plasma_col(rows):
data = [[Paragraph(l, S("pl2",fontSize=11,fontName="Helvetica-Bold",textColor=C_YELLOW,leading=14)),
Paragraph(d, S("pd2",fontSize=9,fontName="Helvetica",textColor=C_WHITE,leading=12))]
for l,d in rows]
ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("ROWBACKGROUNDS",(0,0),(-1,-1), [C_CARD1, colors.HexColor("#162235")]),
("LEFTPADDING", (0,0),(-1,-1), 6),
("RIGHTPADDING", (0,0),(-1,-1), 6),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LINEBEFORE", (0,0),(0,-1), 3, colors.HexColor("#26C6DA")),
("VALIGN", (0,0),(-1,-1), "TOP"),
])
t = Table(data, colWidths=[10*mm, 70*mm])
t.setStyle(ts)
return t
plasma_table = Table([[plasma_col(plasma_left), plasma_col(plasma_right)]], colWidths=[84*mm, 84*mm])
plasma_table.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("BOX", (0,0),(-1,-1), 1.5, colors.HexColor("#26C6DA")),
("LEFTPADDING", (0,0),(-1,-1), 2),
("RIGHTPADDING",(0,0),(-1,-1), 2),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(plasma_table)
# ββββββββββββββββββββββββββ PAGE 2 ββββββββββββββββββββββββββββββββββββββββββ
story.append(PageBreak())
story.append(Paragraph("βΈ THE 5 CLASSIC TOXIDROMES", section_title))
# Big comparison table
tox_headers = ["Toxidrome", "HR", "BP", "Pupils", "Skin", "Key Clue / Mnemonic"]
tox_data = [
[Paragraph(h, S("th",fontSize=9,fontName="Helvetica-Bold",textColor=C_WHITE,leading=12)) for h in tox_headers],
[
Paragraph("π§ͺ Cholinergic\n(OPs, nerve agents, carbamates)", S("tc",fontSize=8.5,fontName="Helvetica-Bold",textColor=C_ACCENT1,leading=12)),
Paragraph("β Low", S("tv",fontSize=8.5,fontName="Helvetica",textColor=colors.HexColor("#EF9A9A"),leading=12)),
Paragraph("Variable", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_WHITE,leading=12)),
Paragraph("Miosis\n(pinpoint)", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("Wet\n(diaphoresis)", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("DUMBELLS / SLUDGE\nBronchorrhea, fasciculations", S("tv",fontSize=8,fontName="Helvetica",textColor=C_YELLOW,leading=11)),
],
[
Paragraph("π΅ Anticholinergic\n(atropine, antihistamines, Datura, TCAs)", S("tc",fontSize=8.5,fontName="Helvetica-Bold",textColor=C_ACCENT2,leading=12)),
Paragraph("β High", S("tv",fontSize=8.5,fontName="Helvetica",textColor=colors.HexColor("#A5D6A7"),leading=12)),
Paragraph("Normal/High", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_WHITE,leading=12)),
Paragraph("Mydriasis\n(dilated)", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("Dry, Hot\nFlushed", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("'Blind as a bat, Mad as a hatter,\nRed as a beet, Dry as a bone'", S("tv",fontSize=8,fontName="Helvetica-Oblique",textColor=C_YELLOW,leading=11)),
],
[
Paragraph("β‘ Sympathomimetic\n(cocaine, amphetamines, ephedrine)", S("tc",fontSize=8.5,fontName="Helvetica-Bold",textColor=C_ACCENT4,leading=12)),
Paragraph("β High", S("tv",fontSize=8.5,fontName="Helvetica",textColor=colors.HexColor("#A5D6A7"),leading=12)),
Paragraph("β High", S("tv",fontSize=8.5,fontName="Helvetica",textColor=colors.HexColor("#A5D6A7"),leading=12)),
Paragraph("Mydriasis", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("Wet\n(diaphoresis)", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("Agitation, hyperthermia,\nseizures possible", S("tv",fontSize=8,fontName="Helvetica",textColor=C_YELLOW,leading=11)),
],
[
Paragraph("π Opioid\n(heroin, fentanyl, morphine, clonidine)", S("tc",fontSize=8.5,fontName="Helvetica-Bold",textColor=C_ACCENT3,leading=12)),
Paragraph("β Low", S("tv",fontSize=8.5,fontName="Helvetica",textColor=colors.HexColor("#EF9A9A"),leading=12)),
Paragraph("β Low", S("tv",fontSize=8.5,fontName="Helvetica",textColor=colors.HexColor("#EF9A9A"),leading=12)),
Paragraph("Miosis\n(pinpoint)", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("Dry", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("CNS/resp. depression\nβ Naloxone reverses", S("tv",fontSize=8,fontName="Helvetica",textColor=C_YELLOW,leading=11)),
],
[
Paragraph("π΄ Sedative-Hypnotic\n(benzos, barbiturates, ethanol, GHB)", S("tc",fontSize=8.5,fontName="Helvetica-Bold",textColor=C_ACCENT5,leading=12)),
Paragraph("Normal", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_WHITE,leading=12)),
Paragraph("Mild β", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_WHITE,leading=12)),
Paragraph("Normal", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("Normal", S("tv",fontSize=8.5,fontName="Helvetica",textColor=C_LIGHT,leading=12)),
Paragraph("Ataxia, slurred speech,\nCNS depression β NO miosis", S("tv",fontSize=8,fontName="Helvetica",textColor=C_YELLOW,leading=11)),
],
]
tox_ts = TableStyle([
("BACKGROUND", (0,0),(-1,0), colors.HexColor("#1A2E45")),
("ROWBACKGROUNDS",(0,1),(-1,-1), [C_CARD1, colors.HexColor("#162235")]),
("GRID", (0,0),(-1,-1), 0.5, colors.HexColor("#2A4A65")),
("BOX", (0,0),(-1,-1), 1.5, C_ACCENT1),
("LEFTPADDING", (0,0),(-1,-1), 6),
("RIGHTPADDING", (0,0),(-1,-1), 6),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "TOP"),
# colour-code first column
("BACKGROUND", (0,1),( 0,1), colors.HexColor("#003840")),
("BACKGROUND", (0,2),( 0,2), colors.HexColor("#3B1A0A")),
("BACKGROUND", (0,3),( 0,3), colors.HexColor("#2A0A3B")),
("BACKGROUND", (0,4),( 0,4), colors.HexColor("#0A3B18")),
("BACKGROUND", (0,5),( 0,5), colors.HexColor("#3B2E0A")),
])
tox_table = Table(tox_data,
colWidths=[44*mm, 12*mm, 14*mm, 17*mm, 17*mm, 62*mm])
tox_table.setStyle(tox_ts)
story.append(tox_table)
story.append(Spacer(1, 5*mm))
# ββ CHOLINERGIC DETAIL CARDS ββββββββββββββββββββββββββββββββββββββββββββββ
story.append(Paragraph("βΈ CHOLINERGIC TOXIDROME β DETAIL", section_title))
dumbell_rows = [
("D", "Diarrhea, Diaphoresis"),
("U", "Urination"),
("M", "Miosis"),
("B", "Bradycardia, Bronchospasm, Bronchorrhea"),
("E", "Emesis"),
("L", "Lacrimation"),
("L", "Lethargy / altered consciousness"),
("S", "Salivation"),
]
weekday_rows = [
("Mon", "Mydriasis (nicotinic)"),
("Tue", "Tachycardia (nicotinic)"),
("Wed", "Weakness"),
("Thu", "Tremors / Fasciculations"),
("Fri", "Fasciculations / Seizures"),
]
def detail_card(title, mnemonic, rows, accent, lw=14*mm, dw=52*mm, width=72*mm):
data_rows = [[Paragraph(l, S("dl",fontSize=10,fontName="Helvetica-Bold",textColor=C_YELLOW,leading=13)),
Paragraph(d, S("dd",fontSize=8.5,fontName="Helvetica",textColor=C_WHITE,leading=12))]
for l,d in rows]
inner_ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("ROWBACKGROUNDS",(0,0),(-1,-1), [C_CARD1, colors.HexColor("#162235")]),
("LEFTPADDING", (0,0),(-1,-1), 4),
("RIGHTPADDING", (0,0),(-1,-1), 4),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LINEBEFORE", (0,0),(0,-1), 3, accent),
("VALIGN", (0,0),(-1,-1), "TOP"),
])
inner = Table(data_rows, colWidths=[lw, dw])
inner.setStyle(inner_ts)
outer_data = [
[Paragraph(f"<b>{title}</b>",
S("dh",fontSize=10,fontName="Helvetica-Bold",textColor=C_WHITE,leading=13))],
[Paragraph(mnemonic,
S("dm",fontSize=17,fontName="Helvetica-Bold",textColor=C_YELLOW,alignment=TA_CENTER,leading=21))],
[inner],
]
outer_ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("BACKGROUND", (0,0),(-1,0), accent),
("BOX", (0,0),(-1,-1), 1.5, accent),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("ALIGN", (0,1),(-1,1), "CENTER"),
("VALIGN", (0,0),(-1,-1), "TOP"),
])
outer = Table(outer_data, colWidths=[width])
outer.setStyle(outer_ts)
return outer
dumb_card = detail_card("π§ Muscarinic Effects", "DUMBELLS",
dumbell_rows, C_ACCENT1, 14*mm, 64*mm, 84*mm)
week_card = detail_card("β‘ Nicotinic Effects", "Days of the Week",
weekday_rows, colors.HexColor("#FFCA28"), 14*mm, 64*mm, 84*mm)
chol_row = Table([[dumb_card, week_card]], colWidths=[86*mm, 86*mm])
chol_row.setStyle(TableStyle([
("LEFTPADDING", (0,0),(-1,-1), 2),
("RIGHTPADDING", (0,0),(-1,-1), 2),
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
story.append(chol_row)
story.append(Spacer(1, 5*mm))
# ββ ANTICHOLINERGIC POEM ββββββββββββββββββββββββββββββββββββββββββββββββββ
story.append(header_banner("π΅ ANTICHOLINERGIC β Classic Poem", C_ACCENT2))
poem_lines = [
"π¦ <b>Blind as a bat</b> (mydriasis, blurred vision)",
"π© <b>Mad as a hatter</b> (agitation, delirium, hallucinations)",
"πΉ <b>Red as a beet</b> (flushed skin, peripheral vasodilation)",
"π₯ <b>Hot as Hades</b> (hyperthermia β anhidrosis = can't sweat)",
"π <b>Dry as a bone</b> (dry mouth, no secretions)",
"π <b>The bowel and bladder lose their tone, and the heart runs alone</b>",
" (urinary retention, constipation, tachycardia)",
]
alt_lines = [
"Can't See Β· Can't Pee Β· Can't Spit Β· Can't Shit",
]
poem_data = [[Paragraph(line, S("pl3",fontSize=9,fontName="Helvetica",textColor=C_LIGHT,leading=14))]
for line in poem_lines]
poem_data.append([Paragraph(alt_lines[0],
S("alt",fontSize=10,fontName="Helvetica-Bold",textColor=C_YELLOW,
alignment=TA_CENTER, leading=14))])
poem_ts = TableStyle([
("BACKGROUND", (0,0),(-1,-1), C_CARD1),
("BOX", (0,0),(-1,-1), 1.5, C_ACCENT2),
("LINEABOVE", (0,-1),(-1,-1), 1, C_ACCENT2),
("LEFTPADDING", (0,0),(-1,-1), 12),
("RIGHTPADDING", (0,0),(-1,-1), 12),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING",(0,0),(-1,-1), 4),
("BACKGROUND", (0,-1),(-1,-1), colors.HexColor("#3B1A0A")),
("ALIGN", (0,-1),(-1,-1), "CENTER"),
])
poem_table = Table(poem_data, colWidths=[168*mm])
poem_table.setStyle(poem_ts)
story.append(poem_table)
# ββ Build βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"PDF saved to: {OUTPUT}")