Explain me 10 common heart diseases with ECG changes and ECG images of those diseases

Reading File
Finding Sources
Searching Images

ECG electrocardiogram myocardial infarction STEMI changes

Finding Sources
Searching Images

atrial fibrillation ECG rhythm strip

Searching Images

ventricular tachycardia ECG wide QRS

Searching Images

complete heart block third degree AV block ECG

Searching Images

left bundle branch block LBBB ECG pattern

Searching Images

hypertrophic cardiomyopathy ECG left ventricular hypertrophy

Searching Images

Wolff-Parkinson-White WPW syndrome delta wave ECG

Searching Images

pericarditis ECG saddle-shaped ST elevation diffuse

Searching Images

pulmonary embolism ECG S1Q3T3 right heart strain

Searching Images

long QT syndrome QT prolongation torsades de pointes ECG

Here is a comprehensive guide to 10 common heart diseases with their ECG changes and images:

10 Common Heart Diseases: ECG Changes & Images


1. πŸ«€ ST-Elevation Myocardial Infarction (STEMI)

Pathophysiology: Complete occlusion of a coronary artery causing transmural (full-thickness) myocardial infarction.

ECG Changes:

StageChanges
Hyperacute (minutes)Tall, peaked "hyperacute" T-waves
AcuteConvex ("tombstone") ST elevation β‰₯1 mm in β‰₯2 contiguous leads
EvolvingT-wave inversion, pathological Q waves (>40 ms wide, >ΒΌ R height)
Old/chronicPersistent Q waves, T-wave inversion
Localisation:
  • Anterior (LAD): V1–V4 ST elevation
  • Inferior (RCA): II, III, aVF ST elevation + reciprocal depression in I, aVL
  • Lateral (LCx): I, aVL, V5–V6
Anterior STEMI (LAD occlusion):
Anterior STEMI ECG showing tombstone ST elevation V2-V5
Inferior STEMI (RCA occlusion):
Inferior STEMI ECG showing ST elevation in II, III, aVF with reciprocal changes

2. πŸ«€ Atrial Fibrillation (AF)

Pathophysiology: Chaotic, disorganized atrial electrical activity from multiple re-entry circuits; most common sustained arrhythmia.

ECG Changes:

  • No P waves β€” replaced by irregular fibrillatory (f) waves (best seen in V1, II)
  • Irregularly irregular RR intervals (hallmark)
  • Narrow QRS complexes (unless aberrant conduction/WPW)
  • Ventricular rate typically 100–160 bpm if uncontrolled
Atrial fibrillation ECG showing absent P waves and irregularly irregular rhythm

3. πŸ«€ Ventricular Tachycardia (VT)

Pathophysiology: Rapid ventricular rhythm (β‰₯3 beats, rate β‰₯100 bpm) originating below the Bundle of His, often from re-entry around scar tissue (post-MI, cardiomyopathy).

ECG Changes:

  • Wide QRS complex (>120 ms), bizarre morphology
  • Regular rapid rate (100–250 bpm)
  • AV dissociation (P waves independent of QRS)
  • Fusion beats and capture beats (pathognomonic)
  • Positive or negative QRS concordance across precordial leads
  • No visible P waves preceding QRS
Ventricular tachycardia ECG showing wide complex tachycardia with AV dissociation

4. πŸ«€ Complete (Third-Degree) AV Heart Block

Pathophysiology: Total failure of conduction from atria to ventricles; the ventricles are maintained by a slow escape rhythm (junctional or ventricular).

ECG Changes:

  • Complete AV dissociation β€” P waves and QRS complexes march independently
  • Regular P-P intervals (atrial rate ~60–100 bpm)
  • Regular RR intervals at a slower rate (ventricular escape 30–50 bpm)
  • Wide QRS if infra-Hisian escape; narrow if junctional
  • P waves may "march through" QRS complexes and T waves
Complete third-degree AV block ECG showing total AV dissociation

5. πŸ«€ Left Bundle Branch Block (LBBB)

Pathophysiology: Failure of conduction through the left bundle branch, causing delayed and abnormal LV depolarization. Can indicate underlying CAD, cardiomyopathy, or hypertension.

ECG Changes (William pattern):

  • QRS duration β‰₯120 ms
  • V1: Broad, deep rS or QS (predominantly negative)
  • V5, V6, I, aVL: Broad, notched monophasic R wave ("M" shape)
  • No septal Q waves in I, V5, V6
  • Discordant ST-T changes (ST/T opposite to main QRS deflection)
  • New LBBB may be a STEMI equivalent (Sgarbossa criteria)
LBBB ECG showing broad notched R waves laterally and deep S in V1

6. πŸ«€ Hypertrophic Cardiomyopathy (HCM)

Pathophysiology: Asymmetric left ventricular hypertrophy (especially septal), causing diastolic dysfunction and LVOT obstruction. Leading cause of sudden cardiac death in young athletes.

ECG Changes:

  • LVH voltage criteria (Sokolow-Lyon: S in V1 + R in V5/V6 β‰₯35 mm)
  • Deep, symmetric "giant" T-wave inversions in precordial leads (V2–V5) β€” classic in apical HCM (Yamaguchi syndrome)
  • Strain pattern: ST depression in lateral leads
  • Absence of septal Q waves in I, aVL, V5, V6 (abnormal septal depolarization)
  • Left axis deviation
  • Atrial fibrillation may be present
HCM ECG showing high voltage LVH and deep T-wave inversions in precordial leads

7. πŸ«€ Wolff-Parkinson-White (WPW) Syndrome

Pathophysiology: Congenital accessory conduction pathway (Bundle of Kent) bypasses the AV node, causing ventricular pre-excitation and tachyarrhythmias (AVRT, AF with rapid ventricular response β€” potentially fatal).

