I am medicine resident make a ecg pdf from basic to advance with all named sign step wise learning and interpretation of ecg in each ecg rate calculation rhythm axis deviation and also disease wise
mkdir -p /home/daytona/workspace/ecg-guide && ls /home/daytona/workspace/ecg-guide/
I am medicine resident make a ecg pdf from basic to advance with all named sign step wise learning and interpretation of ecg in each ecg rate calculation rhythm axis deviation and also disease wise
mkdir -p /home/daytona/workspace/ecg-guide && python3 << 'PYEOF' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.units import mm, cm from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, HRFlowable, KeepTogether) from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY 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') GREEN = colors.HexColor('#1E8449') ORANGE = colors.HexColor('#E67E22') PURPLE = colors.HexColor('#6C3483') TEAL = colors.HexColor('#117A65') YELLOW_BG = colors.HexColor('#FDFCE4') GREY = colors.HexColor('#555555') LIGHT_GREY= colors.HexColor('#F2F3F4') ECG_GREEN = colors.HexColor('#00CC00') ECG_BG = colors.HexColor('#1A1A1A') BOX_BLUE = colors.HexColor('#EAF2FF') BOX_RED = colors.HexColor('#FDEDEC') BOX_GREEN = colors.HexColor('#EAFAF1') BOX_ORANGE= colors.HexColor('#FEF9E7') # ── styles ─────────────────────────────────────────────────────── styles = getSampleStyleSheet() def make_style(name, parent='Normal', **kwargs): return ParagraphStyle(name, parent=styles[parent], **kwargs) cover_title = make_style('CoverTitle', fontSize=36, textColor=colors.white, alignment=TA_CENTER, fontName='Helvetica-Bold', leading=44) cover_sub = make_style('CoverSub', fontSize=16, textColor=colors.HexColor('#BDC3C7'), alignment=TA_CENTER, fontName='Helvetica', leading=24) cover_author = make_style('CoverAuthor', fontSize=12, textColor=colors.HexColor('#85C1E9'), alignment=TA_CENTER, fontName='Helvetica-Oblique') chapter_h = make_style('ChapterH', fontSize=22, textColor=colors.white, fontName='Helvetica-Bold', leading=28, alignment=TA_CENTER) section_h = make_style('SectionH', fontSize=16, textColor=BLUE, fontName='Helvetica-Bold', leading=22, spaceAfter=6, spaceBefore=12) subsect_h = make_style('SubSectH', fontSize=13, textColor=DARK_RED, fontName='Helvetica-Bold', leading=18, spaceAfter=4, spaceBefore=8) body_st = make_style('Body', fontSize=10, leading=15, alignment=TA_JUSTIFY, spaceAfter=4) bullet_st = make_style('Bullet', fontSize=10, leading=14, leftIndent=16, bulletIndent=6, spaceAfter=2) note_st = make_style('Note', fontSize=9, leading=13, textColor=GREY, fontName='Helvetica-Oblique') table_hdr = make_style('TblHdr', fontSize=10, textColor=colors.white, fontName='Helvetica-Bold', alignment=TA_CENTER) table_cell = make_style('TblCell', fontSize=9, leading=13, alignment=TA_CENTER) table_cell_l = make_style('TblCellL', fontSize=9, leading=13, alignment=TA_LEFT) warn_st = make_style('Warn', fontSize=10, leading=14, textColor=DARK_RED, fontName='Helvetica-Bold') key_st = make_style('Key', fontSize=10, leading=14, textColor=GREEN, fontName='Helvetica-Bold') # ── helpers ────────────────────────────────────────────────────── def hline(color=BLUE, thickness=1.5): return HRFlowable(width='100%', thickness=thickness, color=color, spaceAfter=4) def spacer(h=6): return Spacer(1, h) def B(text): return f'<b>{text}</b>' def I(text): return f'<i>{text}</i>' def C(text,c): return f'<font color="{c}">{text}</font>' def body(*parts): return Paragraph(' '.join(parts), body_st) def sect(t): return Paragraph(t, section_h) def sub(t): return Paragraph(t, subsect_h) def bul(t): return Paragraph(f'<bullet>•</bullet> {t}', bullet_st) def note(t): return Paragraph(t, note_st) def warn(t): return Paragraph(t, warn_st) def key(t): return Paragraph(t, key_st) def info_box(title, items, bg=BOX_BLUE, title_color=BLUE): data = [[Paragraph(B(title), make_style('ibT', fontSize=11, textColor=title_color, fontName='Helvetica-Bold'))]] for item in items: data.append([Paragraph(item, body_st)]) t = Table(data, colWidths=[165*mm]) t.setStyle(TableStyle([ ('BACKGROUND', (0,0), (0,0), bg), ('BACKGROUND', (0,1), (-1,-1), colors.white), ('BOX', (0,0), (-1,-1), 1, title_color), ('GRID', (0,0), (-1,-1), 0.3, colors.HexColor('#CCCCCC')), ('LEFTPADDING', (0,0), (-1,-1), 8), ('RIGHTPADDING',(0,0), (-1,-1), 8), ('TOPPADDING', (0,0), (-1,-1), 5), ('BOTTOMPADDING',(0,0),(-1,-1),5), ('ROWBACKGROUNDS',(0,1),(-1,-1),[colors.white, colors.HexColor('#F9F9F9')]), ])) return t def two_col_table(headers, rows, col_widths=None): if col_widths is None: col_widths = [55*mm, 110*mm] data = [[Paragraph(B(h), table_hdr) for h in headers]] + \ [[Paragraph(str(c), table_cell_l) for c in row] for row in rows] t = Table(data, colWidths=col_widths) t.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,0), BLUE), ('TEXTCOLOR', (0,0), (-1,0), colors.white), ('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'), ('FONTSIZE', (0,0), (-1,-1), 9), ('GRID', (0,0), (-1,-1), 0.5, colors.grey), ('ROWBACKGROUNDS',(0,1),(-1,-1),[colors.white, LIGHT_GREY]), ('LEFTPADDING', (0,0), (-1,-1), 6), ('RIGHTPADDING',(0,0), (-1,-1), 6), ('TOPPADDING', (0,0), (-1,-1), 4), ('BOTTOMPADDING',(0,0),(-1,-1),4), ('VALIGN', (0,0), (-1,-1), 'TOP'), ])) return t # ── ECG waveform drawing ───────────────────────────────────────── class ECGWaveformDrawing(Flowable): """Draws a schematic ECG strip with labelled waves.""" def __init__(self, width=165*mm, height=50*mm): super().__init__() self.width = width self.height = height def draw(self): w, h = self.width, self.height c = self.canv # background grid c.setFillColor(colors.HexColor('#F5FFF5')) c.rect(0, 0, w, h, fill=1, stroke=0) # minor grid c.setStrokeColor(colors.HexColor('#CCEECC')) c.setLineWidth(0.3) step = 5*mm x = 0 while x <= w: c.line(x, 0, x, h) x += step y = 0 while y <= h: c.line(0, y, w, y) y += step # major grid c.setStrokeColor(colors.HexColor('#99DD99')) c.setLineWidth(0.7) x = 0 while x <= w: c.line(x, 0, x, h) x += 5*step y = 0 while y <= h: c.line(0, y, w, y) y += 5*step # baseline base = h * 0.38 c.setStrokeColor(colors.HexColor('#006600')) c.setLineWidth(1.8) # draw ECG trace def pt(frac_x, frac_y): return (frac_x * w, base + frac_y * h * 0.9) # one full PQRSTU complex points = [ pt(0.00, 0), # start pt(0.06, 0), # isoelectric # P wave pt(0.09, 0.08), pt(0.13, 0.12), pt(0.17, 0.08), pt(0.21, 0), # end P / PR segment # Q wave pt(0.27, 0), pt(0.29,-0.06), # R wave pt(0.33, 0.55), # S wave pt(0.37,-0.08), pt(0.40, 0), # J point # ST segment pt(0.46, 0.02), # T wave pt(0.52, 0.10), pt(0.58, 0.22), pt(0.64, 0.10), pt(0.70, 0.02), # U wave pt(0.72, 0), pt(0.74, 0.04), pt(0.77, 0), pt(0.80, 0), # end ] path = c.beginPath() path.moveTo(*points[0]) for px, py in points[1:]: path.lineTo(px, py) c.drawPath(path, stroke=1, fill=0) # second beat (partial) shift = 0.55 pts2 = [ pt(0.54+shift*0.0, 0), pt(0.54+shift*0.04, 0), pt(0.54+shift*0.06, 0.08), pt(0.54+shift*0.09, 0.12), pt(0.54+shift*0.12, 0.08), pt(0.54+shift*0.15, 0), ] # only draw if within bounds if pts2[-1][0] <= w: path2 = c.beginPath() path2.moveTo(*pts2[0]) for px, py in pts2[1:]: path2.lineTo(px, py) c.drawPath(path2, stroke=1, fill=0) # labels c.setFillColor(RED) c.setFont('Helvetica-Bold', 7) labels = { 'P': (pt(0.13, 0.12), (0, 5)), 'Q': (pt(0.29,-0.06), (-8, -10)), 'R': (pt(0.33, 0.55), (2, 4)), 'S': (pt(0.37,-0.08), (2, -12)), 'T': (pt(0.58, 0.22), (2, 5)), 'U': (pt(0.74, 0.04), (2, 5)), 'J pt':(pt(0.40, 0), (3, -12)), } c.setFillColor(RED) for lbl, (pos, off) in labels.items(): c.drawString(pos[0]+off[0], pos[1]+off[1], lbl) # interval brackets c.setStrokeColor(BLUE) c.setLineWidth(0.8) # PR interval pr_start = pt(0.06, 0)[0] pr_end = pt(0.27, 0)[0] y_br = base - 9*mm c.line(pr_start, y_br, pr_end, y_br) c.line(pr_start, y_br-1, pr_start, y_br+1) c.line(pr_end, y_br-1, pr_end, y_br+1) c.setFillColor(BLUE); c.setFont('Helvetica-Bold', 7) c.drawCentredString((pr_start+pr_end)/2, y_br-8, 'PR interval') # QRS interval q_x = pt(0.27, 0)[0]; s_x = pt(0.40, 0)[0] y_bq = base - 14*mm c.setStrokeColor(GREEN) c.line(q_x, y_bq, s_x, y_bq) c.line(q_x, y_bq-1, q_x, y_bq+1) c.line(s_x, y_bq-1, s_x, y_bq+1) c.setFillColor(GREEN); c.setFont('Helvetica-Bold', 7) c.drawCentredString((q_x+s_x)/2, y_bq-8, 'QRS') # QT interval qt_end = pt(0.70, 0)[0] y_bqt = base - 19*mm c.setStrokeColor(PURPLE) c.line(q_x, y_bqt, qt_end, y_bqt) c.line(q_x, y_bqt-1, q_x, y_bqt+1) c.line(qt_end, y_bqt-1, qt_end, y_bqt+1) c.setFillColor(PURPLE); c.setFont('Helvetica-Bold', 7) c.drawCentredString((q_x+qt_end)/2, y_bqt-8, 'QT interval') # ST segment label st_s = pt(0.40, 0)[0]; st_e = pt(0.52, 0.02)[0] c.setFillColor(ORANGE); c.setFont('Helvetica-Bold', 7) c.drawCentredString((st_s+st_e)/2, base + h*0.05, 'ST seg') # border c.setStrokeColor(BLUE) c.setLineWidth(1) c.rect(0, 0, w, h, fill=0, stroke=1) class ChapterBanner(Flowable): def __init__(self, text, subtitle='', width=165*mm, height=22*mm, color=BLUE): super().__init__() self.text = text self.subtitle = subtitle self.width = width self.height = height self.color = color def draw(self): c = self.canv w, h = self.width, self.height # gradient-like banner c.setFillColor(self.color) c.roundRect(0, 0, w, h, 4, fill=1, stroke=0) # accent bar c.setFillColor(colors.HexColor('#F39C12')) c.rect(0, 0, 5, h, fill=1, stroke=0) c.setFillColor(colors.white) c.setFont('Helvetica-Bold', 16) c.drawString(12, h-14, self.text) if self.subtitle: c.setFont('Helvetica', 9) c.setFillColor(colors.HexColor('#D6EAF8')) c.drawString(12, 5, self.subtitle) class LeadDiagram(Flowable): """Hexaxial reference system diagram.""" def __init__(self, size=55*mm): super().__init__() self.width = size; self.height = size def draw(self): c = self.canv cx = self.width/2; cy = self.height/2 r = self.width*0.43 # background circle c.setFillColor(BOX_BLUE); c.setStrokeColor(BLUE); c.setLineWidth(0.8) c.circle(cx, cy, r, fill=1, stroke=1) leads = [ ('I', 0, BLUE), ('aVL', -60, GREEN), ('II', 60, BLUE), ('aVF', 90, BLUE), ('III', 120, BLUE), ('aVR',-120, RED), ] for name, angle_deg, col in leads: rad = math.radians(angle_deg) x2 = cx + r*math.cos(rad) y2 = cy + r*math.sin(rad) x1 = cx - r*math.cos(rad) y1 = cy - r*math.sin(rad) c.setStrokeColor(col); c.setLineWidth(1) c.line(x1, y1, x2, y2) # label lx = cx + (r+4)*math.cos(rad) ly = cy + (r+4)*math.sin(rad) c.setFillColor(col); c.setFont('Helvetica-Bold', 7) c.drawCentredString(lx, ly-3, name) # centre dot c.setFillColor(RED); c.circle(cx, cy, 2, fill=1, stroke=0) # axis zones c.setFont('Helvetica', 6); c.setFillColor(GREY) c.drawCentredString(cx, cy - r*0.45, 'Normal: -30° to +90°') print("Styles and helpers defined OK") PYEOF