Give notes on mi
"myocardial infarction"[MeSH Terms] AND "management"[MeSH Terms]
Key fact: When angiography is performed within 4 hours of onset, coronary thrombosis is demonstrable in almost 90% of cases. By 12-24 hours, this falls to only ~60% (some thrombi clear spontaneously via lysis or spasm relaxation).
| Time | Event |
|---|---|
| Seconds | Cessation of aerobic metabolism; inadequate ATP production; accumulation of lactic acid |
| Within ~1 minute | Loss of myocardial contractility |
| Minutes | Ultrastructural changes: myofibrillar relaxation, glycogen depletion, cell/mitochondrial swelling - still reversible |
| 20-30 min of severe ischemia (flow ≤10% normal) | Irreversible necrosis begins |
| 6-12 hours | Progressive loss of viability becomes complete |
"The benefits of reperfusion are greatest when it is achieved quickly." - Robbins
| Artery | Frequency | Area Infarcted |
|---|---|---|
| LAD (Left Anterior Descending) | 40-50% | Anterior wall of LV near apex; anterior ventricular septum; apex circumferentially |
| RCA (Right Coronary Artery) | 30-40% | Inferior/posterior wall of LV; posterior ventricular septum; inferior/posterior RV free wall |
| LCX (Left Circumflex) | 15-20% | Lateral wall of LV (except apex) |
| Time | Gross Changes | Microscopic Changes |
|---|---|---|
| 0-4 hours | None (unstained pale zone on TTC staining from ~2-3 hrs) | None visible on H&E |
| 4-12 hours | Reddish-blue (congestion) | Early coagulative necrosis; "wavy fibers" (non-infarcted myocardium pulling on necrotic fibers) |
| 12-24 hours | Reddish-blue discoloration | Coagulative necrosis with pyknosis of nuclei; marginal contraction band necrosis |
| 1-3 days | Yellow-tan softening | Coagulative necrosis; neutrophilic infiltration begins |
| 3-7 days | Rimmed by a hyperemic zone (yellow center, red-brown border) | Dense neutrophilic infiltration; early macrophage infiltration |
| 7-10 days | Maximally soft; yellow-tan; depressed border | Macrophages prominent; granulation tissue at margins |
| 10-14 days | Pale gray depression with red-tan rim | Granulation tissue with collagen deposition |
| 2-8 weeks | Gray-white scar (at margins) | Increased collagen; reduced cellularity |
| >2 months | Firm white scar | Dense collagen scar; few/no cells |
| Defect in Infarcted Cells | Current Flow | ECG Change (leads over infarct) |
|---|---|---|
| Rapid repolarization (K+ channel opening) | Out of infarct | ST segment elevation |
| Decreased resting membrane potential (K+ loss) | Into infarct during diastole | TQ depression → recorded as ST elevation |
| Delayed depolarization | Out of infarct | ST segment elevation |
| Territory | ECG Leads |
|---|---|
| Anterior (LAD) | V1-V4 |
| Anterolateral (LAD + LCX) | V4-V6, I, aVL |
| Inferior (RCA) | II, III, aVF |
| Lateral (LCX) | I, aVL, V5-V6 |
| Posterior (RCA/LCX) | Tall R in V1-V2 (reciprocal) |
| Right ventricular (RCA) | V1, V3R-V4R |
| Marker | Rises | Peaks | Returns to Normal | Notes |
|---|---|---|---|---|
| Troponin I/T | 3-4 hrs | 24-48 hrs | 7-14 days | Most sensitive and specific; gold standard |
| CK-MB | 4-6 hrs | 18-24 hrs | 48-72 hrs | Used to detect re-infarction |
| Myoglobin | 1-2 hrs | 4-6 hrs | 12-24 hrs | First to rise, least specific |
| LDH | 8-12 hrs | 24-72 hrs | 7-10 days | Historical use; now rarely used |
Troponin is the preferred biomarker. High-sensitivity troponin (hs-cTnT/cTnI) allows diagnosis within 1-2 hours.
| Time | Complication |
|---|---|
| First 24-72 hours | Arrhythmias (most common cause of early death - VF); cardiogenic shock; acute LV failure/pulmonary edema |
| 2-3 days | Fibrinous/hemorrhagic pericarditis (Dressler syndrome early form) |
| 3-7 days | Free wall rupture (most common in days 3-7; tamponade); papillary muscle rupture (acute MR); interventricular septal defect |
| Days-weeks | Mural thrombus → systemic embolism; LV aneurysm (late complication - dilated, thinned scar) |
| Weeks-months | Dressler syndrome (autoimmune pericarditis, pleuritis, fever) |
| Drug | Reason |
|---|---|
| Aspirin + P2Y12 inhibitor (DAPT x 12 months) | Prevent stent thrombosis and recurrent MI |
| Beta-blocker | Reduce mortality; prevent arrhythmias; limit infarct size |
| ACE inhibitor / ARB | Prevent LV remodeling; reduce mortality esp. if EF reduced |
| Statin (high-intensity) | Plaque stabilization, LDL reduction |
| Aldosterone antagonist (eplerenone) | If EF ≤40% with HF or diabetes |
Create a quick reference PDF with ECG changes by MI location
ls -la /home/daytona/workspace/
mkdir -p /home/daytona/workspace/mi-ecg-ref
~/mi-ecg-ref/create_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.graphics.shapes import Drawing, Rect, Line, String, Circle, Polygon
from reportlab.graphics import renderPDF
from reportlab.platypus import Flowable
import math
# ─── Colour palette ────────────────────────────────────────────────────────────
RED = colors.HexColor("#C0392B")
DARK_RED = colors.HexColor("#922B21")
BLUE = colors.HexColor("#1A5276")
LIGHT_BLUE = colors.HexColor("#D6EAF8")
TEAL = colors.HexColor("#1ABC9C")
ORANGE = colors.HexColor("#E67E22")
PURPLE = colors.HexColor("#7D3C98")
GREEN = colors.HexColor("#1E8449")
LIGHT_GREEN= colors.HexColor("#D5F5E3")
LIGHT_RED = colors.HexColor("#FADBD8")
LIGHT_YELLOW = colors.HexColor("#FEF9E7")
YELLOW = colors.HexColor("#F39C12")
DARK_GRAY = colors.HexColor("#2C3E50")
MID_GRAY = colors.HexColor("#7F8C8D")
LIGHT_GRAY = colors.HexColor("#F2F3F4")
WHITE = colors.white
BLACK = colors.black
# ─── ECG Waveform Flowable ─────────────────────────────────────────────────────
class ECGWaveform(Flowable):
"""Draws a schematic ECG strip for a given infarct pattern."""
def __init__(self, pattern, width=120, height=55, color=RED):
super().__init__()
self.pattern = pattern # 'st_elevation', 'st_depression', 'q_wave', 'hyperacute_t', 'tall_r'
self.width = width
self.height = height
self.color = color
def wrap(self, *args):
return self.width, self.height
def draw(self):
c = self.canv
w, h = self.width, self.height
mid = h / 2
# Grid background
c.setFillColor(colors.HexColor("#FDF9F0"))
c.setStrokeColor(colors.HexColor("#F5CBA7"))
c.rect(0, 0, w, h, fill=1, stroke=1)
# Grid lines
c.setStrokeColor(colors.HexColor("#FADBD8"))
c.setLineWidth(0.4)
for x in range(0, int(w)+1, 10):
c.line(x, 0, x, h)
for y in range(0, int(h)+1, 10):
c.line(0, y, w, y)
# Draw ECG trace
c.setStrokeColor(self.color)
c.setLineWidth(1.8)
c.setLineCap(1)
p = self.pattern
path = c.beginPath()
# Baseline left
path.moveTo(0, mid)
path.lineTo(15, mid)
# P wave
path.curveTo(16, mid, 18, mid+6, 20, mid+6)
path.curveTo(22, mid+6, 24, mid, 25, mid)
# PR segment
path.lineTo(30, mid)
if p == 'st_elevation':
# QRS + elevated ST
path.lineTo(32, mid)
path.lineTo(33, mid - 6) # Q
path.lineTo(35, mid + 28) # R
path.lineTo(37, mid + 10) # S (elevated)
path.lineTo(40, mid + 12) # ST elevated
path.lineTo(55, mid + 12) # ST plateau elevated
path.curveTo(60, mid+12, 62, mid+22, 65, mid+22) # T peak
path.curveTo(68, mid+22, 72, mid+8, 76, mid)
path.lineTo(w, mid)
elif p == 'st_depression':
# QRS + depressed ST
path.lineTo(32, mid)
path.lineTo(33, mid - 5)
path.lineTo(35, mid + 26)
path.lineTo(37, mid - 12) # deep S
path.lineTo(40, mid - 10) # ST depressed
path.lineTo(55, mid - 9)
path.curveTo(58, mid-9, 60, mid-3, 63, mid-3) # inverted T
path.curveTo(66, mid-3, 70, mid, 75, mid)
path.lineTo(w, mid)
elif p == 'q_wave':
# Pathological Q wave + normal ST
path.lineTo(31, mid)
path.lineTo(32, mid - 18) # deep Q
path.lineTo(34, mid)
path.lineTo(35, mid + 22) # small R
path.lineTo(37, mid - 2)
path.lineTo(40, mid)
path.lineTo(58, mid)
path.curveTo(60, mid, 62, mid+10, 65, mid+10)
path.curveTo(68, mid+10, 72, mid, 76, mid)
path.lineTo(w, mid)
elif p == 'hyperacute_t':
# Normal QRS + tall peaked T
path.lineTo(32, mid)
path.lineTo(33, mid - 5)
path.lineTo(35, mid + 25)
path.lineTo(37, mid - 4)
path.lineTo(40, mid)
path.lineTo(50, mid)
path.curveTo(52, mid, 55, mid+32, 58, mid+32) # very tall T
path.curveTo(61, mid+32, 65, mid, 70, mid)
path.lineTo(w, mid)
elif p == 'tall_r':
# Tall R in V1 (posterior MI pattern)
path.lineTo(32, mid)
path.lineTo(33, mid + 2) # no Q
path.lineTo(36, mid + 30) # tall R
path.lineTo(38, mid + 4) # small S
path.lineTo(40, mid)
path.lineTo(52, mid)
path.curveTo(54, mid, 57, mid+14, 60, mid+14) # upright T
path.curveTo(63, mid+14, 68, mid, 73, mid)
path.lineTo(w, mid)
elif p == 'normal':
path.lineTo(32, mid)
path.lineTo(33, mid - 4)
path.lineTo(35, mid + 22)
path.lineTo(37, mid - 3)
path.lineTo(40, mid)
path.lineTo(52, mid)
path.curveTo(54, mid, 57, mid+10, 60, mid+10)
path.curveTo(63, mid+10, 67, mid, 72, mid)
path.lineTo(w, mid)
c.drawPath(path, stroke=1, fill=0)
# Label
c.setFont("Helvetica-Bold", 7)
c.setFillColor(self.color)
label_map = {
'st_elevation': 'ST Elevation',
'st_depression': 'ST Depression',
'q_wave': 'Pathological Q',
'hyperacute_t': 'Hyperacute T',
'tall_r': 'Tall R (V1)',
'normal': 'Normal'
}
c.drawCentredString(w/2, 3, label_map.get(self.pattern, ''))
# ─── Heart Diagram Flowable ────────────────────────────────────────────────────
class HeartZoneDiagram(Flowable):
"""Simple schematic heart with coloured zones."""
def __init__(self, highlight='anterior', width=130, height=110):
super().__init__()
self.highlight = highlight
self.width = width
self.height = height
def wrap(self, *args):
return self.width, self.height
def draw(self):
c = self.canv
w, h = self.width, self.height
zone_colors = {
'anterior': colors.HexColor("#E74C3C"),
'inferior': colors.HexColor("#E67E22"),
'lateral': colors.HexColor("#8E44AD"),
'posterior': colors.HexColor("#2980B9"),
'septal': colors.HexColor("#27AE60"),
'rv': colors.HexColor("#F39C12"),
}
zone_labels = {
'anterior': 'Anterior',
'inferior': 'Inferior',
'lateral': 'Lateral',
'posterior': 'Posterior',
'septal': 'Septal',
'rv': 'Right Ventricular',
}
cx, cy = w/2, h/2 + 5
rx, ry = 38, 32
# Base heart oval (LV)
c.setFillColor(colors.HexColor("#FDFEFE"))
c.setStrokeColor(DARK_GRAY)
c.setLineWidth(1.2)
c.ellipse(cx - rx, cy - ry, cx + rx, cy + ry, fill=1, stroke=1)
# Highlight zone
hc = zone_colors.get(self.highlight, RED)
c.setFillColor(hc)
c.setStrokeColor(hc)
c.setLineWidth(0)
if self.highlight == 'anterior':
# top-left arc segment
c.wedge(cx - rx, cy - ry, cx + rx, cy + ry, 60, 120, fill=1, stroke=0)
elif self.highlight == 'inferior':
c.wedge(cx - rx, cy - ry, cx + rx, cy + ry, 240, 80, fill=1, stroke=0)
elif self.highlight == 'lateral':
c.wedge(cx - rx, cy - ry, cx + rx, cy + ry, 150, 90, fill=1, stroke=0)
elif self.highlight == 'posterior':
c.wedge(cx - rx, cy - ry, cx + rx, cy + ry, 260, 60, fill=1, stroke=0)
elif self.highlight == 'septal':
# inner right strip
c.rect(cx - 8, cy - ry + 4, 14, ry*2 - 8, fill=1, stroke=0)
elif self.highlight == 'rv':
c.ellipse(cx + rx - 4, cy - 18, cx + rx + 22, cy + 18, fill=1, stroke=0)
# Redraw heart border
c.setFillColor(colors.Color(0, 0, 0, 0))
c.setStrokeColor(DARK_GRAY)
c.setLineWidth(1.5)
c.ellipse(cx - rx, cy - ry, cx + rx, cy + ry, fill=0, stroke=1)
# Septum line
c.setStrokeColor(colors.HexColor("#7F8C8D"))
c.setLineWidth(0.8)
c.setDash([3, 2])
c.line(cx, cy - ry, cx, cy + ry)
c.setDash([])
# RV outline
c.setStrokeColor(DARK_GRAY)
c.setLineWidth(1)
c.ellipse(cx + rx - 4, cy - 18, cx + rx + 22, cy + 18, fill=0, stroke=1)
# Labels
c.setFont("Helvetica-Bold", 6.5)
c.setFillColor(DARK_GRAY)
c.drawCentredString(cx - 14, cy + ry + 4, "ANT")
c.drawCentredString(cx - 14, cy - ry - 9, "INF")
c.setFont("Helvetica", 6)
c.drawCentredString(cx - rx - 10, cy, "LAT")
c.drawCentredString(cx + 6, cy + 5, "SEPT")
c.drawCentredString(cx + rx + 12, cy, "RV")
# Zone label below
c.setFont("Helvetica-Bold", 8)
c.setFillColor(hc)
c.drawCentredString(w/2, 3, zone_labels.get(self.highlight, ''))
# ─── Build Document ─────────────────────────────────────────────────────────────
def build_pdf(output_path):
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
rightMargin=15*mm,
leftMargin=15*mm,
topMargin=12*mm,
bottomMargin=12*mm,
)
styles = getSampleStyleSheet()
# Custom styles
title_style = ParagraphStyle(
'Title', parent=styles['Normal'],
fontName='Helvetica-Bold', fontSize=20,
textColor=WHITE, alignment=TA_CENTER,
spaceAfter=2,
)
subtitle_style = ParagraphStyle(
'Subtitle', parent=styles['Normal'],
fontName='Helvetica', fontSize=10,
textColor=colors.HexColor("#AED6F1"), alignment=TA_CENTER,
spaceAfter=0,
)
section_title = ParagraphStyle(
'SectionTitle', parent=styles['Normal'],
fontName='Helvetica-Bold', fontSize=12,
textColor=WHITE, alignment=TA_LEFT,
leftIndent=4,
)
lead_style = ParagraphStyle(
'Lead', parent=styles['Normal'],
fontName='Helvetica-Bold', fontSize=8.5,
textColor=DARK_GRAY,
)
body_style = ParagraphStyle(
'Body', parent=styles['Normal'],
fontName='Helvetica', fontSize=8,
textColor=DARK_GRAY, leading=11,
)
small_style = ParagraphStyle(
'Small', parent=styles['Normal'],
fontName='Helvetica', fontSize=7,
textColor=MID_GRAY, leading=10,
)
note_style = ParagraphStyle(
'Note', parent=styles['Normal'],
fontName='Helvetica-Oblique', fontSize=7.5,
textColor=colors.HexColor("#5D6D7E"), leading=11,
)
story = []
# ── Header Banner ──────────────────────────────────────────────────────────
header_data = [[
Paragraph("🫀 ECG Changes by MI Location", title_style),
]]
header_table = Table(header_data, colWidths=[180*mm])
header_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), BLUE),
('ROUNDEDCORNERS', [6, 6, 6, 6]),
('TOPPADDING', (0,0), (-1,-1), 10),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
]))
story.append(header_table)
sub_data = [[Paragraph("Quick Reference Card • Acute Myocardial Infarction • ECG Localization", subtitle_style)]]
sub_table = Table(sub_data, colWidths=[180*mm])
sub_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), DARK_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
]))
story.append(sub_table)
story.append(Spacer(1, 5*mm))
# ── ECG Evolution Banner ───────────────────────────────────────────────────
evo_title = [[Paragraph(" ECG Evolution in STEMI", section_title)]]
evo_t = Table(evo_title, colWidths=[180*mm])
evo_t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), RED),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('ROUNDEDCORNERS', [4,4,4,4]),
]))
story.append(evo_t)
story.append(Spacer(1, 2*mm))
# Evolution table
evo_headers = ["Phase", "Timing", "ECG Finding", "Mechanism"]
evo_rows = [
evo_headers,
["Hyperacute", "0 – 30 min\n(often missed)", "Tall, broad, peaked\nT waves", "Intracellular K+ release\ncauses slow repolarization"],
["Acute / Injury", "Min to hrs", "ST elevation ≥1 mm\n(≥2 mm in V1–V3)", "Current of injury —\nthree membrane defects"],
["Evolving", "Hrs – 2 days", "T wave inversion;\nST normalising", "Repolarisation changes\nover necrotic zone"],
["Established", "Days – weeks", "Pathological Q waves\n(≥40 ms / ≥25% R)", "Electrically silent\nnecrotic tissue"],
["Old / Healed", "> 6 weeks", "Persistent Q waves;\nST/T may normalise", "Fibrotic scar;\nno electrical activity"],
]
evo_col = [35*mm, 32*mm, 60*mm, 53*mm]
evo_table = Table(evo_rows, colWidths=evo_col, repeatRows=1)
evo_table.setStyle(TableStyle([
# Header
('BACKGROUND', (0,0), (-1,0), DARK_GRAY),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('ALIGN', (0,0), (-1,0), 'CENTER'),
# Alternating rows
('BACKGROUND', (0,1), (-1,1), LIGHT_RED),
('BACKGROUND', (0,2), (-1,2), LIGHT_GRAY),
('BACKGROUND', (0,3), (-1,3), LIGHT_RED),
('BACKGROUND', (0,4), (-1,4), LIGHT_GRAY),
('BACKGROUND', (0,5), (-1,5), LIGHT_RED),
# All cells
('FONTNAME', (0,1), (-1,-1), 'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('ALIGN', (0,0), (0,-1), 'CENTER'),
('ALIGN', (1,0), (1,-1), 'CENTER'),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#CACFD2")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
# Bold first col
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1), (0,-1), RED),
]))
story.append(evo_table)
story.append(Spacer(1, 5*mm))
# ── Localization Table ─────────────────────────────────────────────────────
loc_title = [[Paragraph(" ECG Localization of MI", section_title)]]
loc_t = Table(loc_title, colWidths=[180*mm])
loc_t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), BLUE),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('ROUNDEDCORNERS', [4,4,4,4]),
]))
story.append(loc_t)
story.append(Spacer(1, 2*mm))
loc_headers = ["MI Territory", "Culprit Artery", "ECG Leads Affected", "Key ECG Changes", "Clinical Pearls"]
loc_data = [
loc_headers,
[
"ANTERIOR",
"LAD\n(proximal)",
"V1, V2, V3, V4",
"ST↑ V1–V4\nPathological Q V1–V4\nReciprocal ST↓ II, III, aVF",
"Worst prognosis; large\nLV territory; risk of\nLV failure & VF"
],
[
"SEPTAL",
"LAD\n(septal perforators)",
"V1, V2",
"ST↑ V1–V2\nLoss of septal Q in\nV5–V6 (early sign)\nNew LBBB possible",
"Watch for AV block;\nBBB may mask diagnosis;\nnew LBBB = STEMI equivalent"
],
[
"ANTEROLATERAL",
"LAD + LCX\nor Diagonal",
"V4, V5, V6,\nI, aVL",
"ST↑ V4–V6, I, aVL\nPathological Q waves\nReciprocal ST↓ II, III, aVF",
"High lateral (I, aVL)\nmay be subtle;\ncheck for diagonal occlusion"
],
[
"INFERIOR",
"RCA (80%)\nor LCX (20%)",
"II, III, aVF",
"ST↑ II, III, aVF\n(III > II in RCA)\nReciprocal ST↓ I, aVL",
"Always check V3R/V4R\nfor RV involvement;\nbradycardia/AV block common"
],
[
"RIGHT VENTRICULAR",
"RCA (proximal,\nbefore RV branch)",
"V1, V3R, V4R,\nV5R, V6R",
"ST↑ V1 ≥1 mm\nST↑ V3R–V4R ≥1 mm\n(most sensitive sign)",
"AVOID nitrates (↓ preload);\nvolume loading critical;\nusually with inferior MI"
],
[
"LATERAL",
"LCX",
"I, aVL, V5, V6",
"ST↑ I, aVL, V5–V6\nReciprocal ST↓ V1–V2",
"May be subtle / missed;\ncheck posterior leads\n(V7–V9) if suspicious"
],
[
"POSTERIOR",
"RCA or LCX\n(dominant)",
"V1–V3\n(Reciprocal)\n+ V7, V8, V9",
"Tall R V1–V2 (≥0.04s)\nST↓ V1–V3 (≥0.5 mm)\nUpright T V1–V2\nST↑ V7–V9",
"Most commonly missed;\nalways check posterior\nleads; ST↑ in V7–V9\nconfirms diagnosis"
],
]
loc_col_w = [28*mm, 22*mm, 28*mm, 56*mm, 46*mm]
loc_table = Table(loc_data, colWidths=loc_col_w, repeatRows=1)
row_colors = [
colors.HexColor("#FDEDEC"), # anterior - red tint
colors.HexColor("#EBF5FB"), # septal - blue tint
colors.HexColor("#F4ECF7"), # anterolateral - purple tint
colors.HexColor("#FEF9E7"), # inferior - yellow tint
colors.HexColor("#E8F8F5"), # RV - teal tint
colors.HexColor("#F0F3FF"), # lateral - lavender
colors.HexColor("#FDFEFE"), # posterior - white
]
territory_colors = [
RED, BLUE, PURPLE, ORANGE, TEAL, colors.HexColor("#5B6BBA"), MID_GRAY
]
loc_table.setStyle(TableStyle([
# Header
('BACKGROUND', (0,0), (-1,0), DARK_GRAY),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8.5),
('ALIGN', (0,0), (-1,0), 'CENTER'),
# Row bgs
*[('BACKGROUND', (0, i+1), (-1, i+1), row_colors[i]) for i in range(7)],
# All cells
('FONTNAME', (0,1), (-1,-1), 'Helvetica'),
('FONTSIZE', (0,1), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#CACFD2")),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
# Territory col bold + colored
*[('FONTNAME', (0, i+1), (0, i+1), 'Helvetica-Bold') for i in range(7)],
*[('TEXTCOLOR', (0, i+1), (0, i+1), territory_colors[i]) for i in range(7)],
# Align territory
('ALIGN', (0,1), (0,-1), 'CENTER'),
('ALIGN', (1,1), (1,-1), 'CENTER'),
('ALIGN', (2,1), (2,-1), 'CENTER'),
]))
story.append(loc_table)
story.append(Spacer(1, 5*mm))
# ── ECG Waveforms Row ──────────────────────────────────────────────────────
wave_title = [[Paragraph(" Characteristic ECG Waveform Patterns", section_title)]]
wave_t = Table(wave_title, colWidths=[180*mm])
wave_t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), colors.HexColor("#117A65")),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('ROUNDEDCORNERS', [4,4,4,4]),
]))
story.append(wave_t)
story.append(Spacer(1, 2*mm))
wave_patterns = [
('hyperacute_t', 'Hyperacute T', TEAL, '0–30 min\nEarly STEMI'),
('st_elevation', 'ST Elevation', RED, 'Acute phase\nSTEMI hallmark'),
('st_depression','ST Depression', ORANGE, 'Reciprocal changes\nor NSTEMI'),
('q_wave', 'Pathological Q', PURPLE, 'Established\ninfarct'),
('tall_r', 'Tall R (V1)', BLUE, 'Posterior MI\nreciprocal'),
]
wform_cells = []
label_cells = []
for pat, lbl, col, desc in wave_patterns:
wform_cells.append(ECGWaveform(pat, width=120, height=55, color=col))
label_cells.append(Paragraph(f"<b><font color='#{col.hexval()[2:]}'>■</font> {lbl}</b><br/><font size='7'>{desc}</font>", body_style))
wave_row = Table(
[wform_cells, label_cells],
colWidths=[36*mm]*5,
)
wave_row.setStyle(TableStyle([
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 3),
('BOTTOMPADDING', (0,0), (-1,-1), 3),
('BACKGROUND', (0,0), (-1,-1), LIGHT_GRAY),
('GRID', (0,0), (-1,-1), 0.4, colors.HexColor("#D5D8DC")),
]))
story.append(wave_row)
story.append(Spacer(1, 5*mm))
# ── Quick Tip Boxes Row ────────────────────────────────────────────────────
tip_title = [[Paragraph(" High-Yield Clinical Pearls", section_title)]]
tip_t = Table(tip_title, colWidths=[180*mm])
tip_t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), ORANGE),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('ROUNDEDCORNERS', [4,4,4,4]),
]))
story.append(tip_t)
story.append(Spacer(1, 2*mm))
tips = [
("🔴 STEMI Definition",
"• ST↑ ≥1 mm in ≥2 contiguous limb leads\n"
"• ST↑ ≥2 mm in ≥2 contiguous precordial leads\n"
"• New LBBB with ischemic symptoms = STEMI equivalent\n"
"• Posterior ST↑ ≥0.5 mm in V7–V9",
LIGHT_RED, RED),
("🟠 Reciprocal Changes",
"• Inferior MI: ST↓ in I, aVL (reciprocal)\n"
"• Anterior MI: ST↓ in II, III, aVF (reciprocal)\n"
"• Posterior MI: ST↓ V1–V3 is the primary finding\n"
"• Reciprocal changes confirm MI (vs pericarditis)",
LIGHT_YELLOW, ORANGE),
("🟣 Mimics & Pitfalls",
"• LBBB masks Q waves & ST changes\n"
"• LVH: ST↑ in V1–V3 (strain pattern)\n"
"• Pericarditis: diffuse ST↑, concave, no Q waves\n"
"• Brugada: coved ST↑ V1–V3, no ischaemia hx\n"
"• Benign early repolarisation: notched J-point",
colors.HexColor("#F5EEF8"), PURPLE),
("🔵 Special Situations",
"• RV MI: IV fluids, avoid nitrates & diuretics\n"
"• Posterior MI: flip V1–V3 (ST↓ + tall R = ST↑ + Q)\n"
"• LAD 'De Winter' pattern: upsloping ST↓ + tall T\n"
"• Wellens syndrome: deep symmetric T V2–V3 = LAD\n"
"• Aslanger pattern: inferior MI + lateral involvement",
LIGHT_BLUE, BLUE),
]
tip_rows = []
for t_title, t_body, bg, tc in tips:
cell_data = [
[Paragraph(f"<b>{t_title}</b>", ParagraphStyle('TH', fontName='Helvetica-Bold',
fontSize=8, textColor=tc, spaceAfter=3))],
[Paragraph(t_body.replace('\n', '<br/>'), ParagraphStyle('TB', fontName='Helvetica',
fontSize=7.5, textColor=DARK_GRAY, leading=11))],
]
cell_table = Table(cell_data, colWidths=[42*mm])
cell_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('BOX', (0,0), (-1,-1), 1, tc),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('ROUNDEDCORNERS', [4,4,4,4]),
]))
tip_rows.append(cell_table)
tips_row = Table([tip_rows], colWidths=[45.5*mm]*4)
tips_row.setStyle(TableStyle([
('ALIGN', (0,0), (-1,-1), 'LEFT'),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('LEFTPADDING', (0,0), (-1,-1), 0),
('RIGHTPADDING', (0,0), (-1,-1), 2),
]))
story.append(tips_row)
story.append(Spacer(1, 5*mm))
# ── Artery → Territory → Leads Quick Grid ─────────────────────────────────
art_title = [[Paragraph(" Coronary Artery → Territory → Leads at a Glance", section_title)]]
art_t = Table(art_title, colWidths=[180*mm])
art_t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), DARK_GRAY),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('ROUNDEDCORNERS', [4,4,4,4]),
]))
story.append(art_t)
story.append(Spacer(1, 2*mm))
art_data = [
[
Paragraph("<b>LAD</b>", ParagraphStyle('AH', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
Paragraph("<b>RCA</b>", ParagraphStyle('AH', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
Paragraph("<b>LCX</b>", ParagraphStyle('AH', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
],
[
Paragraph("Anterior + Septal\n<font size='8' color='red'>V1 – V4</font>", ParagraphStyle('AC', fontName='Helvetica', fontSize=8, textColor=DARK_GRAY, alignment=TA_CENTER, leading=12)),
Paragraph("Inferior + Posterior + RV\n<font size='8' color='orange'>II, III, aVF + V3R/V4R</font>", ParagraphStyle('AC', fontName='Helvetica', fontSize=8, textColor=DARK_GRAY, alignment=TA_CENTER, leading=12)),
Paragraph("Lateral + Posterior\n<font size='8' color='purple'>I, aVL, V5, V6</font>", ParagraphStyle('AC', fontName='Helvetica', fontSize=8, textColor=DARK_GRAY, alignment=TA_CENTER, leading=12)),
],
[
Paragraph("40–50% of MIs", ParagraphStyle('AS', fontName='Helvetica-Oblique', fontSize=7.5, textColor=MID_GRAY, alignment=TA_CENTER)),
Paragraph("30–40% of MIs", ParagraphStyle('AS', fontName='Helvetica-Oblique', fontSize=7.5, textColor=MID_GRAY, alignment=TA_CENTER)),
Paragraph("15–20% of MIs", ParagraphStyle('AS', fontName='Helvetica-Oblique', fontSize=7.5, textColor=MID_GRAY, alignment=TA_CENTER)),
],
]
art_table = Table(art_data, colWidths=[60*mm, 60*mm, 60*mm])
art_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (0,0), RED),
('BACKGROUND', (1,0), (1,0), ORANGE),
('BACKGROUND', (2,0), (2,0), PURPLE),
('BACKGROUND', (0,1), (0,1), LIGHT_RED),
('BACKGROUND', (1,1), (1,1), LIGHT_YELLOW),
('BACKGROUND', (2,1), (2,1), colors.HexColor("#F5EEF8")),
('BACKGROUND', (0,2), (-1,2), LIGHT_GRAY),
('GRID', (0,0), (-1,-1), 0.5, colors.HexColor("#CACFD2")),
('ALIGN', (0,0), (-1,-1), 'CENTER'),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
]))
story.append(art_table)
story.append(Spacer(1, 5*mm))
# ── Footer ─────────────────────────────────────────────────────────────────
footer_data = [[
Paragraph(
"<font size='7' color='#AEB6BF'>Sources: Robbins Pathologic Basis of Disease | Ganong's Review of Medical Physiology | "
"Braunwald's Heart Disease • Created with Orris Medical AI</font>",
ParagraphStyle('Footer', fontName='Helvetica', fontSize=7, textColor=MID_GRAY, alignment=TA_CENTER)
)
]]
footer_table = Table(footer_data, colWidths=[180*mm])
footer_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), DARK_GRAY),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
]))
story.append(footer_table)
doc.build(story)
print(f"PDF saved to: {output_path}")
if __name__ == "__main__":
build_pdf("/home/daytona/workspace/mi-ecg-ref/MI_ECG_Quick_Reference.pdf")
cd /home/daytona/workspace/mi-ecg-ref && python create_pdf.py 2>&1
ls -lh /home/daytona/workspace/mi-ecg-ref/MI_ECG_Quick_Reference.pdf
MI ECG Quick Reference
PDF Document · PDF