ECG Classic Triad:

  1. Short PR interval (<120 ms) β€” bypassed AV node delay
  2. Delta wave β€” slurred upstroke at the start of QRS
  3. Wide QRS (>120 ms) β€” fusion of pre-excited and normal depolarization
  4. Secondary ST-T wave changes (discordant)
  5. Pseudo-infarction Q waves possible (mimics MI)
WPW syndrome ECG showing short PR interval, delta waves, and widened QRS

8. πŸ«€ Acute Pericarditis

Pathophysiology: Inflammation of the pericardium, classically from viral infection (Coxsackievirus, EBV), causing diffuse epicardial irritation.

ECG Changes (4 stages):

StageFeatures
Stage 1 (acute)Diffuse concave ("saddle-shaped") ST elevation in most leads; PR depression (most leads); PR elevation in aVR
Stage 2ST normalizes, T-waves flatten
Stage 3Diffuse T-wave inversions
Stage 4ECG normalizes
Key differentiators from STEMI:
  • ST elevation is diffuse (not in one vascular territory)
  • Concave (not convex) morphology
  • PR depression is specific to pericarditis
  • Spodick's sign: downsloping TP segment
  • No reciprocal changes (except aVR)
Acute pericarditis ECG showing diffuse saddle-shaped ST elevation and PR depression

9. πŸ«€ Pulmonary Embolism (PE)

Pathophysiology: Thrombus obstructing pulmonary vasculature β†’ acute right heart strain β†’ rightward shift of electrical axis β†’ characteristic ECG findings.

ECG Changes:

  • Sinus tachycardia (most common, ~44% of cases)
  • S1Q3T3 pattern (classic): prominent S wave in lead I + Q wave in lead III + T-wave inversion in lead III
  • T-wave inversions in V1–V4 (right ventricular strain pattern)
  • Incomplete or complete RBBB
  • Right axis deviation
  • P pulmonale (peaked P waves) indicating right atrial strain
  • AF may occur
⚠️ Note: A normal ECG does not exclude PE. S1Q3T3 has low sensitivity (~20%) but is highly specific when present.
Pulmonary embolism ECG showing classic S1Q3T3 pattern with right heart strain

10. πŸ«€ Long QT Syndrome (LQTS)

Pathophysiology: Prolonged ventricular repolarization (congenital ion channel mutations β€” KCNQ1, KCNH2, SCN5A β€” or acquired from drugs/electrolytes), predisposing to life-threatening Torsades de Pointes (TdP) and sudden cardiac death.

ECG Changes:

  • Prolonged QTc interval (corrected for rate using Bazett's formula)
    • Normal: men <440 ms, women <460 ms
    • Borderline: 440–500 ms
    • High risk: >500 ms
  • Broad, prominent T waves (LQTS type 1) or bifid T waves with a "hump" (LQTS type 2) or prominent U waves (LQTS type 3)
  • Torsades de Pointes: polymorphic VT with twisting QRS complexes around the isoelectric line β€” "R on T" initiation pattern
Long QT syndrome ECG showing QT prolongation leading to Torsades de Pointes

Quick Reference Summary Table

#DiseaseKey ECG Finding
1STEMIConvex ST elevation + Q waves in territory
2Atrial FibrillationNo P waves, irregularly irregular RR
3Ventricular TachycardiaWide QRS tachycardia, AV dissociation
4Complete Heart BlockP & QRS march independently (AV dissociation)
5LBBBWide QRS, monophasic R in V5-V6, rS in V1
6HCMLVH + giant T-wave inversions (V2-V5)
7WPWShort PR + delta wave + wide QRS
8PericarditisDiffuse concave ST elevation + PR depression
9Pulmonary EmbolismS1Q3T3 + sinus tachycardia + RV strain
10Long QT SyndromeQTc >500 ms + Torsades de Pointes

Clinical pearl: ECG interpretation is always contextual β€” the same ECG finding can mean different things depending on the clinical scenario (e.g., new LBBB + chest pain = possible STEMI equivalent; diffuse ST elevation + fever + pleuritic chest pain = pericarditis, not MI).

Create a quick reference PDF guide of these 10 heart diseases and their ECG patterns

Reading File
Running Command

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

Running Command

mkdir -p /home/daytona/workspace/ecg-guide && python3 -c "import reportlab; prin…

Writing File

~/ecg-guide/create_ecg_pdf.py

#!/usr/bin/env python3
"""
Create a comprehensive ECG Quick Reference PDF for 10 common heart diseases.
"""

import os
import requests
from io import BytesIO
from PIL import Image

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_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    Image as RLImage, HRFlowable, PageBreak, KeepTogether
)
from reportlab.platypus.flowables import HRFlowable
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor

OUTPUT_PATH = "/home/daytona/workspace/ecg-guide/ECG_Quick_Reference_Guide.pdf"

# ── Colour palette ──────────────────────────────────────────────────────────
DARK_RED    = HexColor("#B71C1C")
MEDIUM_RED  = HexColor("#D32F2F")
LIGHT_RED   = HexColor("#FFEBEE")
DARK_BLUE   = HexColor("#0D47A1")
MEDIUM_BLUE = HexColor("#1565C0")
LIGHT_BLUE  = HexColor("#E3F2FD")
DARK_GREY   = HexColor("#212121")
MID_GREY    = HexColor("#424242")
LIGHT_GREY  = HexColor("#F5F5F5")
TABLE_ALT   = HexColor("#FAFAFA")
WHITE       = colors.white
ACCENT_GREEN = HexColor("#1B5E20")
LIGHT_GREEN  = HexColor("#E8F5E9")

