Hi
Can you make easily and step by step ECG approach on flash card form easily to understand and best explain
ls /home/daytona/workspace/ 2>/dev/null || echo "empty"
mkdir -p /home/daytona/workspace/ecg-flashcards && python3 -c "import reportlab; print('reportlab ok')"
~/ecg-flashcards/make_flashcards.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import Paragraph
from reportlab.lib.enums import TA_CENTER, TA_LEFT
import textwrap
W, H = A4 # 210 x 297 mm (595.27 x 841.89 pt)
# ── colour palette ──────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1A2C5B")
TEAL = colors.HexColor("#0D7377")
AMBER = colors.HexColor("#F5A623")
LIGHT = colors.HexColor("#EAF4FB")
WHITE = colors.white
RED = colors.HexColor("#D0021B")
GREEN = colors.HexColor("#2E7D32")
ORANGE = colors.HexColor("#E65100")
GREY = colors.HexColor("#455A64")
LGREEN = colors.HexColor("#E8F5E9")
LYELLOW = colors.HexColor("#FFFDE7")
LORANGE = colors.HexColor("#FFF3E0")
LRED = colors.HexColor("#FFEBEE")
# ── helper: rounded rectangle ───────────────────────────────────────────────
def rr(c, x, y, w, h, r=8, fill_color=WHITE, stroke_color=NAVY, lw=1.2):
c.setLineWidth(lw)
c.setFillColor(fill_color)
c.setStrokeColor(stroke_color)
c.roundRect(x, y, w, h, r, stroke=1, fill=1)
# ── helper: wrapped text block ───────────────────────────────────────────────
def text_block(c, txt, x, y, max_w, font="Helvetica", size=10, color=GREY,
line_h=14, align="left"):
c.setFont(font, size)
c.setFillColor(color)
words = txt.split()
lines, line = [], []
for w in words:
probe = " ".join(line + [w])
if c.stringWidth(probe, font, size) <= max_w:
line.append(w)
else:
if line:
lines.append(" ".join(line))
line = [w]
if line:
lines.append(" ".join(line))
cur_y = y
for ln in lines:
if align == "center":
c.drawCentredString(x, cur_y, ln)
else:
c.drawString(x, cur_y, ln)
cur_y -= line_h
return cur_y # returns y after last line
# ── helper: bullet list ──────────────────────────────────────────────────────
def bullet_list(c, items, x, y, max_w, font="Helvetica", size=10,
color=GREY, line_h=14, bullet="•", indent=12):
cur_y = y
for item in items:
c.setFont("Helvetica-Bold", size)
c.setFillColor(TEAL)
c.drawString(x, cur_y, bullet)
c.setFont(font, size)
c.setFillColor(color)
cur_y = text_block(c, item, x + indent, cur_y, max_w - indent,
font=font, size=size, color=color,
line_h=line_h) + line_h - line_h # advance
cur_y -= 2
return cur_y
# ── flashcard data ───────────────────────────────────────────────────────────
cards = [
# ═══════════════════════════════ OVERVIEW ═══════════════════════════════════
{
"step": "OVERVIEW",
"icon": "★",
"title": "The 8-Step ECG Approach",
"accent": NAVY,
"bg": LIGHT,
"type": "overview",
"steps": [
("1", "Rate", "How fast is the heart?", TEAL),
("2", "Rhythm", "Regular or irregular?", GREEN),
("3", "P Waves", "Present? Morphology?", ORANGE),
("4", "PR Interval", "AV conduction time", AMBER),
("5", "QRS Complex", "Width & morphology", RED),
("6", "Axis", "Direction of depolarisation", TEAL),
("7", "ST Segment", "Elevation / depression?", RED),
("8", "T & U Waves", "Repolarisation changes", GREEN),
],
"source": "Harrison's 22E, p.1919 | Kaplan & Sadock's p.6823"
},
# ═══════════════════════════════ STEP 1 - RATE ══════════════════════════════
{
"step": "STEP 1",
"icon": "❤",
"title": "Heart Rate",
"accent": TEAL,
"bg": colors.HexColor("#E0F7FA"),
"type": "detail",
"question": "How do I calculate heart rate?",
"key_fact": "Normal HR = 60–100 bpm",
"methods": [
("300 ÷ Rule (regular rhythm)",
"Count large boxes between two consecutive R waves → Divide 300 by that number.\n"
"Example: 4 large boxes apart → 300÷4 = 75 bpm"),
("6-Second Rule (irregular rhythm)",
"Count all QRS complexes in a 10-second strip → Multiply by 6.\n"
"Best for AF or irregular rhythms."),
("Memorise the sequence",
"1 box=300, 2=150, 3=100, 4=75, 5=60, 6=50 bpm"),
],
"normal_ranges": [
"Bradycardia: < 60 bpm",
"Normal: 60 – 100 bpm",
"Tachycardia: > 100 bpm",
],
"tip": "Each small box = 0.04 s | Each large box = 0.2 s | 5 small = 1 large",
"source": "Kaplan & Sadock's p.6824"
},
# ══════════════════════════════ STEP 2 - RHYTHM ═════════════════════════════
{
"step": "STEP 2",
"icon": "~",
"title": "Rhythm",
"accent": GREEN,
"bg": LGREEN,
"type": "detail",
"question": "Is the rhythm regular or irregular?",
"key_fact": "Normal: Regular Sinus Rhythm (RSR)",
"methods": [
("Check R-R intervals",
"Measure distance between consecutive R peaks. Regular = all equal."),
("Identify the pacemaker",
"Is there a P wave before every QRS? → Sinus origin.\n"
"No P waves + irregular → Atrial Fibrillation.\n"
"Saw-tooth baseline → Atrial Flutter."),
("Regularly irregular vs Irregularly irregular",
"Regularly irregular: pattern repeats (e.g. Wenckebach).\n"
"Irregularly irregular: no pattern (e.g. AF)."),
],
"normal_ranges": [
"Sinus Rhythm: P before every QRS, rate 60-100",
"Sinus Brady: < 60 bpm, normal P & QRS",
"Sinus Tachy: > 100 bpm, normal P & QRS",
],
"tip": "Use calipers or fold paper to compare R-R intervals quickly!",
"source": "Kaplan & Sadock's p.6824"
},
# ═══════════════════════════════ STEP 3 - P WAVES ═══════════════════════════
{
"step": "STEP 3",
"icon": "P",
"title": "P Waves",
"accent": ORANGE,
"bg": LORANGE,
"type": "detail",
"question": "Are P waves present, and what do they look like?",
"key_fact": "P wave = Atrial Depolarisation",
"methods": [
("Normal P wave",
"Upright in I, II, aVF. Inverted in aVR.\n"
"Duration < 0.12 s (3 small boxes). Amplitude < 2.5 mm."),
("P before every QRS?",
"Yes → rhythm is atrial in origin.\n"
"No P waves → AF, junctional, or ventricular rhythm.\n"
"More P than QRS → 2nd or 3rd degree AV block."),
("P wave morphology clues",
"Broad, notched P (P mitrale) → Left atrial enlargement.\n"
"Tall, peaked P (P pulmonale) → Right atrial enlargement."),
],
"normal_ranges": [
"Duration: < 0.12 s (< 3 small boxes)",
"Amplitude: < 2.5 mm in limb leads",
"Axis: upright in I & II",
],
"tip": "Lead II is the best lead for seeing P waves clearly.",
"source": "Harrison's 22E, p.1919"
},
# ═══════════════════════════════ STEP 4 - PR INTERVAL ═══════════════════════
{
"step": "STEP 4",
"icon": "PR",
"title": "PR Interval",
"accent": AMBER,
"bg": LYELLOW,
"type": "detail",
"question": "How long does it take the impulse to travel from atria to ventricles?",
"key_fact": "PR = AV Nodal Conduction Time",
"methods": [
("Where to measure",
"From start of P wave to start of QRS complex."),
("Normal PR interval",
"0.12 – 0.20 s (3 – 5 small boxes)."),
("Abnormal PR",
"Long PR (> 0.20 s) → 1st degree AV block.\n"
"Progressively lengthening PR → 2nd degree (Wenckebach/Mobitz I).\n"
"Fixed long PR with dropped beats → Mobitz II.\n"
"No PR relationship → 3rd degree (complete) AV block.\n"
"Short PR (< 0.12 s) → Pre-excitation (WPW syndrome)."),
],
"normal_ranges": [
"Normal: 0.12–0.20 s (3–5 small boxes)",
"> 0.20 s = 1st Degree AV Block",
"< 0.12 s = Pre-excitation / WPW",
],
"tip": "Think of the PR as the time the AV node 'pauses' to let atria empty.",
"source": "Harrison's 22E, p.1919"
},
# ═══════════════════════════════ STEP 5 - QRS ═══════════════════════════════
{
"step": "STEP 5",
"icon": "QRS",
"title": "QRS Complex",
"accent": RED,
"bg": LRED,
"type": "detail",
"question": "How wide is the QRS, and what does it look like?",
"key_fact": "QRS = Ventricular Depolarisation",
"methods": [
("Normal QRS",
"Duration < 0.12 s (< 3 small boxes). Narrow and sharp."),
("Wide QRS (≥ 0.12 s)",
"RBBB: RSR' pattern in V1 ('M shape'), wide S in I & V6.\n"
"LBBB: broad notched R in I, V5, V6; QS in V1.\n"
"Ventricular rhythm / PVC: bizarre wide complex."),
("Pathological Q waves",
"Width > 0.04 s OR depth > 1/4 of R wave height.\n"
"Indicates previous myocardial infarction."),
("R wave progression",
"R wave should grow V1→V6. Loss of progression = anterior MI."),
],
"normal_ranges": [
"Duration: < 0.12 s (< 3 small boxes)",
"Q wave: < 0.04 s wide, < 25% of R height",
"Voltage: R in V5/V6 < 25 mm",
],
"tip": "Narrow QRS = supraventricular origin. Wide QRS = ventricular or aberrant conduction.",
"source": "Harrison's 22E, p.1919 | Kaplan & Sadock's p.6825"
},
# ═══════════════════════════════ STEP 6 - AXIS ══════════════════════════════
{
"step": "STEP 6",
"icon": "↗",
"title": "Electrical Axis",
"accent": TEAL,
"bg": colors.HexColor("#E0F7FA"),
"type": "detail",
"question": "What direction is the heart's net electrical force?",
"key_fact": "Normal Axis: -30° to +90°",
"methods": [
("Quick 2-lead method (Lead I + aVF)",
"Both positive → Normal axis.\n"
"Lead I positive, aVF negative → Left Axis Deviation (LAD).\n"
"Lead I negative, aVF positive → Right Axis Deviation (RAD).\n"
"Both negative → Extreme/Northwest axis."),
("Left Axis Deviation (< -30°)",
"Left anterior fascicular block, inferior MI, LVH, LBBB."),
("Right Axis Deviation (> +90°)",
"RVH, pulmonary HTN / PE, lateral MI, RBBB, dextrocardia."),
],
"normal_ranges": [
"Normal: -30° to +90°",
"LAD: < -30°",
"RAD: > +90°",
"Extreme: < -90° (both leads negative)",
],
"tip": "Mnemonic: 'Leave (LAD) the Right (RAD) behind' — Lead I tells you.",
"source": "Kaplan & Sadock's p.6825 | Harrison's 22E p.1919"
},
# ══════════════════════════════ STEP 7 - ST SEGMENT ═════════════════════════
{
"step": "STEP 7",
"icon": "ST",
"title": "ST Segment",
"accent": RED,
"bg": LRED,
"type": "detail",
"question": "Is the ST segment elevated or depressed?",
"key_fact": "ST = Ventricular Repolarisation — most clinically critical!",
"methods": [
("ST Elevation (≥ 1 mm in ≥ 2 contiguous leads)",
"STEMI: localised elevation (see territory table).\n"
"Pericarditis: diffuse saddle-shaped elevation + PR depression.\n"
"Early Repolarisation: concave elevation, benign in young.\n"
"Also: LBBB, Brugada, stress cardiomyopathy, LV aneurysm."),
("ST Depression",
"Subendocardial ischaemia / NSTEMI.\n"
"Reciprocal change opposite a STEMI.\n"
"Digoxin effect: 'reverse tick / Salvador Dali' pattern.\n"
"LVH strain pattern."),
("MI Territory Guide",
"II, III, aVF → Inferior (RCA).\n"
"I, aVL, V1-V4 → Anterior/Lateral (LAD).\n"
"V1-V3 → Septal. V4-V6, I, aVL → Lateral (LCx)."),
],
"normal_ranges": [
"Isoelectric (flat at baseline)",
"Elevation: ≥ 1 mm (limb) / ≥ 2 mm (precordial) = abnormal",
"Depression: ≥ 0.5–1 mm = abnormal",
],
"tip": "Always compare with old ECG. ST changes + clinical context = diagnosis!",
"source": "Kaplan & Sadock's p.6825 | Harrison's 22E p.1919"
},
# ══════════════════════════════ STEP 8 - T & U WAVES ════════════════════════
{
"step": "STEP 8",
"icon": "TU",
"title": "T Waves & U Waves",
"accent": GREEN,
"bg": LGREEN,
"type": "detail",
"question": "Are the T waves normal? Any U waves?",
"key_fact": "T wave = Ventricular Repolarisation (active, ATP-dependent)",
"methods": [
("Normal T wave",
"Upright in I, II, V3-V6. Inverted in aVR normally.\n"
"Height < 5 mm (limb) or < 10 mm (precordial)."),
("T wave inversions",
"Ischaemia / NSTEMI: deep symmetric inversions.\n"
"RVH / PE: V1-V4 inversions ('right heart strain').\n"
"RBBB/LBBB: secondary T changes (expected).\n"
"Normal: V1, aVR, III in some people."),
("Peaked T waves",
"Tall, symmetrical, narrow → Hyperkalaemia (early sign).\n"
"Hyperacute T waves → Very early STEMI."),
("U waves",
"Small deflection after T wave, same direction as T.\n"
"Prominent U waves → Hypokalaemia, bradycardia, drugs.\n"
"Inverted U waves → LVH, ischaemia."),
],
"normal_ranges": [
"T: upright I, II, V3-V6; inverted aVR",
"T amplitude: < 5 mm limb / < 10 mm precordial",
"QTc (Bazett): < 440 ms men / < 460 ms women",
],
"tip": "A prolonged QT (after T wave) = risk of Torsades de Pointes (TdP)!",
"source": "Harrison's 22E, p.1919 | Kaplan & Sadock's p.6825"
},
# ═══════════════════════════ BONUS: INTERVALS QUICK REF ═════════════════════
{
"step": "BONUS",
"icon": "⌛",
"title": "Normal ECG Intervals — Quick Reference",
"accent": NAVY,
"bg": LIGHT,
"type": "table",
"rows": [
("Interval / Wave", "Normal Value", "Abnormal → Think..."),
("P wave duration", "< 0.12 s (< 3 boxes)","Broad = LAE / LBBB"),
("P amplitude", "< 2.5 mm", "Tall = RAE (P pulmonale)"),
("PR interval", "0.12–0.20 s", "Long=AV block; Short=WPW"),
("QRS duration", "< 0.12 s (< 3 boxes)","Wide = BBB / VT / WPW"),
("QT interval", "0.35–0.45 s", "Long = TdP risk"),
("QTc (Bazett)", "< 440 ms (M) / < 460 ms (F)","Drug-induced/Electrolytes"),
("ST segment", "Isoelectric", "Elevation/Depression = Ischaemia"),
("T wave height", "< 5 mm (limb)", "Peaked = HyperK; Flat = HypoK"),
],
"source": "Harrison's 22E, p.1919"
},
# ═══════════════════════════ BONUS: COMMON PATTERNS ═════════════════════════
{
"step": "CHEAT SHEET",
"icon": "⚡",
"title": "Common ECG Patterns at a Glance",
"accent": NAVY,
"bg": LIGHT,
"type": "patterns",
"patterns": [
("Atrial Fibrillation", "No P waves, irregularly irregular RR, fibrillatory baseline", TEAL),
("Atrial Flutter", "Saw-tooth flutter waves at ~300/min, regular QRS 150/min", TEAL),
("1st Degree AV Block", "PR > 0.20 s, every P conducts, regular rhythm", ORANGE),
("2nd Degree (Wenckebach)","PR progressively lengthens until a QRS is dropped", ORANGE),
("3rd Degree AV Block", "Complete P-QRS dissociation, bradycardia, wide QRS", RED),
("RBBB", "RSR' in V1 ('M'), wide S in I & V6, QRS ≥ 0.12 s", AMBER),
("LBBB", "Broad R in I/V6, QS in V1, no septal Q in I/V6", AMBER),
("STEMI (Inferior)", "STE ≥ 1 mm in II, III, aVF — reciprocal in I, aVL", RED),
("STEMI (Anterior)", "STE in V1-V4, loss of R progression", RED),
("Hyperkalaemia", "Peaked T → wide P → wide QRS → sine wave", GREEN),
("Hypokalaemia", "Flattened T, prominent U waves, ST depression", GREEN),
("PE (Right heart strain)","S1Q3T3: S in I, Q in III, T inversion in III + V1-V4", TEAL),
("WPW", "Short PR, delta wave, wide QRS — risk of SVT", AMBER),
("Long QT", "QTc > 440 ms (M) or > 460 ms (F) — risk of TdP", RED),
],
"source": "Harrison's 22E | Kaplan & Sadock's | Braunwald's"
},
]
# ── DRAWING FUNCTIONS ────────────────────────────────────────────────────────
def draw_header(c, card):
accent = card["accent"]
# top bar
c.setFillColor(accent)
c.rect(0, H - 60, W, 60, fill=1, stroke=0)
# step badge
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 11)
c.drawString(18, H - 22, card["step"])
# icon circle
c.setFillColor(WHITE)
c.circle(W - 38, H - 30, 22, fill=1, stroke=0)
c.setFillColor(accent)
c.setFont("Helvetica-Bold", 18)
c.drawCentredString(W - 38, H - 36, card["icon"])
# title
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 22)
c.drawString(18, H - 48, card["title"])
def draw_footer(c, card):
c.setFillColor(card["accent"])
c.rect(0, 0, W, 28, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Oblique", 8)
c.drawString(12, 10, "Source: " + card["source"])
c.setFont("Helvetica", 8)
c.drawRightString(W - 12, 10, "ECG Flashcards for Medical Students")
def draw_overview(c, card):
c.setFillColor(card["bg"])
c.rect(0, 28, W, H - 88, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 14)
c.setFillColor(NAVY)
c.drawCentredString(W / 2, H - 80, "Follow these 8 steps for EVERY ECG you read")
y = H - 110
col_w = (W - 40) / 2
for i, (num, name, desc, col) in enumerate(card["steps"]):
col_x = 20 + (i % 2) * (col_w + 20)
if i % 2 == 0 and i > 0:
y -= 64
elif i == 0:
pass
# card box
rr(c, col_x, y - 52, col_w, 54, r=6,
fill_color=colors.HexColor("#FFFFFF"), stroke_color=col, lw=2)
# number circle
c.setFillColor(col)
c.circle(col_x + 24, y - 24, 14, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 14)
c.drawCentredString(col_x + 24, y - 29, num)
# step name
c.setFillColor(col)
c.setFont("Helvetica-Bold", 12)
c.drawString(col_x + 44, y - 20, name)
# description
c.setFillColor(GREY)
c.setFont("Helvetica", 9)
c.drawString(col_x + 44, y - 35, desc)
def draw_detail(c, card):
c.setFillColor(card["bg"])
c.rect(0, 28, W, H - 88, fill=1, stroke=0)
y = H - 78
# question bubble
rr(c, 12, y - 30, W - 24, 32, r=6,
fill_color=card["accent"], stroke_color=card["accent"])
c.setFillColor(WHITE)
c.setFont("Helvetica-BoldOblique", 11)
c.drawString(20, y - 18, card["question"])
y -= 42
# key fact
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 11)
c.drawString(20, y, "Key fact: ")
c.setFillColor(card["accent"])
c.setFont("Helvetica-Bold", 11)
c.drawString(80, y, card["key_fact"])
y -= 20
# methods
for method_title, method_body in card["methods"]:
rr(c, 12, y - 52, W - 24, 55, r=5,
fill_color=WHITE, stroke_color=card["accent"], lw=1)
c.setFillColor(card["accent"])
c.setFont("Helvetica-Bold", 10)
c.drawString(20, y - 14, method_title)
c.setFillColor(GREY)
c.setFont("Helvetica", 9.5)
lines = method_body.split("\n")
ly = y - 28
for ln in lines:
c.drawString(24, ly, ln)
ly -= 12
y -= 64
# normal ranges box
rr(c, 12, y - 58, W / 2 - 16, 60, r=5,
fill_color=LGREEN, stroke_color=GREEN, lw=1)
c.setFillColor(GREEN)
c.setFont("Helvetica-Bold", 10)
c.drawString(20, y - 14, "Normal Ranges")
c.setFillColor(GREY)
c.setFont("Helvetica", 9)
ny = y - 27
for r_ in card["normal_ranges"]:
c.drawString(24, ny, r_)
ny -= 12
# tip box
tx = W / 2 + 4
rr(c, tx, y - 58, W / 2 - 16, 60, r=5,
fill_color=LYELLOW, stroke_color=AMBER, lw=1)
c.setFillColor(AMBER)
c.setFont("Helvetica-Bold", 10)
c.drawString(tx + 8, y - 14, "Tip")
c.setFillColor(GREY)
c.setFont("Helvetica", 9)
text_block(c, card["tip"], tx + 8, y - 28, W / 2 - 32,
size=9, line_h=12)
def draw_table(c, card):
c.setFillColor(card["bg"])
c.rect(0, 28, W, H - 88, fill=1, stroke=0)
y = H - 78
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 13)
c.drawCentredString(W / 2, y, "Normal ECG Values — Memorise These!")
y -= 22
col_widths = [150, 130, 200]
col_x = [18, 168, 298]
row_h = 26
for i, row in enumerate(card["rows"]):
if i == 0:
c.setFillColor(NAVY)
elif i % 2 == 0:
c.setFillColor(colors.HexColor("#F0F4FF"))
else:
c.setFillColor(WHITE)
c.rect(col_x[0], y - row_h + 4, W - 36, row_h, fill=1, stroke=0)
for j, (cx, cw, cell) in enumerate(zip(col_x, col_widths, row)):
if i == 0:
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 9.5)
else:
c.setFillColor(GREY if j != 2 else RED)
c.setFont("Helvetica" if j != 0 else "Helvetica-Bold", 9)
c.drawString(cx + 4, y - row_h + 12, cell)
y -= row_h
def draw_patterns(c, card):
c.setFillColor(card["bg"])
c.rect(0, 28, W, H - 88, fill=1, stroke=0)
y = H - 78
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 13)
c.drawCentredString(W / 2, y, "Learn to recognise these common patterns")
y -= 18
col_w = (W - 36) / 2
for i, (name, desc, col) in enumerate(card["patterns"]):
cx = 12 + (i % 2) * (col_w + 12)
if i % 2 == 0 and i > 0:
y -= 34
rr(c, cx, y - 28, col_w, 30, r=4,
fill_color=WHITE, stroke_color=col, lw=1.5)
c.setFillColor(col)
c.setFont("Helvetica-Bold", 9)
c.drawString(cx + 6, y - 11, name)
c.setFillColor(GREY)
c.setFont("Helvetica", 8)
c.drawString(cx + 6, y - 22, desc[:70] + ("..." if len(desc) > 70 else ""))
# ── MAIN ─────────────────────────────────────────────────────────────────────
out_path = "/home/daytona/workspace/ecg-flashcards/ECG_Flashcards.pdf"
c = canvas.Canvas(out_path, pagesize=A4)
for card in cards:
draw_header(c, card)
draw_footer(c, card)
if card["type"] == "overview":
draw_overview(c, card)
elif card["type"] == "detail":
draw_detail(c, card)
elif card["type"] == "table":
draw_table(c, card)
elif card["type"] == "patterns":
draw_patterns(c, card)
c.showPage()
c.save()
print("Done:", out_path)
cd /home/daytona/workspace/ecg-flashcards && python3 make_flashcards.py 2>&1
ls -lh /home/daytona/workspace/ecg-flashcards/ECG_Flashcards.pdf
ECG Flashcards
PDF Document · PDF
| Card | Topic | Key content |
|---|---|---|
| Overview | The 8-Step Approach | Road map — all 8 steps at a glance |
| Step 1 | Heart Rate | 300÷rule, 6-second rule, normal/Brady/Tachy |
| Step 2 | Rhythm | R-R intervals, P before QRS, AF vs Flutter |
| Step 3 | P Waves | Morphology, duration, atrial enlargement |
| Step 4 | PR Interval | AV blocks (1st, 2nd, 3rd), WPW |
| Step 5 | QRS Complex | RBBB, LBBB, Q waves, R-wave progression |
| Step 6 | Electrical Axis | Lead I + aVF trick, LAD vs RAD causes |
| Step 7 | ST Segment | STEMI territories, pericarditis, depression |
| Step 8 | T & U Waves | Inversions, peaked T, QTc, U waves |
| Bonus | Normal Intervals Table | All normal values in one quick table |
| Cheat Sheet | 14 Common Patterns | AF, AV blocks, BBB, STEMI, HyperK, PE, WPW… |