Ecg puri padhni batao

Reading File
Finding Sources
Finding Sources
Searching the Web

ECG reading systematic approach Hindi explained

Reading File
Reading File
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Loading Image
Loading Image
Loading Image
Loading Image
Bahut achha content mil gaya - Harrison's, Ganong's, aur Braunwald's se ECG ka complete material hai. Ab main ek comprehensive ECG guide Hindi mein likhta hoon:

⚡ ECG Puri Padhna - Systematic Guide

(Source: Harrison's Principles of Internal Medicine 22E, Ganong's Medical Physiology, Braunwald's Heart Disease)

ECG Kya Hai?

ECG (Electrocardiogram) dil ki electrical activity ko body surface par electrodes se record karta hai. Standard 12-lead ECG mein:
  • 6 Limb leads: I, II, III, aVR, aVL, aVF (frontal plane)
  • 6 Chest (Precordial) leads: V1, V2, V3, V4, V5, V6 (horizontal plane)
Yeh diagram dekho - har lead body ke alag angle se dil ko "dekhti" hai:
Normal ECG - 12 leads ka placement

ECG Paper Samjho - Sabse Pehle

ECG paper par:
  • Horizontal axis = Time (1 small box = 0.04 sec, 1 large box = 0.2 sec)
  • Vertical axis = Voltage/Amplitude (1 small box = 0.1 mV, 1 large box = 0.5 mV)
  • Standard speed = 25 mm/sec
  • Standard calibration = 10 mm = 1 mV

SYSTEMATIC APPROACH - 14 Steps (Harrison's)

Harrison's ke mutabik har ECG mein yeh 14 cheezein systematic taur par check karo:
  1. Standardization & Technical features
  2. Rhythm
  3. Heart Rate
  4. PR Interval / AV Conduction
  5. QRS Interval
  6. QT/QTc Interval
  7. Mean QRS Electrical Axis
  8. P Waves
  9. QRS Voltages
  10. Precordial R-wave Progression
  11. Abnormal Q Waves
  12. ST Segments
  13. T Waves
  14. U Waves

STEP-BY-STEP BREAKDOWN

1. Standardization Check (Calibration)

  • Har ECG ke shuru mein ek calibration pulse honi chahiye: 10 mm tall, 0.2 sec wide
  • Agar yeh missing hai ya galat hai → ECG unreliable hai

2. RHYTHM

Sinus Rhythm ki Pehchan:
  • Har QRS se pehle ek P wave honi chahiye
  • P wave Lead II mein positive (upright), aVR mein negative honi chahiye
  • P-P interval regular hona chahiye
  • HR: 60-100 bpm
RhythmFeature
Normal SinusP wave → QRS, regular, 60-100 bpm
Sinus BradyWohi par rate <60
Sinus TachyWohi par rate >100
AFP waves absent, irregular irregular
Atrial FlutterSawtooth P waves, 300/min

3. HEART RATE Calculate Karo

Method 1 - Regular Rhythm:
  • 300 ÷ number of large boxes between two R waves
  • Example: 2 large boxes = 300/2 = 150 bpm
  • Shortcut: 300 → 150 → 100 → 75 → 60 → 50 (yaad karo!)
Method 2 - Irregular Rhythm:
  • Count karein kitne QRS complexes hain 10-second strip mein
  • Multiply by 6 = HR per minute

4. P WAVE

Normal P wave:
  • Duration: <120 ms (3 small boxes)
  • Amplitude: <2.5 mm (2.5 small boxes)
  • Lead II mein positive (upright)
  • aVR mein negative
  • V1 mein biphasic (positive + small negative component) ho sakta hai
Abnormalities:
  • P tall (>2.5mm) aur pointed → Right Atrial Enlargement (P-pulmonale)
  • P broad (>120ms), notched → Left Atrial Enlargement (P-mitrale)

5. PR INTERVAL

  • Measure: P wave ke shuru se QRS ke shuru tak
  • Normal: 120-200 ms (3-5 small boxes)
PRMatlab
<120 msPre-excitation (WPW) ya junctional rhythm
>200 ms1st Degree AV Block
Progressive lengthening2nd Degree AV Block (Wenckebach/Mobitz I)
Fixed PR + dropped QRS2nd Degree AV Block (Mobitz II)
No relation P to QRSComplete (3rd Degree) AV Block

6. QRS COMPLEX

Normal QRS:
  • Duration: <120 ms (3 small boxes)
  • V1 mein: Small r + large S (rS pattern)
  • V6 mein: Small q + large R + small s (qRs pattern)
R-wave Progression:
  • V1 se V6 tak R wave progressively barhti hai
  • Transition zone (jahan R=S): usually V3 ya V4
  • Agar V1-V4 mein R nahi badh raha → Poor R-wave progression (anterior MI ka sign)
QRS Width:
  • 120 ms → Bundle Branch Block (BBB)
    • RBBB: V1 mein RSR' (rabbit ears), V6 mein wide S wave
    • LBBB: V6 mein broad R, V1 mein QS/rS pattern
Abnormal Q waves:
  • 40 ms wide (1 small box) ya >25% of QRS height → Pathological Q wave (old MI ka sign)

7. QRS AXIS (Electrical Axis)

Einthoven triangle ke principle par based:
Einthoven Triangle aur Cardiac Axis
Normal Axis: -30° to +100°
Quick Method - Lead I + Lead aVF dekho:
Lead IaVFAxis
PositivePositiveNormal (-30° to +90°)
PositiveNegativeLeft Axis Deviation (LAD)
NegativePositiveRight Axis Deviation (RAD)
NegativeNegativeExtreme / Northwest Axis
Causes:
  • LAD (<-30°): LVH, Left anterior fascicular block (LAFB), Inferior MI
  • RAD (>+100°): RVH, Left posterior fascicular block, Lateral MI, WPW, Dextrocardia

8. ST SEGMENT

  • Baseline (isoelectric) pe honi chahiye - J point ke baad se T wave tak
  • Normal: Isoelectric ± 0.5 mm
ST Elevation (STEMI signs):
  • ≥1 mm in limb leads, ≥2 mm in precordial leads
  • Convex (dome shape) - suggests acute infarction
  • Concave (saddle shape) - suggests pericarditis
ST Depression:
  • Subendocardial ischemia
  • Digoxin effect (scooping)
  • Reciprocal changes in STEMI

9. T WAVE

Normal T wave:
  • Same direction as QRS (concordant)
  • Slightly asymmetric - slow rise, fast fall
  • V1 mein negative ya flat
  • aVR mein negative
Abnormalities:
  • Tall peaked T waves → Hyperkalemia, hyperacute STEMI
  • Inverted T waves → Ischemia, RVH (V1-V3), LVH (V5-V6)
  • Wellens' pattern (deep T inversion V1-V4) → Critical LAD stenosis - Emergency!

10. QT INTERVAL

  • QRS ke shuru se T wave ke end tak
  • Normal QTc (corrected): ≤440 ms men, ≤460 ms women
  • Bazett's formula: QTc = QT / √(RR interval in seconds)
  • Prolonged QT → Torsades de Pointes ka risk (hypokalemia, drugs, congenital)
  • Short QT → Hypercalcemia, digoxin toxicity

11. U WAVE

  • T wave ke baad ek chhoti si wave
  • Normally <1 mm, same direction as T wave
  • Prominent U wave → Hypokalemia (most common cause), amiodarone, sotalol

NORMAL INTERVALS - Quick Reference Table

ParameterNormal Value
Heart Rate60-100 bpm
PR Interval120-200 ms (3-5 small boxes)
QRS Duration<120 ms (<3 small boxes)
QT Interval (QTc)<440 ms (men), <460 ms (women)
QRS Axis-30° to +100°
P wave amplitude<2.5 mm
P wave duration<120 ms

LEADS KA LOCATION - Kaunsa Lead Kya Dekh Raha Hai

LeadsDil Ka Hissa
II, III, aVFInferior wall (RCA territory)
I, aVL, V5-V6Lateral wall (LCx territory)
V1-V4Anterior wall (LAD territory)
V1-V2Septal
aVRRight atrium / aortic root
V4RRight ventricle (special lead)

ECG PADHNE KA MEMORY TRICK

"Rate Rhythm Axis P-wave PR-QRS-ST-T-U"
Ya phir: "Real Radiologists Are Pretty Poor At Studying, Till U nag"
  • Rate
  • Rhythm
  • Axis
  • P wave
  • PR interval
  • QRS
  • ST segment
  • T wave
  • U wave

COMMON PATTERNS - Quick Diagnosis

PatternKya Socho
Diffuse ST elevation + PR depressionAcute Pericarditis
STEMI + reciprocal changesAcute MI
Delta wave + short PRWPW Syndrome
Tall R in V1 + RADRVH / RBBB / Posterior MI
Peaked P in II + RADCor Pulmonale
Electrical alternansCardiac Tamponade
Sine wave patternSevere Hyperkalemia
Osborne (J) waveHypothermia
"S1Q3T3"Pulmonary Embolism

Sources: Harrison's Principles of Internal Medicine 22E (2025) - Ch. 247 | Ganong's Review of Medical Physiology 26E | Braunwald's Heart Disease
ECG ek skill hai - jitni practice karoge utna better hoga. ECG Wave-Maven (Harvard) par free practice cases milte hain.

Create a quick reference PDF for ECG normal values and diagnostic criteria

Reading File
Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace not found"

Running Command

mkdir -p /home/daytona/workspace/ecg-pdf

Writing File

~/ecg-pdf/create_ecg_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.pdfgen import canvas
from reportlab.graphics.shapes import Drawing, Rect, Line, String, Circle
from reportlab.graphics import renderPDF
import os

OUTPUT_PATH = "/home/daytona/workspace/ecg-pdf/ECG_Quick_Reference.pdf"

# ── Color Palette ──────────────────────────────────────────────────────────────
C_RED       = colors.HexColor("#C0392B")
C_DARK      = colors.HexColor("#1A1A2E")
C_BLUE      = colors.HexColor("#1565C0")
C_TEAL      = colors.HexColor("#00695C")
C_ORANGE    = colors.HexColor("#E65100")
C_PURPLE    = colors.HexColor("#6A1B9A")
C_BG_LIGHT  = colors.HexColor("#F5F5F5")
C_BG_RED    = colors.HexColor("#FFEBEE")
C_BG_BLUE   = colors.HexColor("#E3F2FD")
C_BG_TEAL   = colors.HexColor("#E0F2F1")
C_BG_ORANGE = colors.HexColor("#FFF3E0")
C_BG_PURPLE = colors.HexColor("#F3E5F5")
C_BG_GREEN  = colors.HexColor("#E8F5E9")
C_HEADER_BG = colors.HexColor("#B71C1C")
C_WHITE     = colors.white
C_GRAY      = colors.HexColor("#757575")
C_LGRAY     = colors.HexColor("#EEEEEE")
C_YELLOW    = colors.HexColor("#FFF176")

# ── Page Layout ────────────────────────────────────────────────────────────────
PAGE_W, PAGE_H = A4
MARGIN = 14 * mm

# ── Styles ─────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def S(name, **kw):
    return ParagraphStyle(name, **kw)

TITLE_STYLE = S("MainTitle",
    fontName="Helvetica-Bold", fontSize=22, textColor=C_WHITE,
    alignment=TA_CENTER, spaceAfter=0, leading=26)

SUBTITLE_STYLE = S("SubTitle",
    fontName="Helvetica", fontSize=11, textColor=colors.HexColor("#FFCDD2"),
    alignment=TA_CENTER, spaceAfter=0, leading=14)

SEC_HEADER = S("SectionHeader",
    fontName="Helvetica-Bold", fontSize=9.5, textColor=C_WHITE,
    alignment=TA_LEFT, leading=12, leftIndent=3)

CELL_BOLD = S("CellBold",
    fontName="Helvetica-Bold", fontSize=8.2, textColor=C_DARK,
    alignment=TA_LEFT, leading=10)

CELL_NORMAL = S("CellNormal",
    fontName="Helvetica", fontSize=8.0, textColor=C_DARK,
    alignment=TA_LEFT, leading=10)

CELL_GREEN = S("CellGreen",
    fontName="Helvetica-Bold", fontSize=8.2, textColor=C_TEAL,
    alignment=TA_LEFT, leading=10)

CELL_RED = S("CellRed",
    fontName="Helvetica-Bold", fontSize=8.2, textColor=C_RED,
    alignment=TA_LEFT, leading=10)

CELL_CENTER = S("CellCenter",
    fontName="Helvetica", fontSize=8.0, textColor=C_DARK,
    alignment=TA_CENTER, leading=10)

CELL_BOLD_CENTER = S("CellBoldCenter",
    fontName="Helvetica-Bold", fontSize=8.2, textColor=C_DARK,
    alignment=TA_CENTER, leading=10)

TINY = S("Tiny",
    fontName="Helvetica", fontSize=7.0, textColor=C_GRAY,
    alignment=TA_CENTER, leading=9)

FOOTER_STYLE = S("Footer",
    fontName="Helvetica-Oblique", fontSize=7.0, textColor=C_GRAY,
    alignment=TA_CENTER, leading=9)

# ── Table style helper ─────────────────────────────────────────────────────────
def base_ts(header_bg=C_BLUE, header_fg=C_WHITE):
    return TableStyle([
        ('BACKGROUND',   (0, 0), (-1, 0), header_bg),
        ('TEXTCOLOR',    (0, 0), (-1, 0), header_fg),
        ('FONTNAME',     (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE',     (0, 0), (-1, 0), 8.5),
        ('ALIGN',        (0, 0), (-1, -1), 'LEFT'),
        ('VALIGN',       (0, 0), (-1, -1), 'MIDDLE'),
        ('ROWBACKGROUNDS', (0, 1), (-1, -1), [C_WHITE, C_BG_LIGHT]),
        ('GRID',         (0, 0), (-1, -1), 0.4, colors.HexColor("#BDBDBD")),
        ('TOPPADDING',   (0, 0), (-1, -1), 3),
        ('BOTTOMPADDING',(0, 0), (-1, -1), 3),
        ('LEFTPADDING',  (0, 0), (-1, -1), 5),
        ('RIGHTPADDING', (0, 0), (-1, -1), 5),
    ])

def section_label(text, bg=C_BLUE):
    """Returns a single-cell table used as a section header bar."""
    t = Table([[Paragraph(text, SEC_HEADER)]], colWidths=[PAGE_W - 2*MARGIN])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), bg),
        ('TOPPADDING', (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING', (0,0), (-1,-1), 6),
        ('RIGHTPADDING', (0,0), (-1,-1), 6),
        ('ROUNDEDCORNERS', [3, 3, 3, 3]),
    ]))
    return t

# ══════════════════════════════════════════════════════════════════════════════
# PAGE 1 – Normal Values + Lead Anatomy + Rate Calculation
# ══════════════════════════════════════════════════════════════════════════════

def build_page1():
    story = []
    W = PAGE_W - 2*MARGIN

    # ── HEADER BANNER ──────────────────────────────────────────────────────────
    header_table = Table(
        [[Paragraph("ECG Quick Reference Guide", TITLE_STYLE)],
         [Paragraph("Normal Values  •  Diagnostic Criteria  •  Systematic Interpretation", SUBTITLE_STYLE)]],
        colWidths=[W]
    )
    header_table.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,-1), C_HEADER_BG),
        ('TOPPADDING',    (0,0), (-1,-1), 10),
        ('BOTTOMPADDING', (0,0), (-1,-1), 10),
        ('LEFTPADDING',   (0,0), (-1,-1), 8),
        ('RIGHTPADDING',  (0,0), (-1,-1), 8),
        ('ROUNDEDCORNERS',[4,4,4,4]),
    ]))
    story.append(header_table)
    story.append(Spacer(1, 5*mm))

    # ── ECG PAPER BASICS ──────────────────────────────────────────────────────
    story.append(section_label("📋  ECG PAPER BASICS", C_DARK))
    story.append(Spacer(1, 2*mm))

    basics_data = [
        [Paragraph("Parameter", CELL_BOLD_CENTER), Paragraph("Value", CELL_BOLD_CENTER),
         Paragraph("Parameter", CELL_BOLD_CENTER), Paragraph("Value", CELL_BOLD_CENTER)],
        [Paragraph("Paper speed", CELL_BOLD), Paragraph("25 mm/sec", CELL_GREEN),
         Paragraph("1 small box (horiz)", CELL_BOLD), Paragraph("0.04 sec  /  1 mm", CELL_NORMAL)],
        [Paragraph("Standard gain", CELL_BOLD), Paragraph("10 mm = 1 mV", CELL_GREEN),
         Paragraph("1 large box (horiz)", CELL_BOLD), Paragraph("0.20 sec  /  5 mm", CELL_NORMAL)],
        [Paragraph("1 small box (vert)", CELL_BOLD), Paragraph("0.1 mV", CELL_NORMAL),
         Paragraph("1 large box (vert)", CELL_BOLD), Paragraph("0.5 mV", CELL_NORMAL)],
    ]
    col_w = W / 4
    basics_t = Table(basics_data, colWidths=[col_w]*4)
    ts = base_ts(header_bg=C_DARK)
    ts.add('BACKGROUND', (0,0), (-1,0), C_DARK)
    basics_t.setStyle(ts)
    story.append(basics_t)
    story.append(Spacer(1, 4*mm))

    # ── NORMAL INTERVALS TABLE ─────────────────────────────────────────────────
    story.append(section_label("⏱  NORMAL ECG INTERVALS & AMPLITUDES", C_BLUE))
    story.append(Spacer(1, 2*mm))

    intervals_data = [
        [Paragraph("Wave / Interval", CELL_BOLD), Paragraph("Normal Range", CELL_BOLD),
         Paragraph("Boxes", CELL_BOLD), Paragraph("Clinical Note", CELL_BOLD)],
        [Paragraph("Heart Rate", CELL_BOLD),        Paragraph("60 – 100 bpm", CELL_GREEN),
         Paragraph("—", CELL_CENTER),               Paragraph("Sinus bradycardia <60, tachycardia >100", CELL_NORMAL)],
        [Paragraph("P wave duration", CELL_BOLD),   Paragraph("< 120 ms", CELL_GREEN),
         Paragraph("< 3 small", CELL_CENTER),        Paragraph(">120 ms notched = Left atrial enlargement", CELL_NORMAL)],
        [Paragraph("P wave amplitude", CELL_BOLD),  Paragraph("< 2.5 mm", CELL_GREEN),
         Paragraph("< 2.5 small", CELL_CENTER),      Paragraph(">2.5 mm peaked = Right atrial enlargement", CELL_NORMAL)],
        [Paragraph("PR interval", CELL_BOLD),        Paragraph("120 – 200 ms", CELL_GREEN),
         Paragraph("3 – 5 large", CELL_CENTER),      Paragraph("<120 WPW/junctional; >200 1st-degree AV block", CELL_NORMAL)],
        [Paragraph("QRS duration", CELL_BOLD),       Paragraph("< 120 ms", CELL_GREEN),
         Paragraph("< 3 small", CELL_CENTER),         Paragraph(">120 ms = Bundle Branch Block", CELL_NORMAL)],
        [Paragraph("QT interval (QTc)", CELL_BOLD),  Paragraph("Male <440 ms\nFemale <460 ms", CELL_GREEN),
         Paragraph("—", CELL_CENTER),                Paragraph("Bazett: QTc = QT / sqrt(RR in sec)", CELL_NORMAL)],
        [Paragraph("QRS Axis", CELL_BOLD),           Paragraph("-30° to +100°", CELL_GREEN),
         Paragraph("—", CELL_CENTER),                Paragraph("LAD < -30°;  RAD > +100°", CELL_NORMAL)],
        [Paragraph("R-wave progression", CELL_BOLD), Paragraph("V1 → V6 increasing", CELL_GREEN),
         Paragraph("—", CELL_CENTER),                Paragraph("Transition (R=S) normally at V3/V4", CELL_NORMAL)],
        [Paragraph("U wave", CELL_BOLD),             Paragraph("< 1 mm", CELL_GREEN),
         Paragraph("< 1 small", CELL_CENTER),         Paragraph("Prominent U = hypokalemia, amiodarone", CELL_NORMAL)],
    ]
    cw = [W*0.19, W*0.18, W*0.13, W*0.50]
    iv_t = Table(intervals_data, colWidths=cw)
    ts2 = base_ts(header_bg=C_BLUE)
    ts2.add('ROWBACKGROUNDS', (0,1), (-1,-1), [C_WHITE, C_BG_BLUE])
    iv_t.setStyle(ts2)
    story.append(iv_t)
    story.append(Spacer(1, 4*mm))

    # ── TWO-COLUMN ROW: Leads Anatomy + Heart Rate ─────────────────────────────
    story.append(section_label("🗺  LEAD TERRITORIES & CULPRIT VESSELS", C_TEAL))
    story.append(Spacer(1, 2*mm))

    lead_data = [
        [Paragraph("Leads", CELL_BOLD_CENTER), Paragraph("Wall", CELL_BOLD_CENTER), Paragraph("Artery", CELL_BOLD_CENTER)],
        [Paragraph("II, III, aVF", CELL_BOLD),    Paragraph("Inferior", CELL_NORMAL),          Paragraph("RCA", CELL_NORMAL)],
        [Paragraph("V1 – V4", CELL_BOLD),         Paragraph("Anterior", CELL_NORMAL),          Paragraph("LAD", CELL_NORMAL)],
        [Paragraph("V1 – V2", CELL_BOLD),         Paragraph("Septal", CELL_NORMAL),            Paragraph("LAD (septal perfs)", CELL_NORMAL)],
        [Paragraph("I, aVL, V5–V6", CELL_BOLD),  Paragraph("Lateral", CELL_NORMAL),           Paragraph("LCx", CELL_NORMAL)],
        [Paragraph("V1–V2 tall R", CELL_BOLD),    Paragraph("Posterior", CELL_NORMAL),         Paragraph("RCA / LCx", CELL_NORMAL)],
        [Paragraph("V4R", CELL_BOLD),             Paragraph("Right ventricle", CELL_NORMAL),   Paragraph("RCA (proximal)", CELL_NORMAL)],
        [Paragraph("aVR", CELL_BOLD),             Paragraph("RV outflow/aortic root", CELL_NORMAL), Paragraph("LMCA / LAD", CELL_NORMAL)],
    ]
    lead_t = Table(lead_data, colWidths=[W*0.3*0.35, W*0.3*0.35, W*0.3*0.30])
    ts3 = base_ts(header_bg=C_TEAL)
    ts3.add('ROWBACKGROUNDS', (0,1), (-1,-1), [C_WHITE, C_BG_TEAL])
    lead_t.setStyle(ts3)

    # Heart Rate Quick Calc
    hr_data = [
        [Paragraph("HEART RATE QUICK CALCULATION", CELL_BOLD_CENTER)],
        [Paragraph("<b>Regular rhythm:</b>  300 ÷ (large boxes between R-R)", CELL_NORMAL)],
        [Paragraph("<b>Mnemonic ladder:</b>  1 box=300  |  2=150  |  3=100\n4=75  |  5=60  |  6=50  |  7=43", CELL_NORMAL)],
        [Paragraph("<b>Irregular rhythm:</b>  Count QRS in 10-sec strip × 6", CELL_NORMAL)],
        [Paragraph("<b>300-method tip:</b>  Find an R on a heavy line, count boxes to next R", CELL_NORMAL)],
    ]
    hr_t = Table(hr_data, colWidths=[W*0.68])
    hr_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), C_ORANGE),
        ('TEXTCOLOR',     (0,0), (-1,0), C_WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'Helvetica-Bold'),
        ('GRID',          (0,0), (-1,-1), 0.4, colors.HexColor("#BDBDBD")),
        ('ROWBACKGROUNDS',(0,1), (-1,-1), [C_BG_ORANGE, C_WHITE]),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 6),
        ('RIGHTPADDING',  (0,0), (-1,-1), 6),
    ]))

    two_col = Table(
        [[lead_t, Spacer(3*mm, 1), hr_t]],
        colWidths=[W*0.30, 3*mm, W*0.68]
    )
    two_col.setStyle(TableStyle([
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('LEFTPADDING', (0,0), (-1,-1), 0),
        ('RIGHTPADDING', (0,0), (-1,-1), 0),
        ('TOPPADDING', (0,0), (-1,-1), 0),
        ('BOTTOMPADDING', (0,0), (-1,-1), 0),
    ]))
    story.append(two_col)
    story.append(Spacer(1, 4*mm))

    # ── SOURCE FOOTER ──────────────────────────────────────────────────────────
    story.append(HRFlowable(width=W, thickness=0.5, color=C_GRAY))
    story.append(Spacer(1, 1*mm))
    story.append(Paragraph(
        "Sources: Harrison's Principles of Internal Medicine 22E (2025) | Ganong's Review of Medical Physiology 26E | Braunwald's Heart Disease 12E     •     Page 1 of 2",
        FOOTER_STYLE))

    return story