# ── ECG Image URLs ───────────────────────────────────────────────────────────
IMAGE_URLS = {
    "stemi_anterior":   "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_cdfb20f9fc1a8412ffb27a5e6e1ae510547606f1b0c520ad6dc5bda4ec26caeb.jpg",
    "stemi_inferior":   "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_5342e382e64f43dd39173eaf875b1588a49cc8c50a1751e548df704019996777.jpg",
    "af":               "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_3c76fbc6e31a1f92963db97bfbae29eae90022cbdb45c2b5f37af6c14bee4bfc.jpg",
    "vt":               "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_d15a92de4e89b46f273b22d254d1e30fcd6a190c6d581b193e6dd338338c6bb6.jpg",
    "chb":              "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_8406f3f9d6a209bfd199f5a59f790f58cd2a726f5674a8e7fbed222b0a72b837.jpg",
    "lbbb":             "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_0be77a66b30cb075621a21dd8998f7155ef974dd06a6228a0372c6857541db8d.jpg",
    "hcm":              "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_2910b31808487d7cd1b8678e2a6942b2ca7518030d03f48787ac4b092bd7e92e.jpg",
    "wpw":              "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_fd0d58cc1913acd56cbc62cbf42657e4a465f052a9bb2ea0d3893584ef6f70e6.jpg",
    "pericarditis":     "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_cd4008531c1b1a2a99ab6d9b4282dcdcedfcf77c302b6ecbb7f7e01c5c985cf9.jpg",
    "pe":               "https://cdn.orris.care/cdss_images/pmc_clinical_VQA_5f391caa5a24ec425e9f48d4d9f73620a3b91f92cea400318772fda12e.jpg",
    "lqts":             "https://cdn.orris.care/cdss_images/HARRISON_1763034143291_d1a2f9c1-7f66-4dec-a3f8-19b81f2ba927.png",
}

def download_image(url, max_size=(500, 300)):
    """Download image and return a ReportLab Image object, or None on failure."""
    try:
        resp = requests.get(url, timeout=15)
        resp.raise_for_status()
        img = Image.open(BytesIO(resp.content)).convert("RGB")
        img.thumbnail(max_size, Image.LANCZOS)
        buf = BytesIO()
        img.save(buf, format="JPEG", quality=85)
        buf.seek(0)
        rl_img = RLImage(buf)
        # Scale to fit width
        w, h = img.size
        target_w = 160 * mm
        scale = target_w / w
        rl_img.drawWidth  = target_w
        rl_img.drawHeight = h * scale
        return rl_img
    except Exception as e:
        print(f"  [WARN] Could not load image {url}: {e}")
        return None


# ── Page template with header/footer ─────────────────────────────────────────
class NumberedCanvas(canvas.Canvas):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._saved_page_states = []

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def save(self):
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self.draw_page_number(num_pages)
            super().showPage()
        super().save()

    def draw_page_number(self, page_count):
        self.saveState()
        page_num = self._pageNumber
        # Footer bar
        self.setFillColor(DARK_RED)
        self.rect(0, 0, A4[0], 18*mm, fill=1, stroke=0)
        self.setFillColor(WHITE)
        self.setFont("Helvetica", 8)
        self.drawString(20*mm, 7*mm, "ECG Quick Reference Guide  |  10 Common Heart Diseases")
        self.drawRightString(A4[0]-20*mm, 7*mm, f"Page {page_num} of {page_count}")
        # Header bar (skip cover page = page 1)
        if page_num > 1:
            self.setFillColor(DARK_BLUE)
            self.rect(0, A4[1]-14*mm, A4[0], 14*mm, fill=1, stroke=0)
            self.setFillColor(WHITE)
            self.setFont("Helvetica-Bold", 9)
            self.drawString(20*mm, A4[1]-9*mm, "ECG QUICK REFERENCE GUIDE")
            self.drawRightString(A4[0]-20*mm, A4[1]-9*mm, "Clinical Cardiology")
        self.restoreState()


# ── Styles ────────────────────────────────────────────────────────────────────
def build_styles():
    base = getSampleStyleSheet()

    styles = {
        "cover_title": ParagraphStyle(
            "cover_title", fontName="Helvetica-Bold", fontSize=32,
            textColor=WHITE, alignment=TA_CENTER, leading=40, spaceAfter=6
        ),
        "cover_sub": ParagraphStyle(
            "cover_sub", fontName="Helvetica", fontSize=14,
            textColor=HexColor("#BBDEFB"), alignment=TA_CENTER, leading=20
        ),
        "cover_byline": ParagraphStyle(
            "cover_byline", fontName="Helvetica-Oblique", fontSize=10,
            textColor=HexColor("#90CAF9"), alignment=TA_CENTER
        ),
        "section_num": ParagraphStyle(
            "section_num", fontName="Helvetica-Bold", fontSize=22,
            textColor=WHITE, alignment=TA_CENTER
        ),
        "section_title": ParagraphStyle(
            "section_title", fontName="Helvetica-Bold", fontSize=17,
            textColor=WHITE, alignment=TA_LEFT, leading=22
        ),
        "section_sub": ParagraphStyle(
            "section_sub", fontName="Helvetica-Oblique", fontSize=10,
            textColor=HexColor("#FFCDD2"), alignment=TA_LEFT
        ),
        "body": ParagraphStyle(
            "body", fontName="Helvetica", fontSize=9,
            textColor=DARK_GREY, leading=14, spaceAfter=3
        ),
        "bullet": ParagraphStyle(
            "bullet", fontName="Helvetica", fontSize=9,
            textColor=DARK_GREY, leading=13, spaceAfter=2,
            leftIndent=10, bulletIndent=0
        ),
        "bold_label": ParagraphStyle(
            "bold_label", fontName="Helvetica-Bold", fontSize=9,
            textColor=DARK_BLUE, leading=13
        ),
        "warning_box": ParagraphStyle(
            "warning_box", fontName="Helvetica-Oblique", fontSize=8.5,
            textColor=ACCENT_GREEN, leading=13
        ),
        "caption": ParagraphStyle(
            "caption", fontName="Helvetica-Oblique", fontSize=7.5,
            textColor=MID_GREY, alignment=TA_CENTER, leading=11
        ),
        "toc_item": ParagraphStyle(
            "toc_item", fontName="Helvetica", fontSize=10,
            textColor=DARK_GREY, leading=16
        ),
        "toc_bold": ParagraphStyle(
            "toc_bold", fontName="Helvetica-Bold", fontSize=10,
            textColor=DARK_BLUE, leading=16
        ),
        "summary_head": ParagraphStyle(
            "summary_head", fontName="Helvetica-Bold", fontSize=11,
            textColor=DARK_BLUE, alignment=TA_CENTER, spaceAfter=4
        ),
    }
    return styles


