Explain me ecg changes in images with pdf
ls /home/daytona/workspace/ 2>/dev/null && echo "---exists---" || mkdir -p /home/daytona/workspace/ecg-report
mkdir -p /home/daytona/workspace/ecg-report && curl -s -o /home/daytona/workspace/ecg-report/patient_ecg.jpg "https://d361swluvexzqs.cloudfront.net/chats-attachments/20260608_213336-1780974618521-vwdq6p.jpg" && echo "Downloaded"
~/ecg-report/generate_ecg_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
Image, HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.pdfgen import canvas
import os
OUTPUT = "/home/daytona/workspace/ecg-report/ECG_Changes_Analysis.pdf"
ECG_IMG = "/home/daytona/workspace/ecg-report/patient_ecg.jpg"
# ── Custom page with header/footer ────────────────────────────────────────────
def header_footer(canvas_obj, doc):
canvas_obj.saveState()
w, h = A4
# Header bar
canvas_obj.setFillColor(colors.HexColor("#1a3d6b"))
canvas_obj.rect(0, h - 2*cm, w, 2*cm, fill=1, stroke=0)
canvas_obj.setFillColor(colors.white)
canvas_obj.setFont("Helvetica-Bold", 14)
canvas_obj.drawString(1.5*cm, h - 1.3*cm, "ECG ANALYSIS REPORT")
canvas_obj.setFont("Helvetica", 9)
canvas_obj.drawRightString(w - 1.5*cm, h - 1.3*cm, "Patient: Goutam Giloi | Date: 05/06/26")
# Footer
canvas_obj.setFillColor(colors.HexColor("#1a3d6b"))
canvas_obj.rect(0, 0, w, 1.2*cm, fill=1, stroke=0)
canvas_obj.setFillColor(colors.white)
canvas_obj.setFont("Helvetica", 8)
canvas_obj.drawString(1.5*cm, 0.45*cm, "Educational reference — Not a substitute for clinical judgment")
canvas_obj.drawRightString(w - 1.5*cm, 0.45*cm, f"Page {doc.page}")
canvas_obj.restoreState()
# ── Styles ─────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
title_style = ParagraphStyle(
"Title2", parent=styles["Title"],
fontSize=18, textColor=colors.HexColor("#1a3d6b"),
spaceAfter=4, alignment=TA_CENTER
)
h1 = ParagraphStyle("H1", parent=styles["Heading1"],
fontSize=13, textColor=colors.HexColor("#1a3d6b"),
spaceBefore=14, spaceAfter=4,
borderPad=4, leading=16
)
h2 = ParagraphStyle("H2", parent=styles["Heading2"],
fontSize=11, textColor=colors.HexColor("#c0392b"),
spaceBefore=10, spaceAfter=3
)
body = ParagraphStyle("Body", parent=styles["Normal"],
fontSize=9.5, leading=14, alignment=TA_JUSTIFY,
spaceAfter=6
)
bullet = ParagraphStyle("Bullet", parent=styles["Normal"],
fontSize=9.5, leading=14, leftIndent=16,
bulletIndent=6, spaceAfter=3
)
caption = ParagraphStyle("Caption", parent=styles["Normal"],
fontSize=8.5, textColor=colors.HexColor("#555555"),
alignment=TA_CENTER, spaceAfter=6, fontName="Helvetica-Oblique"
)
warn_style = ParagraphStyle("Warn", parent=styles["Normal"],
fontSize=9.5, leading=14, backColor=colors.HexColor("#fff3cd"),
textColor=colors.HexColor("#856404"), leftIndent=8, rightIndent=8,
borderPad=6, spaceAfter=8
)
def section_bar(text):
"""Returns a styled section header table."""
data = [[Paragraph(text, ParagraphStyle("SH", fontSize=11,
textColor=colors.white, fontName="Helvetica-Bold", leading=14))]]
t = Table(data, colWidths=[17*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#1a3d6b")),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("LEFTPADDING", (0,0), (-1,-1), 10),
]))
return t
# ── Build story ────────────────────────────────────────────────────────────────
story = []
story.append(Spacer(1, 0.3*cm))
# --- Patient ECG Image ---
story.append(section_bar("PATIENT ECG — SUBMITTED TRACING"))
story.append(Spacer(1, 0.3*cm))
if os.path.exists(ECG_IMG):
img = Image(ECG_IMG, width=16*cm, height=10*cm, kind="proportional")
story.append(img)
story.append(Paragraph(
"Patient ECG recorded at Midnapore Multispeciality Hospital. 10 mm/mV, 25 mm/s. "
"Date 05/06/26, Time 10:20 AM. Patient: Goutam Giloi.",
caption
))
story.append(Spacer(1, 0.4*cm))
# ── 1. ECG BASICS ─────────────────────────────────────────────────────────────
story.append(section_bar("1. ECG BASICS & SYSTEMATIC READING"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"A 12-lead ECG records the heart's electrical activity from 12 different angles. "
"Standard paper speed is 25 mm/s; 1 small box = 40 ms (0.04 s), "
"1 large box = 200 ms (0.2 s). Amplitude: 1 mm = 0.1 mV at standard 10 mm/mV gain.",
body
))
basics_data = [
["Component", "Normal Duration / Amplitude", "Significance"],
["P wave", "80–120 ms; ≤2.5 mm tall", "Atrial depolarisation"],
["PR interval", "120–200 ms", "AV nodal conduction"],
["QRS complex", "< 120 ms", "Ventricular depolarisation"],
["ST segment", "Isoelectric (±1 mm)", "Early ventricular repolarisation"],
["T wave", "Positive in I, II, V4-V6", "Ventricular repolarisation"],
["QT interval", "< 440 ms (men), < 460 ms (women)", "Total ventricular action potential"],
["QTc", "Corrected for HR (Bazett formula)", "Risk of arrhythmia if prolonged"],
]
t = Table(basics_data, colWidths=[4.5*cm, 6*cm, 6.5*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#1a3d6b")),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.HexColor("#f0f4f8"), colors.white]),
("GRID", (0,0), (-1,-1), 0.5, colors.HexColor("#b0bec5")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t)
story.append(Spacer(1, 0.4*cm))
# ── 2. ANALYSIS OF SUBMITTED ECG ──────────────────────────────────────────────
story.append(section_bar("2. ANALYSIS OF SUBMITTED ECG"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"The submitted tracing (Goutam Giloi, 05/06/26) is a standard 12-lead ECG recorded "
"at 25 mm/s, 10 mm/mV. The following observations can be made from visual inspection:",
body
))
findings = [
("<b>Rhythm:</b>", "Regular narrow QRS complexes are visible throughout. The rhythm appears to be sinus in origin with P waves preceding each QRS. Rate appears in the range of 70–90 bpm (estimated)."),
("<b>QRS Axis:</b>", "The dominant deflections in limb leads suggest a normal or leftward axis. Detailed axis calculation requires precise lead I and aVF amplitudes."),
("<b>QRS Morphology:</b>", "QRS complexes appear narrow (< 120 ms), arguing against bundle branch block. Tall R waves are noted in left precordial leads (V4-V6 region)."),
("<b>ST Segment:</b>", "No obvious gross ST elevation in multiple leads. Careful lead-by-lead inspection is required for subtle changes. Some baseline wobble noted."),
("<b>T waves:</b>", "T waves appear upright in most leads visible. Possible flattening in inferior or lateral leads — clinical correlation needed."),
("<b>Precordial leads:</b>", "R-wave progression visible across chest leads. No obvious Q waves suggestive of old infarction identified on gross inspection."),
("<b>Voltage:</b>", "Tall precordial voltages may suggest LVH — formal Sokolov-Lyon or Cornell criteria should be applied (see Section 4)."),
]
for label, text in findings:
story.append(Paragraph(f"{label} {text}", bullet))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"⚠ <b>Note:</b> This is a visual interpretation guide. Definitive clinical ECG reading must be "
"performed by a qualified cardiologist with access to the original tracing and patient history.",
warn_style
))
story.append(Spacer(1, 0.4*cm))
# ── 3. COMMON ECG CHANGES ─────────────────────────────────────────────────────
story.append(section_bar("3. COMMON PATHOLOGICAL ECG CHANGES"))
story.append(Spacer(1, 0.2*cm))
ecg_changes = [
["ECG Change", "Description", "Common Cause(s)"],
["ST Elevation (STE)", ">1 mm in ≥2 contiguous limb leads or >2 mm in precordial leads above J-point",
"STEMI, pericarditis, Brugada, early repolarisation, LVH strain, vasospasm"],
["ST Depression", ">0.5 mm below baseline in ≥2 contiguous leads",
"NSTEMI/UA, LVH strain, digoxin effect, subendocardial ischemia"],
["T-wave Inversion", "T wave negative where normally positive",
"Ischemia, ARVD, PE (V1-V4), LVH strain, hypertrophic cardiomyopathy"],
["Pathological Q waves", "≥0.04 s wide and ≥25% of R wave height in ≥2 contiguous leads",
"Previous MI (transmural), LBBB, WPW"],
["Tall peaked P waves", ">2.5 mm in II, III, aVF",
"Right atrial enlargement, pulmonary hypertension, COPD"],
["Broad notched P waves", ">0.12 s, bifid P in lead II",
"Left atrial enlargement, mitral stenosis"],
["Prolonged QTc", ">440 ms (men), >460 ms (women)",
"Drugs (quinolones, antipsychotics), hypokalaemia, hypomagnesaemia, congenital LQTS"],
["Delta wave / Short PR", "Slurred QRS upstroke, PR < 120 ms",
"Wolff-Parkinson-White syndrome (pre-excitation)"],
["LBBB", "QRS ≥120 ms; broad notched R in I, aVL, V5-V6; absence of septal Q",
"IHD, cardiomyopathy, hypertension, conduction disease"],
["RBBB", "QRS ≥120 ms; rSR' in V1; wide S in I, V5-V6",
"RV strain, PE, ASD, ischaemia, normal variant (incomplete RBBB)"],
]
t2 = Table(ecg_changes, colWidths=[4*cm, 7*cm, 6*cm])
t2.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#c0392b")),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.HexColor("#fdf5f5"), colors.white]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#e0b0b0")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
("WORDWRAP", (0,0), (-1,-1), True),
]))
story.append(t2)
story.append(Spacer(1, 0.4*cm))
# ── 4. LVH CRITERIA ───────────────────────────────────────────────────────────
story.append(section_bar("4. LEFT VENTRICULAR HYPERTROPHY (LVH) CRITERIA"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"LVH is relevant when tall precordial voltages are seen (as may be present in this tracing). "
"LVH increases amplitude of forces directed left and posteriorly, with characteristic "
"repolarisation changes (ST depression + T-wave inversion in lateral leads = 'strain pattern').",
body
))
lvh_data = [
["Criterion", "Formula / Threshold", "Sensitivity / Notes"],
["Sokolov-Lyon", "SV1 + RV5 (or RV6) > 35 mm", "~50% sensitivity, higher specificity"],
["Cornell (men)", "RaVL + SV3 > 28 mm", "Better in obese patients"],
["Cornell (women)", "RaVL + SV3 > 20 mm", "Gender-adjusted threshold"],
["Lewis index", "RI + SIII - RIII - SI > 16 mm", "Less commonly used"],
["RaVL alone", "> 28 mm (men), > 20 mm (women)", "Simple limb-lead criterion"],
["Strain pattern", "ST depression + T-wave inversion in I, aVL, V5-V6", "Indicates pressure overload"],
]
t3 = Table(lvh_data, colWidths=[4.5*cm, 6.5*cm, 6*cm])
t3.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#1a6b3d")),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.HexColor("#f0faf4"), colors.white]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#a0c8b0")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t3)
story.append(Spacer(1, 0.4*cm))
# ── 5. MI ECG EVOLUTION ───────────────────────────────────────────────────────
story.append(section_bar("5. ECG EVOLUTION IN MYOCARDIAL INFARCTION"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"The ECG evolves in a predictable sequence during MI. Recognising which stage has been reached "
"guides reperfusion decisions (Braunwald's Heart Disease, 22nd ed):",
body
))
mi_phases = [
["Phase", "Timeframe", "ECG Features"],
["Hyperacute / Early", "Minutes to ~1 hour", "Tall, peaked (hyperacute) T waves; subtle STE at J-point"],
["Acute", "Hours to ~24 hours", "STE ≥ 1 mm in ≥2 contiguous leads; reciprocal ST depression opposite wall"],
["Evolving", "24–72 hours", "STE persists; Q waves develop; T-wave inversion begins"],
["Subacute", "Days to weeks", "STE diminishes; deep T-wave inversion; Q waves present"],
["Chronic / Old", "Weeks to years", "Q waves persist; T waves normalise; ST returns to baseline"],
["Posterior MI", "Any phase", "ST depression + tall R + upright T in V1-V3 (mirror image)"],
]
t4 = Table(mi_phases, colWidths=[3.5*cm, 4*cm, 9.5*cm])
t4.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#7b2d8b")),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.HexColor("#faf0fc"), colors.white]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#c8a0d0")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(t4)
story.append(Spacer(1, 0.4*cm))
# ── 6. LOCALISATION TABLE ─────────────────────────────────────────────────────
story.append(section_bar("6. ECG LOCALISATION OF MI TERRITORY"))
story.append(Spacer(1, 0.2*cm))
loc_data = [
["Territory", "ECG Leads Affected", "Artery (typically)"],
["Anterior", "V1, V2, V3, V4", "LAD (proximal)"],
["Anteroseptal", "V1, V2, V3", "LAD (septal branches)"],
["Lateral", "I, aVL, V5, V6", "LCx or diagonal branch"],
["Inferior", "II, III, aVF", "RCA (80%) or LCx (20%)"],
["Posterior", "V1-V3 (reciprocal)", "RCA or LCx"],
["Right ventricular", "V3R, V4R (right-sided leads)", "RCA (proximal)"],
["Anterolateral", "V1-V6, I, aVL", "LAD or LCx"],
]
t5 = Table(loc_data, colWidths=[4.5*cm, 6*cm, 6.5*cm])
t5.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#c0392b")),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.HexColor("#fff5f5"), colors.white]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#e0b0b0")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t5)
story.append(Spacer(1, 0.4*cm))
# ── 7. DIFFERENTIAL DIAGNOSIS ─────────────────────────────────────────────────
story.append(section_bar("7. DIFFERENTIAL DIAGNOSIS OF ST ELEVATION"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"ST elevation has a broad differential beyond STEMI. The Washington Manual of Medical Therapeutics "
"lists the following important mimics (Table 4-16):",
body
))
diff_data = [
["Cardiac Etiologies", "Non-Cardiac / Other"],
["STEMI (acute MI)", "Pulmonary embolism (S1Q3T3 pattern)"],
["Pericarditis (diffuse saddle-shaped STE)", "Hyperkalemia (peaked T, then STE)"],
["Myocarditis", "Normal variant / early repolarisation"],
["LV aneurysm (persistent STE after old MI)", "Brugada syndrome (V1-V3)"],
["Aortic dissection with coronary involvement", ""],
["Hypertrophic cardiomyopathy", ""],
["Coronary vasospasm (Prinzmetal angina)", ""],
["LV hypertrophy with strain", ""],
]
t6 = Table(diff_data, colWidths=[8.5*cm, 8.5*cm])
t6.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#2c5f8a")),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.HexColor("#f0f7ff"), colors.white]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#a0c0e0")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t6)
story.append(Spacer(1, 0.4*cm))
# ── 8. PATHOLOGICAL Q WAVES ───────────────────────────────────────────────────
story.append(section_bar("8. PATHOLOGICAL Q WAVES — CRITERIA"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"Pathological Q waves indicate transmural infarction. Criteria (Washington Manual, 2023):",
body
))
for pt in [
"In leads <b>V2 and V3</b>: Q wave ≥ 0.02 s OR QS complex in both V2 and V3.",
"In <b>all other leads</b> (except V1, III, aVR): Q wave ≥ 0.03 s AND ≥ 0.1 mV deep in ≥2 contiguous leads.",
"Isolated Q in V1 or lead III is <b>normal</b>.",
"<b>Posterior MI marker:</b> R wave ≥ 0.04 s in V1-V2 with R/S ≥ 1 and positive T wave (absence of RVH/RBBB).",
"Q waves alone without symptoms do not mandate reperfusion — compare to prior ECG for chronicity.",
]:
story.append(Paragraph(f"• {pt}", bullet))
story.append(Spacer(1, 0.4*cm))
# ── 9. SGARBOSSA CRITERIA ─────────────────────────────────────────────────────
story.append(section_bar("9. SGARBOSSA CRITERIA — STEMI IN LBBB / PACED RHYTHM"))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"When LBBB or RV paced rhythm is present, standard STE criteria cannot be applied. "
"Sgarbossa criteria (GUSTO trial) identify STEMI:",
body
))
sgb = [
["Criterion", "Points", "Interpretation"],
["STE > 1 mm concordant with QRS (same direction)", "5", "Highly specific for STEMI"],
["ST depression > 1 mm in V1-V3", "3", "Reciprocal change suggesting STEMI"],
["STE > 5 mm discordant with QRS (opposite direction)", "2", "Less specific alone"],
["Score ≥ 3 points", "—", "High likelihood of STEMI; proceed to cath lab"],
]
t7 = Table(sgb, colWidths=[8*cm, 2.5*cm, 6.5*cm])
t7.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), colors.HexColor("#7b4e2d")),
("TEXTCOLOR", (0,0), (-1,0), colors.white),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [colors.HexColor("#faf5f0"), colors.white]),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#d0b090")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("ALIGN", (0,0), (-1,-1), "LEFT"),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t7)
story.append(Spacer(1, 0.4*cm))
# ── 10. REFERENCES ─────────────────────────────────────────────────────────────
story.append(section_bar("10. REFERENCES"))
story.append(Spacer(1, 0.2*cm))
for ref in [
"1. Zipes DP et al. <i>Braunwald's Heart Disease: A Textbook of Cardiovascular Medicine</i>, 12th ed. Elsevier (2022). ISBN 9780323722193.",
"2. Longo DL et al. <i>Harrison's Principles of Internal Medicine</i>, 22nd ed. McGraw-Hill (2025). ISBN 9781265977061.",
"3. Kollef MH et al. <i>The Washington Manual of Medical Therapeutics</i>. Wolters Kluwer (2023). ISBN 9781975190620.",
"4. Tintinalli JE et al. <i>Tintinalli's Emergency Medicine</i>. McGraw-Hill (2020). ISBN 9781260019933.",
"5. Marx JA et al. <i>Rosen's Emergency Medicine</i>, 10th ed. Elsevier (2023). ISBN 9780323757898.",
"6. Sgarbossa EB et al. Electrocardiographic diagnosis of evolving acute myocardial infarction in the presence of left bundle-branch block. <i>N Engl J Med</i>. 1996;334(8):481-487.",
]:
story.append(Paragraph(ref, body))
story.append(Spacer(1, 0.2*cm))
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor("#1a3d6b")))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"<b>Disclaimer:</b> This report is generated for educational purposes only. "
"ECG interpretation in a clinical context must be performed by a licensed cardiologist or physician. "
"Do not make clinical decisions based solely on this document.",
ParagraphStyle("Disc", parent=styles["Normal"], fontSize=8,
textColor=colors.HexColor("#888888"), alignment=TA_CENTER)
))
# ── Build PDF ──────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
topMargin=2.5*cm, bottomMargin=1.8*cm,
leftMargin=1.5*cm, rightMargin=1.5*cm,
title="ECG Analysis Report — Goutam Giloi",
author="Orris Medical AI",
subject="12-Lead ECG Interpretation"
)
doc.build(story, onFirstPage=header_footer, onLaterPages=header_footer)
print(f"PDF created: {OUTPUT}")
cd /home/daytona/workspace/ecg-report && python generate_ecg_pdf.py
| Feature | Observation |
|---|---|
| Rhythm | Regular narrow QRS complexes; P waves precede each QRS → likely sinus rhythm |
| Rate | Estimated ~70-90 bpm (R-R interval ~3-4 large boxes) |
| QRS width | Narrow (<120 ms) - no bundle branch block pattern visible |
| Precordial voltages | Tall R waves in left precordial leads - possible LVH by voltage criteria |
| ST segment | No gross ST elevation across leads; requires careful measurement |
| T waves | Upright in most visible leads; possible flattening in some inferior leads |
| Q waves | No obvious pathological Q waves on gross inspection |
| P waves | Present before each QRS; morphology needs closer measurement |
ECG Changes Analysis
PDF Document · PDF
Important: This analysis is educational. A qualified cardiologist must perform the formal clinical ECG interpretation, especially before any treatment decision.
Ecg changes analysis report pdf
ls -lh /home/daytona/workspace/ecg-report/
ECG Changes Analysis
PDF Document · PDF