# ══════════════════════════════════════════════════════════════════════════════
# PAGE 2 – Diagnostic Criteria
# ══════════════════════════════════════════════════════════════════════════════

def build_page2():
    story = []
    W = PAGE_W - 2*MARGIN

    # Mini header
    mini_h = Table([[Paragraph("ECG Diagnostic Criteria  —  Quick Reference", TITLE_STYLE)]],
                   colWidths=[W])
    mini_h.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,-1), C_HEADER_BG),
        ('TOPPADDING',    (0,0), (-1,-1), 8),
        ('BOTTOMPADDING', (0,0), (-1,-1), 8),
        ('LEFTPADDING',   (0,0), (-1,-1), 8),
        ('RIGHTPADDING',  (0,0), (-1,-1), 8),
        ('ROUNDEDCORNERS',[4,4,4,4]),
    ]))
    story.append(mini_h)
    story.append(Spacer(1, 4*mm))

    # ── AV BLOCKS ─────────────────────────────────────────────────────────────
    story.append(section_label("💔  AV CONDUCTION BLOCKS", C_RED))
    story.append(Spacer(1, 2*mm))

    av_data = [
        [Paragraph("Block Type", CELL_BOLD), Paragraph("PR Interval", CELL_BOLD),
         Paragraph("Dropped QRS?", CELL_BOLD), Paragraph("P:QRS Ratio", CELL_BOLD), Paragraph("Key Feature", CELL_BOLD)],
        [Paragraph("1st Degree", CELL_NORMAL),        Paragraph(">200 ms  (fixed)", CELL_RED),
         Paragraph("No", CELL_NORMAL),                Paragraph("1:1", CELL_CENTER),
         Paragraph("Prolonged but all conduct", CELL_NORMAL)],
        [Paragraph("2nd Degree – Mobitz I\n(Wenckebach)", CELL_NORMAL),  Paragraph("Progressive lengthening", CELL_RED),
         Paragraph("Yes (periodic)", CELL_NORMAL),    Paragraph(">1:1", CELL_CENTER),
         Paragraph("Grouped beating; PR lengthens until drop", CELL_NORMAL)],
        [Paragraph("2nd Degree – Mobitz II", CELL_NORMAL), Paragraph("Fixed PR", CELL_RED),
         Paragraph("Yes (sudden)", CELL_NORMAL),      Paragraph(">1:1", CELL_CENTER),
         Paragraph("Sudden non-conducted P; often needs pacemaker", CELL_NORMAL)],
        [Paragraph("3rd Degree (Complete)", CELL_BOLD), Paragraph("No relation (dissociation)", CELL_RED),
         Paragraph("P & QRS independent", CELL_NORMAL), Paragraph("None", CELL_CENTER),
         Paragraph("P rate > QRS rate; escape rhythm present", CELL_NORMAL)],
    ]
    cw_av = [W*0.20, W*0.18, W*0.14, W*0.12, W*0.36]
    av_t = Table(av_data, colWidths=cw_av)
    ts_av = base_ts(header_bg=C_RED)
    ts_av.add('ROWBACKGROUNDS', (0,1), (-1,-1), [C_WHITE, C_BG_RED])
    av_t.setStyle(ts_av)
    story.append(av_t)
    story.append(Spacer(1, 4*mm))

    # ── BUNDLE BRANCH BLOCKS ──────────────────────────────────────────────────
    story.append(section_label("🔀  BUNDLE BRANCH BLOCKS  (QRS > 120 ms)", C_PURPLE))
    story.append(Spacer(1, 2*mm))

    bbb_data = [
        [Paragraph("Feature", CELL_BOLD), Paragraph("RBBB", CELL_BOLD), Paragraph("LBBB", CELL_BOLD)],
        [Paragraph("V1 pattern", CELL_BOLD),      Paragraph("RSR' (rabbit ears) / rSR'", CELL_NORMAL),    Paragraph("QS or rS (broad notched)", CELL_NORMAL)],
        [Paragraph("V6 pattern", CELL_BOLD),      Paragraph("Wide slurred S wave", CELL_NORMAL),          Paragraph("Broad monophasic R (no Q/S)", CELL_NORMAL)],
        [Paragraph("Lead I", CELL_BOLD),          Paragraph("Wide S wave", CELL_NORMAL),                  Paragraph("Broad R, no Q", CELL_NORMAL)],
        [Paragraph("ST-T changes", CELL_BOLD),    Paragraph("Discordant to QRS terminal", CELL_NORMAL),   Paragraph("Discordant to QRS (ST opp. to R)", CELL_NORMAL)],
        [Paragraph("Sgarbossa criteria", CELL_BOLD), Paragraph("Not typically applied", CELL_NORMAL),     Paragraph("Used to detect MI in LBBB", CELL_NORMAL)],
    ]
    cw_bbb = [W*0.22, W*0.39, W*0.39]
    bbb_t = Table(bbb_data, colWidths=cw_bbb)
    ts_bbb = base_ts(header_bg=C_PURPLE)
    ts_bbb.add('ROWBACKGROUNDS', (0,1), (-1,-1), [C_WHITE, C_BG_PURPLE])
    bbb_t.setStyle(ts_bbb)
    story.append(bbb_t)
    story.append(Spacer(1, 4*mm))

    # ── TWO-COLUMN: Axis + Hypertrophy ─────────────────────────────────────────
    col_left_w = W * 0.46
    col_right_w = W * 0.50
    gap = W * 0.04

    # LEFT: QRS Axis
    story.append(section_label("🧭  QRS AXIS  (Frontal Plane)", C_TEAL))
    story.append(Spacer(1, 2*mm))

    axis_data = [
        [Paragraph("Lead I", CELL_BOLD_CENTER), Paragraph("aVF", CELL_BOLD_CENTER),
         Paragraph("Axis", CELL_BOLD_CENTER),   Paragraph("Causes", CELL_BOLD)],
        [Paragraph("Positive (+)", CELL_CENTER), Paragraph("Positive (+)", CELL_CENTER),
         Paragraph("NORMAL\n(-30 to +90)", CELL_GREEN), Paragraph("Normal", CELL_NORMAL)],
        [Paragraph("Positive (+)", CELL_CENTER), Paragraph("Negative (-)", CELL_CENTER),
         Paragraph("LAD\n(< -30°)", CELL_RED),  Paragraph("LVH, LAFB, inferior MI", CELL_NORMAL)],
        [Paragraph("Negative (-)", CELL_CENTER), Paragraph("Positive (+)", CELL_CENTER),
         Paragraph("RAD\n(> +100°)", CELL_RED), Paragraph("RVH, LPFB, lateral MI, PE", CELL_NORMAL)],
        [Paragraph("Negative (-)", CELL_CENTER), Paragraph("Negative (-)", CELL_CENTER),
         Paragraph("EXTREME\n(NW axis)", CELL_RED), Paragraph("Severe RVH, dextrocardia, artifact", CELL_NORMAL)],
    ]
    cw_axis = [col_left_w*0.22, col_left_w*0.22, col_left_w*0.22, col_left_w*0.34]
    axis_t = Table(axis_data, colWidths=cw_axis)
    ts_axis = base_ts(header_bg=C_TEAL)
    ts_axis.add('ROWBACKGROUNDS', (0,1), (-1,-1), [C_WHITE, C_BG_TEAL])
    axis_t.setStyle(ts_axis)

    # RIGHT: Hypertrophy Criteria
    hyp_data = [
        [Paragraph("Hypertrophy Criteria", CELL_BOLD_CENTER), Paragraph("Criterion", CELL_BOLD_CENTER)],
        [Paragraph("LVH – Sokolow-Lyon", CELL_BOLD), Paragraph("S(V1) + R(V5 or V6) > 35 mm", CELL_NORMAL)],
        [Paragraph("LVH – Cornell (men)", CELL_BOLD),   Paragraph("R(aVL) + S(V3) > 28 mm", CELL_NORMAL)],
        [Paragraph("LVH – Cornell (women)", CELL_BOLD), Paragraph("R(aVL) + S(V3) > 20 mm", CELL_NORMAL)],
        [Paragraph("LVH – aVL alone", CELL_BOLD),       Paragraph("R(aVL) > 11 mm", CELL_NORMAL)],
        [Paragraph("RVH criteria", CELL_BOLD), Paragraph("R>S in V1, RAD, R(V1)+S(V5/6)>11mm", CELL_NORMAL)],
        [Paragraph("LAE (P-mitrale)", CELL_BOLD), Paragraph("P duration >120ms or biphasic in V1", CELL_NORMAL)],
        [Paragraph("RAE (P-pulmonale)", CELL_BOLD), Paragraph("P amplitude >2.5mm in II, III, aVF", CELL_NORMAL)],
    ]
    cw_hyp = [col_right_w*0.45, col_right_w*0.55]
    hyp_t = Table(hyp_data, colWidths=cw_hyp)
    ts_hyp = base_ts(header_bg=C_ORANGE)
    ts_hyp.add('ROWBACKGROUNDS', (0,1), (-1,-1), [C_WHITE, C_BG_ORANGE])
    hyp_t.setStyle(ts_hyp)

    two_col2 = Table(
        [[axis_t, Spacer(gap, 1), hyp_t]],
        colWidths=[col_left_w, gap, col_right_w]
    )
    two_col2.setStyle(TableStyle([
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('LEFTPADDING', (0,0), (-1,-1), 0),
        ('RIGHTPADDING', (0,0), (-1,-1), 0),
        ('TOPPADDING', (0,0), (-1,-1), 0),
        ('BOTTOMPADDING', (0,0), (-1,-1), 0),
    ]))
    story.append(two_col2)
    story.append(Spacer(1, 4*mm))

    # ── CLASSIC ECG PATTERNS ──────────────────────────────────────────────────
    story.append(section_label("🔍  CLASSIC / HIGH-YIELD ECG PATTERNS", C_DARK))
    story.append(Spacer(1, 2*mm))

    pat_data = [
        [Paragraph("Pattern", CELL_BOLD), Paragraph("ECG Finding", CELL_BOLD), Paragraph("Diagnosis", CELL_BOLD)],
        [Paragraph("Saddle-shaped ST elevation + PR depression", CELL_NORMAL), Paragraph("Diffuse concave STE + PR depression in I,II,V2-6", CELL_NORMAL), Paragraph("Acute Pericarditis", CELL_BOLD)],
        [Paragraph("Electrical alternans", CELL_NORMAL), Paragraph("Alternating QRS height beat-to-beat", CELL_NORMAL), Paragraph("Cardiac Tamponade", CELL_BOLD)],
        [Paragraph("Delta wave + short PR (<120ms)", CELL_NORMAL), Paragraph("Slurred upstroke of QRS, short PR, wide QRS", CELL_NORMAL), Paragraph("WPW Syndrome", CELL_BOLD)],
        [Paragraph("S1Q3T3", CELL_NORMAL), Paragraph("S in I, Q in III, T inversion in III", CELL_NORMAL), Paragraph("Pulmonary Embolism (non-specific)", CELL_BOLD)],
        [Paragraph("Osborne (J) waves", CELL_NORMAL), Paragraph("Positive deflection at J point (best in V4-V6)", CELL_NORMAL), Paragraph("Hypothermia", CELL_BOLD)],
        [Paragraph("Sine wave / peaked T + wide QRS", CELL_NORMAL), Paragraph("Broad bizarre QRS; peaked T waves", CELL_NORMAL), Paragraph("Severe Hyperkalemia", CELL_BOLD)],
        [Paragraph("Deep T-inversion V1-V4 (Wellens')", CELL_NORMAL), Paragraph("Biphasic or deep symmetric T inversion V1-V4", CELL_NORMAL), Paragraph("Critical LAD stenosis — Emergency!", CELL_RED)],
        [Paragraph("Short QT + down-sloping ST", CELL_NORMAL), Paragraph("QTc <340ms, shortened ST segment", CELL_NORMAL), Paragraph("Hypercalcemia / Digoxin toxicity", CELL_BOLD)],
        [Paragraph("STEMI + reciprocal ST depression", CELL_NORMAL), Paragraph("Convex STE in territory + mirror depression", CELL_NORMAL), Paragraph("Acute Myocardial Infarction (STEMI)", CELL_RED)],
        [Paragraph("Tall R in V1 + RAD + no septal Q in V6", CELL_NORMAL), Paragraph("Dominant R in V1 (R>S), axis deviation", CELL_NORMAL), Paragraph("RBBB / RVH / Posterior MI", CELL_BOLD)],
        [Paragraph("Epsilon wave (V1-V3)", CELL_NORMAL), Paragraph("Small positive deflection after QRS in V1-V3", CELL_NORMAL), Paragraph("ARVC (Arrhythmogenic RV Cardiomyopathy)", CELL_BOLD)],
    ]
    cw_p = [W*0.30, W*0.38, W*0.32]
    pat_t = Table(pat_data, colWidths=cw_p)
    ts_pat = base_ts(header_bg=C_DARK)
    ts_pat.add('ROWBACKGROUNDS', (0,1), (-1,-1), [C_WHITE, C_BG_LIGHT])
    pat_t.setStyle(ts_pat)
    story.append(pat_t)
    story.append(Spacer(1, 3*mm))

    # ── STEMI TERRITORIES TABLE ───────────────────────────────────────────────
    story.append(section_label("❤️  STEMI LOCALISATION", C_RED))
    story.append(Spacer(1, 2*mm))

    stemi_data = [
        [Paragraph("Leads with STE", CELL_BOLD), Paragraph("Territory", CELL_BOLD),
         Paragraph("Culprit", CELL_BOLD), Paragraph("Reciprocal Changes", CELL_BOLD)],
        [Paragraph("II, III, aVF", CELL_NORMAL),   Paragraph("Inferior", CELL_NORMAL),    Paragraph("RCA (85%)", CELL_NORMAL), Paragraph("I, aVL", CELL_NORMAL)],
        [Paragraph("V1 – V4", CELL_NORMAL),         Paragraph("Anterior", CELL_NORMAL),    Paragraph("LAD", CELL_NORMAL),       Paragraph("II, III, aVF", CELL_NORMAL)],
        [Paragraph("V1 – V2", CELL_NORMAL),         Paragraph("Septal", CELL_NORMAL),      Paragraph("LAD proximal", CELL_NORMAL), Paragraph("None typical", CELL_NORMAL)],
        [Paragraph("I, aVL, V5–V6", CELL_NORMAL),  Paragraph("Lateral", CELL_NORMAL),     Paragraph("LCx", CELL_NORMAL),       Paragraph("II, III, aVF", CELL_NORMAL)],
        [Paragraph("V1-V3 tall R + ST depression", CELL_NORMAL), Paragraph("Posterior", CELL_NORMAL), Paragraph("RCA/LCx", CELL_NORMAL), Paragraph("(Mirror image in V1-V3)", CELL_NORMAL)],
        [Paragraph("aVR STE + diffuse depression", CELL_NORMAL), Paragraph("LMCA/prox LAD", CELL_NORMAL), Paragraph("LMCA or LAD", CELL_NORMAL), Paragraph("Diffuse ST depression", CELL_NORMAL)],
    ]
    cw_s = [W*0.25, W*0.20, W*0.22, W*0.33]
    stemi_t = Table(stemi_data, colWidths=cw_s)
    ts_s = base_ts(header_bg=C_RED)
    ts_s.add('ROWBACKGROUNDS', (0,1), (-1,-1), [C_WHITE, C_BG_RED])
    stemi_t.setStyle(ts_s)
    story.append(stemi_t)
    story.append(Spacer(1, 3*mm))

    # ── FOOTER ────────────────────────────────────────────────────────────────
    story.append(HRFlowable(width=W, thickness=0.5, color=C_GRAY))
    story.append(Spacer(1, 1*mm))
    story.append(Paragraph(
        "Sources: Harrison's Principles of Internal Medicine 22E (2025) | Ganong's Review of Medical Physiology 26E | Braunwald's Heart Disease 12E     •     Page 2 of 2",
        FOOTER_STYLE))

    return story


# ══════════════════════════════════════════════════════════════════════════════
# BUILD PDF
# ══════════════════════════════════════════════════════════════════════════════

def build_pdf():
    doc = SimpleDocTemplate(
        OUTPUT_PATH,
        pagesize=A4,
        leftMargin=MARGIN, rightMargin=MARGIN,
        topMargin=MARGIN, bottomMargin=MARGIN,
        title="ECG Quick Reference Guide",
        author="Orris Medical AI",
        subject="ECG Normal Values and Diagnostic Criteria",
    )

    story = []
    story += build_page1()
    from reportlab.platypus import PageBreak
    story.append(PageBreak())
    story += build_page2()

    doc.build(story)
    size_kb = os.path.getsize(OUTPUT_PATH) // 1024
    print(f"PDF created: {OUTPUT_PATH}  ({size_kb} KB)")

if __name__ == "__main__":
    build_pdf()
Running Command

cd /home/daytona/workspace/ecg-pdf && python create_ecg_pdf.py 2>&1

Your credits are exhausted. Your daily credits reset tomorrow, or you can purchase a top-up for more.
This is a shared conversation. Sign in to Orris to start your own chat.