# ── Helper builders ───────────────────────────────────────────────────────────
def header_banner(num_text, title_text, subtitle_text, styles, w=170*mm):
    """Red banner for each disease section."""
    inner = Table(
        [[
            Paragraph(num_text, styles["section_num"]),
            [Paragraph(title_text, styles["section_title"]),
             Paragraph(subtitle_text, styles["section_sub"])]
        ]],
        colWidths=[22*mm, w-22*mm]
    )
    inner.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), DARK_RED),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
        ("LEFTPADDING",  (0,0), (-1,-1), 6),
        ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("TOPPADDING",   (0,0), (-1,-1), 8),
        ("BOTTOMPADDING",(0,0), (-1,-1), 8),
        ("LINEAFTER", (0,0), (0,-1), 1, HexColor("#EF9A9A")),
    ]))
    return inner


def ecg_changes_table(rows, styles, w=170*mm):
    """Two-column table: Feature | Detail."""
    data = [[Paragraph("<b>ECG Feature</b>", styles["bold_label"]),
             Paragraph("<b>Description</b>", styles["bold_label"])]]
    for feat, desc in rows:
        data.append([
            Paragraph(f"<b>{feat}</b>", styles["bullet"]),
            Paragraph(desc, styles["bullet"])
        ])
    col_w = [52*mm, w-52*mm]
    t = Table(data, colWidths=col_w, repeatRows=1)
    ts = TableStyle([
        ("BACKGROUND",   (0,0), (-1,0),  DARK_BLUE),
        ("TEXTCOLOR",    (0,0), (-1,0),  WHITE),
        ("FONTNAME",     (0,0), (-1,0),  "Helvetica-Bold"),
        ("ROWBACKGROUNDS",(0,1),(-1,-1), [LIGHT_GREY, WHITE]),
        ("GRID",         (0,0), (-1,-1), 0.4, HexColor("#BDBDBD")),
        ("TOPPADDING",   (0,0), (-1,-1), 4),
        ("BOTTOMPADDING",(0,0), (-1,-1), 4),
        ("LEFTPADDING",  (0,0), (-1,-1), 6),
        ("VALIGN",       (0,0), (-1,-1), "TOP"),
    ])
    t.setStyle(ts)
    return t


def info_box(text, styles, bg=LIGHT_BLUE, border=DARK_BLUE):
    t = Table([[Paragraph(text, styles["warning_box"])]], colWidths=[170*mm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("BOX",        (0,0), (-1,-1), 1, border),
        ("LEFTPADDING", (0,0),(-1,-1), 8),
        ("RIGHTPADDING",(0,0),(-1,-1), 8),
        ("TOPPADDING",  (0,0),(-1,-1), 6),
        ("BOTTOMPADDING",(0,0),(-1,-1), 6),
    ]))
    return t


# ── Disease data ──────────────────────────────────────────────────────────────
DISEASES = [
    {
        "num": "01", "name": "ST-Elevation Myocardial Infarction",
        "abbr": "STEMI",
        "subtitle": "Acute complete coronary artery occlusion β€” transmural infarction",
        "ecg_rows": [
            ("Hyperacute T-waves", "Tall, peaked T-waves β€” earliest sign (minutes after occlusion)"),
            ("ST Elevation", "Convex ('tombstone') ST elevation β‰₯1 mm in β‰₯2 contiguous leads"),
            ("Reciprocal Changes", "ST depression in leads opposite to the infarct territory"),
            ("Pathological Q waves", "Width >40 ms, depth >ΒΌ R height β€” indicates necrosis"),
            ("T-wave inversion", "Develops hours–days after occlusion; may persist"),
        ],
        "localisation": [
            ("Anterior (LAD)", "V1–V4 ST elevation"),
            ("Inferior (RCA)",  "II, III, aVF elevation + reciprocal depression in I, aVL"),
            ("Lateral (LCx)",   "I, aVL, V5–V6 elevation"),
            ("Posterior (RCA/LCx)", "ST depression V1–V3 (mirror image); tall R in V1–V2"),
        ],
        "note": "Clinical pearl: New LBBB with chest pain may be STEMI equivalent (Sgarbossa criteria). Always compare with prior ECGs.",
        "images": ["stemi_anterior", "stemi_inferior"],
        "img_captions": [
            "Anterolateral STEMI: tombstone ST elevation V1–V5, hyperacute T-waves, reciprocal inferior depression.",
            "Inferior STEMI: ST elevation in II, III, aVF with classic reciprocal changes in I and aVL."
        ],
    },
    {
        "num": "02", "name": "Atrial Fibrillation",
        "abbr": "AF",
        "subtitle": "Most common sustained arrhythmia β€” chaotic atrial electrical activity",
        "ecg_rows": [
            ("P waves",          "Absent β€” replaced by irregular fibrillatory (f) waves"),
            ("f waves",          "Rapid, irregular baseline oscillations (best in V1 and II); rate 350–600/min"),
            ("RR intervals",     "Irregularly irregular β€” hallmark finding"),
            ("QRS morphology",   "Narrow unless aberrant conduction or WPW coexists"),
            ("Ventricular rate", "Variable: 100–160 bpm uncontrolled; can be slower if treated or AV block present"),
        ],
        "note": "AF with regular RR intervals suggests complete AV block with junctional/ventricular escape β€” a dangerous combination requiring urgent pacing assessment.",
        "images": ["af"],
        "img_captions": [
            "Classic AF: absent P waves, fibrillatory baseline, and irregularly irregular ventricular response."
        ],
    },
    {
        "num": "03", "name": "Ventricular Tachycardia",
        "abbr": "VT",
        "subtitle": "β‰₯3 consecutive ventricular beats at rate β‰₯100 bpm β€” medical emergency",
        "ecg_rows": [
            ("Rate",             "100–250 bpm; regular rhythm"),
            ("QRS duration",     ">120 ms; bizarre, wide morphology"),
            ("AV dissociation",  "P waves march independently from QRS (pathognomonic when visible)"),
            ("Fusion beats",     "Normal + ectopic QRS hybrid β€” diagnostic of VT"),
            ("Capture beats",    "Narrow QRS within VT β€” confirms AV dissociation"),
            ("Concordance",      "All precordial QRS complexes positive (or all negative) β€” strong VT indicator"),
            ("Axis",             "Extreme right or northwest axis common"),
        ],
        "note": "Treat all wide-complex tachycardias as VT until proven otherwise. Haemodynamic instability β†’ immediate DC cardioversion.",
        "images": ["vt"],
        "img_captions": [
            "Monomorphic VT: regular wide QRS tachycardia, no visible P waves, positive concordance in precordial leads."
        ],
    },
    {
        "num": "04", "name": "Complete (3rd Degree) AV Block",
        "abbr": "CHB",
        "subtitle": "Total failure of AV conduction β€” atria and ventricles beat independently",
        "ecg_rows": [
            ("AV dissociation",   "P waves and QRS complexes completely independent (P 'marches through' QRS/T)"),
            ("Atrial rate",       "Normal (60–100 bpm); regular P-P intervals"),
            ("Ventricular escape","Slow: 40–60 bpm if junctional; 20–40 bpm if ventricular"),
            ("QRS morphology",    "Narrow if junctional escape; wide (>120 ms) if infra-Hisian escape"),
            ("PR interval",       "No fixed PR relationship β€” varies randomly"),
        ],
        "note": "Complete heart block is a pacemaker indication. Causes: inferior MI (usually transient), Lyme disease, drug toxicity (digoxin, beta-blockers), congenital, calcific degeneration.",
        "images": ["chb"],
        "img_captions": [
            "Complete AV block: independent P waves and wide ventricular escape complexes with no fixed PR relationship."
        ],
    },
    {
        "num": "05", "name": "Left Bundle Branch Block",
        "abbr": "LBBB",
        "subtitle": "Delayed LV depolarization β€” may mask or mimic myocardial infarction",
        "ecg_rows": [
            ("QRS duration",     "β‰₯120 ms (complete LBBB); 110–119 ms (incomplete LBBB)"),
            ("V1",               "Broad, deep rS or QS pattern (predominantly negative deflection)"),
            ("V5, V6, I, aVL",   "Broad, notched monophasic R wave ('W' in V1, 'M' in V6 β€” William mnemonic)"),
            ("Septal Q waves",   "Absent in I, V5, V6 (abnormal septal depolarization)"),
            ("ST/T changes",     "Discordant: ST-T direction opposite to main QRS deflection (secondary changes)"),
            ("Axis",             "Normal or left axis deviation"),
        ],
        "note": "Sgarbossa criteria for STEMI in LBBB: (1) concordant ST elevation β‰₯1 mm in any lead [5 pts], (2) concordant ST depression β‰₯1 mm in V1–V3 [3 pts], (3) discordant ST elevation β‰₯5 mm [2 pts]. Score β‰₯3 = STEMI.",
        "images": ["lbbb"],
        "img_captions": [
            "Complete LBBB: broad QRS, deep rS in V1, monophasic notched R waves laterally, discordant ST-T changes."
        ],
    },
    {
        "num": "06", "name": "Hypertrophic Cardiomyopathy",
        "abbr": "HCM",
        "subtitle": "Asymmetric LV hypertrophy β€” leading cause of SCD in young athletes",
        "ecg_rows": [
            ("LVH voltage",      "Sokolow-Lyon: S(V1) + R(V5/V6) β‰₯35 mm; Cornell: R(aVL) + S(V3) β‰₯28 mm (men)"),
            ("Giant T inversions","Deep, symmetric T-wave inversions V2–V5 β€” classic apical HCM (Yamaguchi)"),
            ("Strain pattern",   "Downsloping ST depression with asymmetric T-wave inversion in lateral leads"),
            ("Absent septal Q",  "No septal Q waves in I, aVL, V5, V6 (abnormal septal hypertrophy)"),
            ("Axis",             "Left axis deviation common"),
            ("Arrhythmias",      "AF, non-sustained VT, Wolff-Parkinson-White pattern possible"),
        ],
        "note": "80% of HCM patients have an abnormal ECG. ECG abnormalities often precede symptom onset. Annual ECG monitoring is standard in known HCM.",
        "images": ["hcm"],
        "img_captions": [
            "HCM (septal variant): high-voltage QRS, ST depression, deep T-wave inversions V2–V6, absent septal Q waves."
        ],
    },
    {
        "num": "07", "name": "Wolff-Parkinson-White Syndrome",
        "abbr": "WPW",
        "subtitle": "Accessory pathway bypasses AV node β€” ventricular pre-excitation",
        "ecg_rows": [
            ("Short PR interval", "<120 ms β€” AV node delay bypassed by accessory pathway"),
            ("Delta wave",        "Slurred upstroke at start of QRS β€” slow conduction through ventricular myocardium"),
            ("Wide QRS",          ">120 ms β€” fusion of delta pre-excitation and normal conduction"),
            ("ST-T changes",      "Secondary discordant ST-T changes (opposite main QRS direction)"),
            ("Pseudo-Q waves",    "Negative delta waves in some leads may mimic infarction Q waves"),
            ("Tachyarrhythmia",   "AVRT (most common); AF with rapid ventricular response (dangerous β€” may cause VF)"),
        ],
        "note": "NEVER give AV nodal blocking agents (digoxin, verapamil, diltiazem, adenosine) in WPW with AF β€” may accelerate conduction via accessory pathway causing VF. Use procainamide or DC cardioversion.",
        "images": ["wpw"],
        "img_captions": [
            "WPW: classic triad of short PR (<120 ms), delta wave (slurred QRS upstroke), and QRS widening with secondary ST-T changes."
        ],
    },
    {
        "num": "08", "name": "Acute Pericarditis",
        "abbr": "Pericarditis",
        "subtitle": "Pericardial inflammation β€” classically viral; evolves through 4 ECG stages",
        "ecg_rows": [
            ("Stage 1 (acute)",   "Diffuse concave ('saddle-shaped') ST elevation in most leads; PR depression"),
            ("Stage 2",           "ST normalises; T-waves flatten"),
            ("Stage 3",           "Diffuse T-wave inversions develop"),
            ("Stage 4",           "ECG normalises (weeks to months)"),
            ("PR depression",     "Most specific sign β€” best seen in lead II; reciprocal PR elevation in aVR"),
            ("Spodick's sign",    "Downsloping TP segment β€” highly specific for pericarditis"),
            ("aVR",               "Reciprocal ST depression and PR elevation"),
        ],
        "note": "Key STEMI vs pericarditis differentiators: (1) concave not convex ST, (2) diffuse not territorial distribution, (3) PR depression, (4) no Q waves, (5) no true reciprocal depression (except aVR).",
        "images": ["pericarditis"],
        "img_captions": [
            "Acute pericarditis Stage 1: diffuse saddle-shaped ST elevation, PR depression in II and most leads, PR elevation in aVR."
        ],
    },
    {
        "num": "09", "name": "Pulmonary Embolism",
        "abbr": "PE",
        "subtitle": "Pulmonary vascular obstruction β†’ acute right heart strain",
        "ecg_rows": [
            ("Sinus tachycardia", "Most common ECG finding (~44% of cases)"),
            ("S1Q3T3",            "Prominent S in lead I + Q wave in lead III + T-wave inversion in lead III"),
            ("RV strain",         "T-wave inversions V1–V4 (right precordial leads)"),
            ("RBBB",              "Incomplete (or complete) right bundle branch block due to RV dilation"),
            ("Right axis deviation","Shift of QRS axis rightward"),
            ("P pulmonale",       "Peaked P waves >2.5 mm in lead II β€” right atrial strain"),
            ("Normal ECG",        "Present in ~30% of PE cases β€” does NOT exclude diagnosis"),
        ],
        "note": "S1Q3T3 has ~20% sensitivity but high specificity. The most common ECG finding is sinus tachycardia. ECG is adjunctive β€” CT pulmonary angiography remains the gold standard for diagnosis.",
        "images": ["pe"],
        "img_captions": [
            "Acute PE: classic S1Q3T3 pattern (annotated circles) with sinus tachycardia and RV strain pattern in precordial leads."
        ],
    },
    {
        "num": "10", "name": "Long QT Syndrome",
        "abbr": "LQTS",
        "subtitle": "Prolonged ventricular repolarization β†’ Torsades de Pointes β†’ sudden death",
        "ecg_rows": [
            ("QTc interval",      "Corrected QT (Bazett: QT/√RR): >440 ms men, >460 ms women; HIGH RISK >500 ms"),
            ("T-wave morphology", "LQTS1: broad-based; LQTS2: bifid/notched 'hump'; LQTS3: late-onset narrow T"),
            ("U waves",           "Prominent U waves (especially LQTS3/drug-induced)"),
            ("R-on-T phenomenon", "PVC landing on T-wave triggers Torsades de Pointes"),
            ("Torsades de Pointes","Polymorphic VT with characteristic 'twisting' QRS morphology around isoelectric line"),
            ("Bradycardia",       "Relative or absolute bradycardia may unmask QT prolongation"),
        ],
        "note": "Common drug causes: antiarrhythmics (amiodarone, sotalol), antibiotics (azithromycin, fluoroquinolones), antipsychotics (haloperidol, quetiapine), antiemetics (ondansetron, domperidone). Check QTc before prescribing.",
        "images": ["lqts"],
        "img_captions": [
            "Long QT Syndrome: Panel A shows marked QTc prolongation (680 ms) with prominent U waves; Panel B shows R-on-T initiation of polymorphic VT (Torsades de Pointes). [Harrison's Principles of Internal Medicine]"
        ],
    },
]

# ── Summary table data ────────────────────────────────────────────────────────
SUMMARY_ROWS = [
    ["#", "Disease", "Key ECG Finding", "Emergency Action"],
    ["01", "STEMI",         "Convex ST elevation + Q waves",           "PCI within 90 min"],
    ["02", "Atrial Fibrillation", "No P waves; irregularly irregular RR",    "Rate control / anticoagulation"],
    ["03", "Ventricular Tachycardia", "Wide QRS tachy + AV dissociation",   "DC cardioversion if unstable"],
    ["04", "Complete Heart Block", "P & QRS dissociated; slow escape",       "Temporary/permanent pacemaker"],
    ["05", "LBBB",          "QRS β‰₯120 ms; rS V1; R in V6",              "Rule out STEMI (Sgarbossa)"],
    ["06", "HCM",           "LVH + giant T inversions",                  "Beta-blocker; ICD if high risk"],
    ["07", "WPW",           "Short PR + delta wave + wide QRS",          "Avoid AV nodal blockers in AF"],
    ["08", "Pericarditis",  "Diffuse concave ST + PR depression",         "NSAIDs + colchicine"],
    ["09", "Pulmonary Embolism", "S1Q3T3 + sinus tachycardia",            "Anticoagulation; thrombolysis"],
    ["10", "Long QT Syndrome", "QTc >500 ms β†’ Torsades de Pointes",      "Stop offending drugs; Mg IV"],
]


# ── Build PDF ─────────────────────────────────────────────────────────────────
def build_pdf():
    print("Building PDF...")
    styles = build_styles()
    doc = SimpleDocTemplate(
        OUTPUT_PATH,
        pagesize=A4,
        leftMargin=20*mm, rightMargin=20*mm,
        topMargin=22*mm, bottomMargin=24*mm,
        title="ECG Quick Reference Guide",
        author="Orris Medical AI",
        subject="10 Common Heart Diseases & ECG Patterns",
    )

    story = []
    W = 170*mm  # usable width

    # ── COVER PAGE ─────────────────────────────────────────────────────────
    # Full-page dark blue background via a big table
    cover_bg = Table(
        [[
            Paragraph("❀", ParagraphStyle("h", fontName="Helvetica-Bold", fontSize=48,
                                           textColor=MEDIUM_RED, alignment=TA_CENTER)),
        ],
        [Paragraph("ECG Quick Reference Guide", styles["cover_title"])],
        [Paragraph("10 Common Heart Diseases &amp; Their ECG Patterns", styles["cover_sub"])],
        [Spacer(1, 8*mm)],
        [Paragraph("A comprehensive visual guide for clinicians, residents &amp; medical students",
                   styles["cover_byline"])],
        [Spacer(1, 5*mm)],
        [HRFlowable(width=80*mm, thickness=1, color=HexColor("#5C6BC0"), spaceAfter=8)],
        [Paragraph("Orris Medical AI  β€’  May 2026", styles["cover_byline"])],
        ],
        colWidths=[W]
    )
    cover_bg.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
        ("ALIGN",      (0,0), (-1,-1), "CENTER"),
        ("VALIGN",     (0,0), (-1,-1), "MIDDLE"),
        ("TOPPADDING", (0,0), (-1,-1), 6),
        ("BOTTOMPADDING",(0,0),(-1,-1), 6),
        ("BOX",        (0,0), (-1,-1), 3, MEDIUM_RED),
        ("LEFTPADDING",(0,0), (-1,-1), 20),
        ("RIGHTPADDING",(0,0),(-1,-1), 20),
    ]))
    story.append(Spacer(1, 30*mm))
    story.append(cover_bg)
    story.append(PageBreak())

    # ── TABLE OF CONTENTS ──────────────────────────────────────────────────
    story.append(Spacer(1, 8*mm))
    story.append(Paragraph("Table of Contents", ParagraphStyle(
        "toc_h", fontName="Helvetica-Bold", fontSize=16, textColor=DARK_BLUE,
        spaceAfter=6, alignment=TA_CENTER)))
    story.append(HRFlowable(width=W, thickness=2, color=DARK_RED, spaceAfter=8))

    toc_data = [
        ["#", "Disease", "Abbr."],
    ] + [[d["num"], d["name"], d["abbr"]] for d in DISEASES] + [
        ["", "Quick Reference Summary Table", ""],
    ]
    toc_t = Table(toc_data, colWidths=[15*mm, 125*mm, 30*mm])
    toc_t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0),  (-1,0),  DARK_BLUE),
        ("TEXTCOLOR",    (0,0),  (-1,0),  WHITE),
        ("FONTNAME",     (0,0),  (-1,0),  "Helvetica-Bold"),
        ("FONTSIZE",     (0,0),  (-1,0),  9),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [LIGHT_GREY, WHITE]),
        ("FONTNAME",     (0,1),  (0,-1),  "Helvetica-Bold"),
        ("TEXTCOLOR",    (0,1),  (0,-1),  MEDIUM_RED),
        ("FONTNAME",     (1,1),  (1,-1),  "Helvetica"),
        ("FONTSIZE",     (0,1),  (-1,-1), 9),
        ("GRID",         (0,0),  (-1,-1), 0.4, HexColor("#BDBDBD")),
        ("TOPPADDING",   (0,0),  (-1,-1), 5),
        ("BOTTOMPADDING",(0,0),  (-1,-1), 5),
        ("LEFTPADDING",  (0,0),  (-1,-1), 8),
        ("BACKGROUND",   (0,-1), (-1,-1), LIGHT_BLUE),
        ("FONTNAME",     (0,-1), (-1,-1), "Helvetica-Bold"),
        ("TEXTCOLOR",    (0,-1), (-1,-1), DARK_BLUE),
    ]))
    story.append(toc_t)
    story.append(PageBreak())

    # ── DISEASE SECTIONS ───────────────────────────────────────────────────
    for d in DISEASES:
        print(f"  Processing disease {d['num']}: {d['abbr']}...")
        story.append(KeepTogether([
            header_banner(d["num"], d["name"], d["subtitle"], styles, W),
            Spacer(1, 4*mm),
        ]))

        # ECG changes table
        story.append(Paragraph("ECG Changes", styles["bold_label"]))
        story.append(Spacer(1, 2*mm))
        story.append(ecg_changes_table(d["ecg_rows"], styles, W))
        story.append(Spacer(1, 3*mm))

        # Localisation table (STEMI only)
        if "localisation" in d:
            story.append(Paragraph("Localisation by Territory", styles["bold_label"]))
            story.append(Spacer(1, 2*mm))
            loc_data = [[Paragraph("<b>Territory</b>", styles["bullet"]),
                         Paragraph("<b>Leads Affected</b>", styles["bullet"])]]
            for terr, leads in d["localisation"]:
                loc_data.append([Paragraph(terr, styles["bullet"]), Paragraph(leads, styles["bullet"])])
            loc_t = Table(loc_data, colWidths=[52*mm, W-52*mm])
            loc_t.setStyle(TableStyle([
                ("BACKGROUND",   (0,0),(-1,0),  MEDIUM_BLUE),
                ("TEXTCOLOR",    (0,0),(-1,0),  WHITE),
                ("ROWBACKGROUNDS",(0,1),(-1,-1),[LIGHT_BLUE, WHITE]),
                ("GRID",         (0,0),(-1,-1), 0.4, HexColor("#BDBDBD")),
                ("TOPPADDING",   (0,0),(-1,-1), 4),
                ("BOTTOMPADDING",(0,0),(-1,-1), 4),
                ("LEFTPADDING",  (0,0),(-1,-1), 6),
                ("VALIGN",       (0,0),(-1,-1), "TOP"),
            ]))
            story.append(loc_t)
            story.append(Spacer(1, 3*mm))

        # Clinical note
        story.append(info_box(f"<b>Clinical Pearl:</b> {d['note']}", styles,
                               bg=LIGHT_GREEN, border=ACCENT_GREEN))
        story.append(Spacer(1, 4*mm))

        # ECG images
        story.append(Paragraph("Representative ECG", styles["bold_label"]))
        story.append(Spacer(1, 2*mm))

        img_items = []
        for key, cap in zip(d["images"], d["img_captions"]):
            url = IMAGE_URLS[key]
            rl_img = download_image(url, max_size=(900, 500))
            if rl_img:
                # Constrain height so it doesn't overflow
                max_h = 55*mm
                if rl_img.drawHeight > max_h:
                    scale = max_h / rl_img.drawHeight
                    rl_img.drawHeight = max_h
                    rl_img.drawWidth  = rl_img.drawWidth * scale
                img_items.append([rl_img, Paragraph(cap, styles["caption"])])
            else:
                img_items.append([
                    Paragraph(f"[Image unavailable: {key}]", styles["caption"]),
                    Paragraph(cap, styles["caption"])
                ])

        if len(img_items) == 2:
            # Two images side by side
            row_data = [[img_items[0][0], img_items[1][0]],
                        [img_items[0][1], img_items[1][1]]]
            img_t = Table(row_data, colWidths=[W/2 - 2*mm, W/2 - 2*mm])
            img_t.setStyle(TableStyle([
                ("ALIGN",  (0,0),(-1,-1), "CENTER"),
                ("VALIGN", (0,0),(-1,-1), "TOP"),
                ("TOPPADDING",   (0,0),(-1,-1), 2),
                ("BOTTOMPADDING",(0,0),(-1,-1), 2),
            ]))
            story.append(img_t)
        else:
            for img_el, cap_el in img_items:
                t = Table([[img_el], [cap_el]], colWidths=[W])
                t.setStyle(TableStyle([
                    ("ALIGN",  (0,0),(-1,-1), "CENTER"),
                    ("TOPPADDING",   (0,0),(-1,-1), 2),
                    ("BOTTOMPADDING",(0,0),(-1,-1), 2),
                ]))
                story.append(t)

        story.append(PageBreak())

    # ── QUICK REFERENCE SUMMARY TABLE ─────────────────────────────────────
    story.append(Spacer(1, 6*mm))
    story.append(Paragraph("Quick Reference Summary Table", ParagraphStyle(
        "sum_h", fontName="Helvetica-Bold", fontSize=14, textColor=DARK_BLUE,
        spaceAfter=4, alignment=TA_CENTER)))
    story.append(Paragraph("10 Heart Diseases at a Glance", ParagraphStyle(
        "sum_sub", fontName="Helvetica-Oblique", fontSize=9,
        textColor=MID_GREY, spaceAfter=8, alignment=TA_CENTER)))
    story.append(HRFlowable(width=W, thickness=2, color=DARK_RED, spaceAfter=6))

    sum_data = []
    for i, row in enumerate(SUMMARY_ROWS):
        sum_data.append([Paragraph(cell, ParagraphStyle(
            f"s{i}", fontName="Helvetica-Bold" if i==0 else "Helvetica",
            fontSize=8.5, textColor=WHITE if i==0 else DARK_GREY, leading=12
        )) for cell in row])

    sum_t = Table(sum_data, colWidths=[10*mm, 48*mm, 65*mm, 47*mm], repeatRows=1)
    sum_t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,0),   DARK_RED),
        ("TEXTCOLOR",    (0,0), (-1,0),   WHITE),
        ("ROWBACKGROUNDS",(0,1),(-1,-1),  [LIGHT_GREY, WHITE]),
        ("GRID",         (0,0), (-1,-1),  0.4, HexColor("#BDBDBD")),
        ("TOPPADDING",   (0,0), (-1,-1),  5),
        ("BOTTOMPADDING",(0,0), (-1,-1),  5),
        ("LEFTPADDING",  (0,0), (-1,-1),  6),
        ("VALIGN",       (0,0), (-1,-1),  "TOP"),
        ("FONTNAME",     (0,1), (0,-1),   "Helvetica-Bold"),
        ("TEXTCOLOR",    (0,1), (0,-1),   MEDIUM_RED),
        # Alternate deeper blue on even rows
        ("BACKGROUND",   (0,2), (-1,2),   LIGHT_BLUE),
        ("BACKGROUND",   (0,4), (-1,4),   LIGHT_BLUE),
        ("BACKGROUND",   (0,6), (-1,6),   LIGHT_BLUE),
        ("BACKGROUND",   (0,8), (-1,8),   LIGHT_BLUE),
        ("BACKGROUND",   (0,10),(-1,10),  LIGHT_BLUE),
    ]))
    story.append(sum_t)

    story.append(Spacer(1, 8*mm))
    story.append(info_box(
        "<b>Disclaimer:</b> This guide is intended as a quick clinical reference tool for educational purposes. "
        "ECG interpretation should always be combined with full clinical assessment, patient history, and "
        "appropriate investigations. ECG images sourced from peer-reviewed PMC publications.",
        styles, bg=LIGHT_RED, border=DARK_RED
    ))

    # ── Build ──────────────────────────────────────────────────────────────
    print("Assembling PDF...")
    doc.build(story, canvasmaker=NumberedCanvas)
    print(f"Done! Saved to: {OUTPUT_PATH}")


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

cd /home/daytona/workspace/ecg-guide && python3 create_ecg_pdf.py 2>&1

Your credits are exhausted. Your daily credits reset tomorrow, or you can purchase a top-up for more.

Download the ECG reference PDF from workspace

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.