Hypertension case proforma general medicine
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or missing"
~/hypertension-proforma/generate_proforma.py
#!/usr/bin/env python3
"""
Hypertension Case Proforma - General Medicine (MBBS Level)
Generates a comprehensive PDF case proforma using ReportLab.
"""
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,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.pdfgen import canvas
OUTPUT_PATH = "/home/daytona/workspace/hypertension-proforma/Hypertension_Case_Proforma.pdf"
W, H = A4 # 595.28 x 841.89 points
# ── Colour palette ──────────────────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1A3A5C")
MED_BLUE = colors.HexColor("#2E6DA4")
LIGHT_BLUE = colors.HexColor("#D6E4F0")
ACCENT = colors.HexColor("#C0392B")
GREY_BG = colors.HexColor("#F2F4F6")
LINE_GREY = colors.HexColor("#BDC3C7")
WHITE = colors.white
BLACK = colors.black
# ── Helper: dotted answer line ───────────────────────────────────────────────
def line(n=1, width="100%"):
"""Return a series of underline tables acting as blank fill lines."""
rows = []
for _ in range(n):
t = Table([["_" * 90]], colWidths=["100%"])
t.setStyle(TableStyle([
("TEXTCOLOR", (0, 0), (-1, -1), LINE_GREY),
("FONTSIZE", (0, 0), (-1, -1), 7),
("TOPPADDING", (0, 0), (-1, -1), 0),
("BOTTOMPADDING", (0, 0), (-1, -1), 2),
("LEFTPADDING", (0, 0), (-1, -1), 0),
]))
rows.append(t)
rows.append(Spacer(1, 1*mm))
return rows
def field_row(label, n_lines=1, label_width=5*cm):
"""Label + dotted line(s) as a two-column table."""
line_str = "_" * 70
lines_text = ("<br/>".join([line_str] * n_lines))
data = [[label, lines_text]]
t = Table(data, colWidths=[label_width, None])
t.setStyle(TableStyle([
("VALIGN", (0, 0), (-1, -1), "BOTTOM"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("TEXTCOLOR", (0, 0), (0, -1), DARK_BLUE),
("TEXTCOLOR", (1, 0), (1, -1), LINE_GREY),
("BOTTOMPADDING", (0, 0), (-1, -1), 2),
("TOPPADDING", (0, 0), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 0),
]))
return t
# ── Page template with header/footer ────────────────────────────────────────
class HeaderFooterCanvas(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):
page_count = len(self._saved_page_states)
for state in self._saved_page_states:
self.__dict__.update(state)
self.draw_page(page_count)
canvas.Canvas.showPage(self)
canvas.Canvas.save(self)
def draw_page(self, page_count):
self.saveState()
pg = self._pageNumber
# Header bar
self.setFillColor(DARK_BLUE)
self.rect(0, H - 1.2*cm, W, 1.2*cm, fill=1, stroke=0)
self.setFillColor(WHITE)
self.setFont("Helvetica-Bold", 11)
self.drawString(1.5*cm, H - 0.85*cm, "HYPERTENSION CASE PROFORMA")
self.setFont("Helvetica", 8)
self.drawRightString(W - 1.5*cm, H - 0.85*cm, "General Medicine | MBBS")
# Footer
self.setFillColor(DARK_BLUE)
self.rect(0, 0, W, 0.8*cm, fill=1, stroke=0)
self.setFillColor(WHITE)
self.setFont("Helvetica", 7.5)
self.drawString(1.5*cm, 0.28*cm, "Department of General Medicine")
self.drawCentredString(W/2, 0.28*cm, "CONFIDENTIAL - For Academic Use Only")
self.drawRightString(W - 1.5*cm, 0.28*cm, f"Page {pg} of {page_count}")
self.restoreState()
# ── Style helpers ────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
base = styles["Normal"]
return ParagraphStyle(name, parent=base, **kw)
TITLE = S("TITLE", fontSize=15, textColor=DARK_BLUE, fontName="Helvetica-Bold",
spaceAfter=2, alignment=TA_CENTER)
SEC = S("SEC", fontSize=10.5, textColor=WHITE, fontName="Helvetica-Bold",
spaceAfter=2, leading=14)
SUBSEC = S("SUBSEC", fontSize=9.5, textColor=DARK_BLUE, fontName="Helvetica-Bold",
spaceBefore=4, spaceAfter=2)
BODY = S("BODY", fontSize=9, textColor=BLACK, leading=13)
SMALL = S("SMALL", fontSize=8, textColor=colors.HexColor("#555555"), leading=11)
NOTE = S("NOTE", fontSize=8, textColor=ACCENT, fontName="Helvetica-Oblique")
LABEL = S("LABEL", fontSize=9, textColor=DARK_BLUE, fontName="Helvetica-Bold")
def section_header(text):
"""Blue banner heading."""
data = [[Paragraph(text, SEC)]]
t = Table(data, colWidths=[W - 3*cm])
t.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, -1), MED_BLUE),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("ROUNDEDCORNERS", [3]),
]))
return KeepTogether([Spacer(1, 4*mm), t, Spacer(1, 2*mm)])
def checkbox_row(items, cols=4):
"""Row of checkbox items."""
padded = items + [""] * (cols - len(items) % cols if len(items) % cols else 0)
rows = []
for i in range(0, len(padded), cols):
chunk = padded[i:i+cols]
row_data = [["☐ " + c if c else ""] for c in chunk]
flat = [["☐ " + c if c else "" for c in chunk]]
rows.append(flat[0])
t = Table(rows, colWidths=[(W - 3*cm) / cols] * cols)
t.setStyle(TableStyle([
("FONTSIZE", (0, 0), (-1, -1), 9),
("TEXTCOLOR", (0, 0), (-1, -1), BLACK),
("TOPPADDING", (0, 0), (-1, -1), 3),
("BOTTOMPADDING", (0, 0), (-1, -1), 3),
("LEFTPADDING", (0, 0), (-1, -1), 4),
]))
return t
def two_col_checklist(left_items, right_items, header_left="", header_right=""):
"""Two-column checklist table."""
max_len = max(len(left_items), len(right_items))
left_items = left_items + [""] * (max_len - len(left_items))
right_items = right_items + [""] * (max_len - len(right_items))
data = []
if header_left or header_right:
data.append([
Paragraph(header_left, SUBSEC),
Paragraph(header_right, SUBSEC)
])
for l, r in zip(left_items, right_items):
data.append([
("☐ " + l) if l else "",
("☐ " + r) if r else ""
])
half = (W - 3*cm) / 2
t = Table(data, colWidths=[half, half])
t.setStyle(TableStyle([
("FONTSIZE", (0, 0), (-1, -1), 9),
("TOPPADDING", (0, 0), (-1, -1), 3),
("BOTTOMPADDING", (0, 0), (-1, -1), 3),
("LEFTPADDING", (0, 0), (-1, -1), 4),
("LINEAFTER", (0, 0), (0, -1), 0.5, LINE_GREY),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold") if (header_left or header_right) else ("FONTSIZE", (0,0), (-1,-1), 9),
]))
return t
def bp_table():
"""JNC 8 / ACC/AHA 2017 BP classification table."""
headers = ["Category", "Systolic (mmHg)", "", "Diastolic (mmHg)"]
data = [
headers,
["Normal", "< 120", "AND", "< 80"],
["Elevated", "120-129", "AND", "< 80"],
["Stage 1 HTN", "130-139", "OR", "80-89"],
["Stage 2 HTN", "≥ 140", "OR", "≥ 90"],
["Hypertensive Crisis", "≥ 180", "AND/OR", "≥ 120"],
]
col_w = [(W - 3*cm) * f for f in [0.35, 0.25, 0.1, 0.30]]
t = Table(data, colWidths=col_w)
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, -1), 8.5),
("ROWBACKGROUNDS",(0, 1), (-1, -1), [WHITE, GREY_BG]),
("GRID", (0, 0), (-1, -1), 0.4, LINE_GREY),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("ALIGN", (1, 0), (-1, -1), "CENTER"),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
# Highlight hypertensive crisis row
("BACKGROUND", (0, 5), (-1, 5), colors.HexColor("#FDECEA")),
("TEXTCOLOR", (0, 5), (-1, 5), ACCENT),
("FONTNAME", (0, 5), (-1, 5), "Helvetica-Bold"),
]))
return t
# ── BUILD DOCUMENT ───────────────────────────────────────────────────────────
def build():
doc = SimpleDocTemplate(
OUTPUT_PATH,
pagesize=A4,
leftMargin=1.5*cm, rightMargin=1.5*cm,
topMargin=1.6*cm, bottomMargin=1.2*cm,
title="Hypertension Case Proforma",
author="Department of General Medicine",
)
story = []
# ── TITLE BLOCK ──────────────────────────────────────────────────────────
story.append(Spacer(1, 4*mm))
title_data = [[
Paragraph("HYPERTENSION", S("t1", fontSize=18, fontName="Helvetica-Bold",
textColor=DARK_BLUE, alignment=TA_CENTER)),
],[
Paragraph("CASE PROFORMA — General Medicine (MBBS)", S("t2", fontSize=11,
fontName="Helvetica", textColor=MED_BLUE, alignment=TA_CENTER)),
]]
tb = Table(title_data, colWidths=[W - 3*cm])
tb.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, -1), LIGHT_BLUE),
("TOPPADDING", (0, 0), (-1, -1), 8),
("BOTTOMPADDING", (0, 0), (-1, -1), 8),
("ROUNDEDCORNERS", [4]),
]))
story.append(tb)
story.append(Spacer(1, 6*mm))
# ── SECTION 1: PATIENT PARTICULARS ──────────────────────────────────────
story.append(section_header("SECTION 1 : PATIENT PARTICULARS"))
demo_data = [
["Name:", "_" * 35, "IP/OP No.:", "_" * 15],
["Age:", "_" * 12, "Sex:", "☐ Male ☐ Female ☐ Other"],
["Date of Admission:", "_" * 18, "Date of Examination:", "_" * 18],
["Ward / Bed No.:", "_" * 18, "Unit:", "_" * 18],
["Occupation:", "_" * 22, "Socioeconomic Status:", "☐ Upper ☐ Middle ☐ Lower"],
["Address:", "_" * 35, "Religion:", "_" * 15],
["Informant:", "_" * 22, "Reliability:", "☐ Good ☐ Fair ☐ Poor"],
]
cw = [(W-3*cm)*f for f in [0.14, 0.36, 0.18, 0.32]]
dt = Table(demo_data, colWidths=cw)
dt.setStyle(TableStyle([
("FONTSIZE", (0, 0), (-1, -1), 9),
("FONTNAME", (0, 0), (0, -1), "Helvetica-Bold"),
("FONTNAME", (0, 0), (2, -1), "Helvetica-Bold"),
("TEXTCOLOR", (0, 0), (0, -1), DARK_BLUE),
("TEXTCOLOR", (2, 0), (2, -1), DARK_BLUE),
("TEXTCOLOR", (1, 0), (1, -1), LINE_GREY),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 3),
("BACKGROUND", (0, 0), (-1, -1), GREY_BG),
("ROWBACKGROUNDS",(0, 0), (-1, -1), [WHITE, GREY_BG]),
]))
story.append(dt)
story.append(Spacer(1, 4*mm))
# ── SECTION 2: CHIEF COMPLAINTS ─────────────────────────────────────────
story.append(section_header("SECTION 2 : CHIEF COMPLAINTS"))
story.append(Paragraph(
"List complaints in chronological order with duration:", SMALL))
story.append(Spacer(1, 2*mm))
cc_data = [["No.", "Complaint", "Duration"]]
for i in range(1, 7):
cc_data.append([str(i), "_" * 45, "_" * 15])
cct = Table(cc_data, colWidths=[(W-3*cm)*f for f in [0.07, 0.72, 0.21]])
cct.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, -1), 9),
("ROWBACKGROUNDS",(0, 1), (-1, -1), [WHITE, GREY_BG]),
("GRID", (0, 0), (-1, -1), 0.4, LINE_GREY),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("ALIGN", (0, 0), (0, -1), "CENTER"),
("TEXTCOLOR", (1, 1), (1, -1), LINE_GREY),
("TEXTCOLOR", (2, 1), (2, -1), LINE_GREY),
]))
story.append(cct)
story.append(Spacer(1, 4*mm))
# ── SECTION 3: HISTORY OF PRESENT ILLNESS ───────────────────────────────
story.append(section_header("SECTION 3 : HISTORY OF PRESENT ILLNESS"))
story.append(Paragraph(
"<b>A. Onset of Hypertension</b>", SUBSEC))
hpi1 = [
["Age at diagnosis:", "_" * 20, "Known HTN since:", "_" * 18],
["Incidental finding (asymptomatic):", "☐ Yes ☐ No",
"Symptomatic at diagnosis:", "☐ Yes ☐ No"],
]
cw4 = [(W-3*cm)*f for f in [0.22, 0.28, 0.22, 0.28]]
t1 = Table(hpi1, colWidths=cw4)
t1.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 9),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("FONTNAME", (2,0),(2,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("TEXTCOLOR", (2,0),(2,-1), DARK_BLUE),
("TEXTCOLOR", (1,0),(1,-1), LINE_GREY),
("TOPPADDING",(0,0),(-1,-1), 5),
("BOTTOMPADDING",(0,0),(-1,-1), 5),
("ROWBACKGROUNDS",(0,0),(-1,-1),[WHITE, GREY_BG]),
]))
story.append(t1)
story.append(Spacer(1, 3*mm))
story.append(Paragraph("<b>B. Current Symptoms - Tick all that apply</b>", SUBSEC))
sym_data = [
["Headache (occipital)", "Blurred vision / Visual disturbance",
"Chest pain / Angina", "Palpitations"],
["Breathlessness (grade:", "Dizziness / Giddiness",
"Epistaxis", "Facial flushing"],
["Nocturia / Polyuria", "Leg swelling / Oedema",
"Weakness / Fatigue", "Tinnitus"],
["Haematuria", "Decreased urine output", "Claudication (leg pain on walking)",
"Facial / limb numbness"],
["Seizures", "Altered consciousness", "Neck stiffness", "Vomiting"],
]
sym_flat = [item for row in sym_data for item in row]
story.append(checkbox_row(sym_flat, cols=4))
story.append(Spacer(1, 2*mm))
story.append(field_row("Duration of current episode:", 1))
story.append(Spacer(1, 2*mm))
story.append(Paragraph("<b>Additional details of presenting complaint:</b>", SUBSEC))
for _ in range(3):
story.append(field_row("", 1, label_width=0.5*cm))
story.append(Spacer(1, 4*mm))
story.append(Paragraph("<b>C. Drug History (current antihypertensives)</b>", SUBSEC))
drug_data = [["Drug Name", "Dose", "Frequency", "Duration", "Compliance"]]
for _ in range(5):
drug_data.append(["_"*20, "_"*10, "_"*12, "_"*10, "☐ Regular ☐ Irregular"])
cwd = [(W-3*cm)*f for f in [0.27, 0.13, 0.17, 0.13, 0.30]]
dt2 = Table(drug_data, colWidths=cwd)
dt2.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,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 5),
("TEXTCOLOR", (0,1),(-1,-1), LINE_GREY),
]))
story.append(dt2)
story.append(Spacer(1, 2*mm))
story.append(field_row("Other medications (NSAIDs / OCP / steroids / decongestants):", 1, label_width=9*cm))
story.append(Spacer(1, 4*mm))
# ── SECTION 4: PAST HISTORY ──────────────────────────────────────────────
story.append(section_header("SECTION 4 : PAST HISTORY"))
past_items_l = [
"Diabetes Mellitus Type: _____ Since: ___",
"Coronary Artery Disease Since: ______",
"Previous MI / Stent / CABG",
"Stroke / TIA When: ____________",
"CKD / Renal Disease Stage: _____",
"Hyperlipidaemia",
"Thyroid Disorder",
"Sleep Apnoea",
]
past_items_r = [
"Peripheral Arterial Disease",
"Atrial Fibrillation",
"Heart Failure (HFrEF / HFpEF)",
"Aortic Aneurysm",
"Pheochromocytoma / Conn's",
"Previous surgery",
"Hospitalisation (reason): ________",
"Retinopathy Grade: ___________",
]
story.append(two_col_checklist(past_items_l, past_items_r,
"Cardiovascular / Metabolic", "Other Comorbidities"))
story.append(Spacer(1, 4*mm))
# ── SECTION 5: FAMILY HISTORY ────────────────────────────────────────────
story.append(section_header("SECTION 5 : FAMILY HISTORY"))
fam = [
["Condition", "Father", "Mother", "Sibling(s)", "Maternal Grand", "Paternal Grand"],
["Hypertension", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No"],
["Diabetes Mellitus","☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No"],
["Heart Disease", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No"],
["Stroke / CVA", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No"],
["Renal Disease", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No", "☐ Yes ☐ No"],
]
cwf = [(W-3*cm)*f for f in [0.22, 0.155, 0.155, 0.155, 0.155, 0.16]]
ft = Table(fam, colWidths=cwf)
ft.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,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("ALIGN", (1,0),(-1,-1), "CENTER"),
]))
story.append(ft)
story.append(Spacer(1, 2*mm))
story.append(field_row("Family history of early CVD (men <55 y, women <65 y):", 1, label_width=9*cm))
story.append(Spacer(1, 4*mm))
# ── SECTION 6: PERSONAL / SOCIAL HISTORY ────────────────────────────────
story.append(section_header("SECTION 6 : PERSONAL / SOCIAL HISTORY"))
social_data = [
["Smoking:", "☐ Never ☐ Current ☐ Ex-smoker",
"Pack-years:", "_________"],
["Alcohol:", "☐ Never ☐ Occasional ☐ Regular ☐ Binge",
"Units/week:", "_________"],
["Tobacco (chewing):", "☐ Never ☐ Current ☐ Former",
"Duration:", "_________"],
["Diet:", "☐ Vegetarian ☐ Non-vegetarian",
"Salt intake:", "☐ High ☐ Normal ☐ Restricted"],
["Physical activity:", "☐ Sedentary ☐ Moderate ☐ Active",
"BMI category:", "☐ Normal ☐ Overweight ☐ Obese"],
["Stress level:", "☐ Low ☐ Moderate ☐ High",
"Sleep (hrs/night):", "_________"],
]
soc_t = Table(social_data, colWidths=[(W-3*cm)*f for f in [0.18, 0.40, 0.14, 0.28]])
soc_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 9),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("FONTNAME", (2,0),(2,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("TEXTCOLOR", (2,0),(2,-1), DARK_BLUE),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.3, LINE_GREY),
("LEFTPADDING", (0,0),(-1,-1), 5),
]))
story.append(soc_t)
story.append(Spacer(1, 4*mm))
# ── SECTION 7: GENERAL PHYSICAL EXAMINATION ─────────────────────────────
story.append(section_header("SECTION 7 : GENERAL PHYSICAL EXAMINATION"))
story.append(Paragraph("<b>A. Vital Signs</b>", SUBSEC))
vs_data = [
["Blood Pressure", "Right Arm (Sitting):", "____/____ mmHg",
"Left Arm (Sitting):", "____/____ mmHg"],
["", "Right Arm (Standing):", "____/____ mmHg",
"Right Arm (Supine):", "____/____ mmHg"],
["", "Pulse Pressure:", "_____ mmHg",
"Mean Arterial Pressure:", "_____ mmHg"],
["Pulse Rate", "Rate:", "_____ bpm",
"Rhythm:", "☐ Regular ☐ Irregular"],
["", "Character:", "____________________",
"Volume:", "☐ Normal ☐ Raised ☐ Reduced"],
["Respiratory Rate", "_____ /min", "",
"Temperature:", "_____°F / °C"],
["SpO2", "_____% (Room air / O2)", "",
"Height:", "_____ cm"],
["Weight", "_____ kg", "",
"BMI:", "_____ kg/m²"],
["Waist Circumference", "_____ cm", "",
"Waist-Hip Ratio:", "_____"],
]
vs_t = Table(vs_data, colWidths=[(W-3*cm)*f for f in [0.19, 0.22, 0.22, 0.2, 0.17]])
vs_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 8.5),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.3, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("TEXTCOLOR", (2,0),(-1,-1), LINE_GREY),
("SPAN", (0,0),(0,2)), # BP spans 3 rows
("SPAN", (0,3),(0,4)), # Pulse spans 2 rows
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
]))
story.append(vs_t)
story.append(Spacer(1, 3*mm))
story.append(Paragraph("<b>B. General Appearance</b>", SUBSEC))
gen_items = [
"Conscious & oriented", "Cooperative", "Pallor", "Icterus",
"Cyanosis (central / peripheral)", "Clubbing", "Lymphadenopathy",
"Pedal oedema (Grade: ____)", "Raised JVP", "Xanthelasma / Xanthoma",
"Cushingoid facies", "Acromegalic features",
]
story.append(checkbox_row(gen_items, cols=4))
story.append(Spacer(1, 2*mm))
story.append(field_row("Other findings:", 1))
story.append(Spacer(1, 4*mm))
# ── SECTION 8: SYSTEMIC EXAMINATION ─────────────────────────────────────
story.append(section_header("SECTION 8 : SYSTEMIC EXAMINATION"))
# Cardiovascular
story.append(Paragraph("<b>A. Cardiovascular System</b>", SUBSEC))
cvs_data = [
["Apex Beat:", "Position: ____________", "Character: ____________",
"Forceful: ☐ Yes ☐ No"],
["Heart Sounds:", "S1: ☐ N ☐ Loud ☐ Soft",
"S2: ☐ N ☐ A2>P2 ☐ P2>A2", "S3/S4: ☐ Present ☐ Absent"],
["Murmurs:", "☐ Absent", "☐ Present - Site: _________",
"Grade: ___ Radiation: _________"],
["Peripheral Pulses:", "Radial: ☐ Present ☐ Absent",
"Femoral: ☐ Present ☐ Absent", "Dorsalis pedis: ☐ P ☐ A"],
["Radio-femoral delay:", "☐ Present ☐ Absent", "Bruit:", "☐ Carotid ☐ Renal ☐ None"],
]
cvs_t = Table(cvs_data, colWidths=[(W-3*cm)*f for f in [0.19, 0.27, 0.27, 0.27]])
cvs_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 8.5),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.3, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
]))
story.append(cvs_t)
story.append(Spacer(1, 3*mm))
# Respiratory
story.append(Paragraph("<b>B. Respiratory System</b>", SUBSEC))
story.append(checkbox_row([
"Air entry equal bilaterally", "Wheeze", "Crepitations",
"Pleural effusion (R / L)", "Normal vesicular breath sounds", "Rhonchi"], cols=3))
story.append(field_row("Additional findings:", 1))
story.append(Spacer(1, 3*mm))
# Abdomen
story.append(Paragraph("<b>C. Abdomen</b>", SUBSEC))
abd_items = [
"Liver: palpable ___ cm / not palpable",
"Spleen: palpable / not palpable",
"Kidneys: ballotable (R / L) / not palpable",
"Renal bruit: ☐ Present ☐ Absent",
"Ascites: ☐ Present ☐ Absent",
"Abdominal aortic pulsation: ☐ Normal ☐ Expansile",
]
story.append(checkbox_row(abd_items, cols=2))
story.append(Spacer(1, 3*mm))
# Neurological
story.append(Paragraph("<b>D. Neurological System</b>", SUBSEC))
neuro_data = [
["Higher functions:", "☐ Normal ☐ Impaired",
"Speech:", "☐ Normal ☐ Dysarthria ☐ Aphasia"],
["Cranial nerves:", "☐ Intact ☐ Deficit (specify: _______)",
"Pupils:", "☐ Equal ☐ Unequal ☐ Reacting"],
["Power (UL):", "R: ___/5 L: ___/5",
"Power (LL):", "R: ___/5 L: ___/5"],
["Reflexes:", "☐ Normal ☐ Brisk ☐ Absent",
"Plantar:", "☐ Flexor ☐ Extensor ☐ Equivocal"],
["Sensory:", "☐ Normal ☐ Reduced", "Coordination:", "☐ Normal ☐ Ataxia"],
["Fundoscopy:", "☐ Normal Grade: ___ ☐ Papilloedema ☐ Flame haemorrhages ☐ Exudates ☐ AV nipping", "", ""],
]
neuro_t = Table(neuro_data, colWidths=[(W-3*cm)*f for f in [0.15, 0.35, 0.15, 0.35]])
neuro_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 8.5),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.3, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("SPAN", (1,5),(3,5)),
]))
story.append(neuro_t)
story.append(Spacer(1, 4*mm))
# ── SECTION 9: INVESTIGATIONS ────────────────────────────────────────────
story.append(section_header("SECTION 9 : INVESTIGATIONS"))
# Routine
story.append(Paragraph("<b>A. Routine / Baseline Investigations</b>", SUBSEC))
inv_data = [
["Investigation", "Result", "Normal Range", "Interpretation"],
["Haemoglobin (g/dL)", "", "M: 13-17 F: 12-15", ""],
["Total WBC (cells/µL)", "", "4,000-11,000", ""],
["Platelet Count (lakh/µL)", "", "1.5-4.5", ""],
["Fasting Blood Glucose (mg/dL)", "", "70-100", ""],
["HbA1c (%)", "", "< 5.7 normal", ""],
["Serum Creatinine (mg/dL)", "", "M:0.7-1.3 F:0.5-1.1", ""],
["eGFR (mL/min/1.73m²)", "", "> 60 normal", ""],
["Blood Urea (mg/dL)", "", "7-20", ""],
["Serum Sodium (mEq/L)", "", "135-145", ""],
["Serum Potassium (mEq/L)", "", "3.5-5.0", ""],
["Serum Calcium (mg/dL)", "", "8.5-10.5", ""],
["Uric Acid (mg/dL)", "", "M:3.5-7.2 F:2.6-6.0", ""],
["Total Cholesterol (mg/dL)", "", "< 200 desirable", ""],
["LDL (mg/dL)", "", "< 100 optimal", ""],
["HDL (mg/dL)", "", "M:> 40 F:> 50", ""],
["Triglycerides (mg/dL)", "", "< 150", ""],
["TSH (mIU/L)", "", "0.5-4.5", ""],
["Urine Albumin-Creatinine Ratio", "", "< 30 normal", ""],
["Urine Routine & Microscopy", "", "No RBC/protein/casts", ""],
]
cw_inv = [(W-3*cm)*f for f in [0.35, 0.22, 0.23, 0.20]]
inv_t = Table(inv_data, colWidths=cw_inv)
inv_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,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LEFTPADDING", (0,0),(-1,-1), 5),
("ALIGN", (2,0),(-1,-1), "CENTER"),
]))
story.append(inv_t)
story.append(Spacer(1, 3*mm))
# Special investigations
story.append(Paragraph("<b>B. Cardiac & Imaging Investigations</b>", SUBSEC))
card_inv = [
["ECG findings:", "____________________________________________________________________"],
["Chest X-ray:", "☐ Cardiomegaly ☐ Pulmonary oedema ☐ Rib notching ☐ Normal"],
["2D Echo findings:", "____________________________________________________________________"],
["LV mass index:", "_______ g/m² (Normal: M<115, F<95)"],
["EF (%):", "_______ ☐ Normal (>55%) ☐ Reduced ☐ Preserved"],
["Renal USG:", "R kidney: ___cm L kidney: ___cm Cortical thickness: ___cm"],
["Renal Doppler:", "☐ Normal ☐ Renal artery stenosis ☐ Increased RI"],
["CT / MRI Brain:", "☐ Normal ☐ Lacunar infarcts ☐ Haemorrhage ☐ WMC"],
["24-hr Ambulatory BP:", "Daytime avg: ____/____ Night avg: ____/____ Dipping: ☐ Yes ☐ No"],
]
cw_c = [(W-3*cm)*f for f in [0.25, 0.75]]
card_t = Table(card_inv, colWidths=cw_c)
card_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 8.5),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.3, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("TEXTCOLOR", (1,0),(-1,-1), LINE_GREY),
]))
story.append(card_t)
story.append(Spacer(1, 3*mm))
# Secondary HTN workup
story.append(Paragraph("<b>C. Secondary Hypertension Workup (if indicated)</b>", SUBSEC))
sec_inv = [
["Plasma renin activity:", "_____________",
"Serum aldosterone:", "_____________"],
["Aldosterone/Renin ratio:", "_____________",
"24-hr urine catecholamines:", "_____________"],
["24-hr urine VMA:", "_____________",
"Plasma metanephrines:", "_____________"],
["Serum cortisol (AM):", "_____________",
"Overnight dex. suppression:", "_____________"],
["CT Adrenals:", "__________________________",
"MRI Pituitary:", "__________________________"],
["Sleep study (AHI):", "_____________",
"Thyroid function:", "_____________"],
["MIBG scan:", "_____________",
"Renal artery angiogram:", "_____________"],
]
sec_t = Table(sec_inv, colWidths=[(W-3*cm)*f for f in [0.22, 0.28, 0.22, 0.28]])
sec_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 8.5),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("FONTNAME", (2,0),(2,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("TEXTCOLOR", (2,0),(2,-1), DARK_BLUE),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.3, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("TEXTCOLOR", (1,0),(1,-1), LINE_GREY),
("TEXTCOLOR", (3,0),(3,-1), LINE_GREY),
]))
story.append(sec_t)
story.append(Spacer(1, 4*mm))
# ── SECTION 10: BP CLASSIFICATION ───────────────────────────────────────
story.append(section_header("SECTION 10 : BP CLASSIFICATION (ACC/AHA 2017)"))
story.append(bp_table())
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"<b>Patient's BP stage:</b> ☐ Normal ☐ Elevated ☐ Stage 1 HTN "
"☐ Stage 2 HTN ☐ Hypertensive Urgency ☐ Hypertensive Emergency",
BODY))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"<b>Type:</b> ☐ Primary (Essential) ☐ Secondary (cause: ___________________) "
"☐ Isolated Systolic ☐ Isolated Diastolic ☐ White-coat HTN",
BODY))
story.append(Spacer(1, 4*mm))
# ── SECTION 11: TARGET ORGAN DAMAGE ─────────────────────────────────────
story.append(section_header("SECTION 11 : TARGET ORGAN DAMAGE ASSESSMENT"))
tod_data = [
["Organ", "Evidence of Damage", "Severity"],
["Heart",
"☐ LVH on ECG/Echo ☐ HF ☐ CAD ☐ AF ☐ None",
"☐ Mild ☐ Mod ☐ Severe"],
["Brain",
"☐ Stroke/TIA ☐ Lacunar infarcts ☐ Cognitive impairment ☐ None",
"☐ Mild ☐ Mod ☐ Severe"],
["Kidney",
"☐ Microalbuminuria ☐ Proteinuria ☐ CKD ☐ None",
"☐ Mild ☐ Mod ☐ Severe"],
["Eye",
"☐ Grade I ☐ Grade II ☐ Grade III ☐ Grade IV retinopathy ☐ None",
"☐ Mild ☐ Mod ☐ Severe"],
["Peripheral Vessels",
"☐ PAD ☐ Aortic aneurysm ☐ Carotid plaque ☐ None",
"☐ Mild ☐ Mod ☐ Severe"],
]
tod_cw = [(W-3*cm)*f for f in [0.15, 0.65, 0.20]]
tod_t = Table(tod_data, colWidths=tod_cw)
tod_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,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 5),
]))
story.append(tod_t)
story.append(Spacer(1, 4*mm))
# ── SECTION 12: CARDIOVASCULAR RISK STRATIFICATION ──────────────────────
story.append(section_header("SECTION 12 : CARDIOVASCULAR RISK STRATIFICATION"))
story.append(Paragraph("<b>Risk Factors Present:</b>", SUBSEC))
rf_items = [
"Age ≥ 55 y (Male) / ≥ 65 y (Female)",
"Smoking",
"Dyslipidaemia (LDL > 130 or HDL low)",
"Diabetes Mellitus",
"Family history of premature CVD",
"Obesity (BMI ≥ 30)",
"Sedentary lifestyle",
"hs-CRP > 2 mg/L",
"Metabolic syndrome",
"Microalbuminuria / CKD Stage G3+",
]
story.append(checkbox_row(rf_items, cols=2))
story.append(Spacer(1, 2*mm))
risk_strat = [
["Risk Category", "Definition", "Patient Qualifies?"],
["Low", "No TOD, no RF, Stage 1 HTN", "☐"],
["Moderate", "1-2 RF, no TOD, Stage 1-2 HTN", "☐"],
["High", "≥ 3 RF OR 1 TOD, Stage 1-2 HTN", "☐"],
["Very High", "Known CVD / DM with TOD / Stage 3 HTN","☐"],
]
rs_t = Table(risk_strat, colWidths=[(W-3*cm)*f for f in [0.18, 0.62, 0.20]])
rs_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,-1), 9),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 5),
("ALIGN", (2,0),(-1,-1), "CENTER"),
]))
story.append(rs_t)
story.append(Spacer(1, 4*mm))
# ── SECTION 13: DIAGNOSIS ────────────────────────────────────────────────
story.append(section_header("SECTION 13 : DIAGNOSIS"))
dx_data = [
["Primary Diagnosis:", "_______________________________________________"],
["Secondary Diagnosis 1:", "_______________________________________________"],
["Secondary Diagnosis 2:", "_______________________________________________"],
["Complications:", "_______________________________________________"],
["Hypertensive Emergency:", "☐ Yes ☐ No If yes, type: _____________________"],
]
dx_t = Table(dx_data, colWidths=[(W-3*cm)*f for f in [0.26, 0.74]])
dx_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 9),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.3, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 6),
("BOTTOMPADDING", (0,0),(-1,-1), 6),
("LEFTPADDING", (0,0),(-1,-1), 5),
("TEXTCOLOR", (1,0),(-1,-1), LINE_GREY),
]))
story.append(dx_t)
story.append(Spacer(1, 4*mm))
# ── SECTION 14: MANAGEMENT PLAN ─────────────────────────────────────────
story.append(section_header("SECTION 14 : MANAGEMENT PLAN"))
story.append(Paragraph("<b>A. Non-Pharmacological (Lifestyle Modifications)</b>", SUBSEC))
nlm_items = [
"Weight reduction (target BMI < 25 kg/m²)",
"DASH diet / reduce saturated fat",
"Restrict sodium (< 2.3 g/day NaCl)",
"Increase potassium intake (diet)",
"Regular aerobic exercise (150 min/week)",
"Stop smoking / cessation counselling",
"Reduce alcohol (< 2 units/day)",
"Stress reduction / relaxation",
"Sleep hygiene (target 7-8 hrs)",
"Home BP monitoring education",
]
story.append(checkbox_row(nlm_items, cols=2))
story.append(Spacer(1, 3*mm))
story.append(Paragraph("<b>B. Pharmacological Treatment</b>", SUBSEC))
story.append(Paragraph(
"<i>First-line agents: ACE inhibitors, ARBs, CCBs, Thiazide diuretics. "
"Beta-blockers for specific indications (CHF, post-MI, AF rate control).</i>", SMALL))
story.append(Spacer(1, 2*mm))
rx_data = [["Drug", "Class", "Dose", "Frequency", "Indication / Rationale", "Precaution"]]
for _ in range(6):
rx_data.append(["", "", "", "", "", ""])
rx_cw = [(W-3*cm)*f for f in [0.18, 0.15, 0.12, 0.12, 0.27, 0.16]]
rx_t = Table(rx_data, colWidths=rx_cw)
rx_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,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 10),
("BOTTOMPADDING", (0,0),(-1,-1), 10),
("LEFTPADDING", (0,0),(-1,-1), 4),
]))
story.append(rx_t)
story.append(Spacer(1, 3*mm))
story.append(Paragraph("<b>C. Target BP Goals</b>", SUBSEC))
goal_data = [
["Patient Profile", "Target BP", "Evidence Base"],
["General HTN (< 65 y)", "< 130/80 mmHg", "ACC/AHA 2017"],
["Elderly (≥ 65 y)", "< 130/80 mmHg (SBP)", "SPRINT trial"],
["HTN + Diabetes", "< 130/80 mmHg", "ADA Standards 2024"],
["HTN + CKD (non-DM)", "< 130/80 mmHg", "KDIGO 2021"],
["HTN + CKD + Proteinuria", "< 130/80 mmHg (ACE/ARB preferred)", "KDIGO 2021"],
["HTN + CAD / Post-MI", "< 130/80 mmHg", "ACC/AHA 2017"],
["Hypertensive Emergency", "Reduce MAP by 25% in 1 hr, then 160/100 over 2-6 hr", "ESC 2018"],
]
goal_cw = [(W-3*cm)*f for f in [0.37, 0.30, 0.33]]
goal_t = Table(goal_data, colWidths=goal_cw)
goal_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), MED_BLUE),
("TEXTCOLOR", (0,0),(-1,0), WHITE),
("FONTNAME", (0,0),(-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0),(-1,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
]))
story.append(goal_t)
story.append(Spacer(1, 4*mm))
# ── SECTION 15: PROGRESS NOTES ──────────────────────────────────────────
story.append(section_header("SECTION 15 : DAILY PROGRESS NOTES"))
prog_data = [["Date", "BP (mmHg)", "PR (/min)", "Symptoms", "Assessment", "Plan / Changes"]]
for _ in range(6):
prog_data.append(["", "", "", "", "", ""])
prog_cw = [(W-3*cm)*f for f in [0.13, 0.15, 0.12, 0.20, 0.20, 0.20]]
prog_t = Table(prog_data, colWidths=prog_cw)
prog_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,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 18),
("BOTTOMPADDING", (0,0),(-1,-1), 18),
("LEFTPADDING", (0,0),(-1,-1), 4),
]))
story.append(prog_t)
story.append(Spacer(1, 4*mm))
# ── SECTION 16: DISCHARGE SUMMARY ───────────────────────────────────────
story.append(section_header("SECTION 16 : DISCHARGE SUMMARY"))
disc_data = [
["Date of Admission:", "_____________", "Date of Discharge:", "_____________"],
["Duration of Stay:", "_____________", "Condition at Discharge:",
"☐ Improved ☐ Stable ☐ Same ☐ DAMA"],
["Discharge BP:", "____/____ mmHg", "Discharge HR:", "_____ bpm"],
["Final Diagnosis:", "_____________________________",
"ICD-10 Code:", "___________"],
]
disc_t = Table(disc_data, colWidths=[(W-3*cm)*f for f in [0.20, 0.30, 0.22, 0.28]])
disc_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 9),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("FONTNAME", (2,0),(2,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("TEXTCOLOR", (2,0),(2,-1), DARK_BLUE),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.3, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 5),
("TEXTCOLOR", (1,0),(-1,-1), LINE_GREY),
]))
story.append(disc_t)
story.append(Spacer(1, 3*mm))
story.append(Paragraph("<b>Discharge Medications:</b>", SUBSEC))
disc_rx = [["Drug", "Dose", "Frequency", "Duration", "Special Instructions"]]
for _ in range(5):
disc_rx.append(["", "", "", "", ""])
disc_rx_cw = [(W-3*cm)*f for f in [0.25, 0.15, 0.15, 0.15, 0.30]]
disc_rx_t = Table(disc_rx, colWidths=disc_rx_cw)
disc_rx_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,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 10),
("BOTTOMPADDING", (0,0),(-1,-1), 10),
("LEFTPADDING", (0,0),(-1,-1), 4),
]))
story.append(disc_rx_t)
story.append(Spacer(1, 3*mm))
story.append(Paragraph("<b>Follow-up Instructions:</b>", SUBSEC))
fu_items = [
"Review in OPD in _____ days / weeks",
"Repeat BP check in _____ days",
"Repeat labs: ______________________",
"Ambulatory BP monitoring",
"Ophthalmology review",
"Nephrology referral",
"Cardiology referral",
"ECHO review in _____ months",
]
story.append(checkbox_row(fu_items, cols=2))
story.append(Spacer(1, 3*mm))
story.append(field_row("Patient education given on:", 1))
story.append(Spacer(1, 4*mm))
# ── SECTION 17: SIGNATURES ───────────────────────────────────────────────
story.append(section_header("SECTION 17 : SIGNATURES"))
sig_data = [
["Student Name:", "_________________________",
"Roll No.:", "_____________"],
["Signature:", "_________________________",
"Date:", "_____________"],
["", "", "", ""],
["Resident / PG Signature:", "_________________________",
"Date:", "_____________"],
["Consultant Signature:", "_________________________",
"Date:", "_____________"],
["Unit Chief / HOD Signature:", "_________________________",
"Stamp:", "_____________"],
]
sig_t = Table(sig_data, colWidths=[(W-3*cm)*f for f in [0.25, 0.35, 0.14, 0.26]])
sig_t.setStyle(TableStyle([
("FONTSIZE", (0,0),(-1,-1), 9),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("FONTNAME", (2,0),(2,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,0),(0,-1), DARK_BLUE),
("TEXTCOLOR", (2,0),(2,-1), DARK_BLUE),
("ROWBACKGROUNDS",(0,0),(-1,-1), [WHITE, GREY_BG]),
("TOPPADDING", (0,0),(-1,-1), 8),
("BOTTOMPADDING", (0,0),(-1,-1), 8),
("LEFTPADDING", (0,0),(-1,-1), 5),
("TEXTCOLOR", (1,0),(-1,-1), LINE_GREY),
("LINEBELOW", (0,-1),(-1,-1), 0.5, LINE_GREY),
]))
story.append(sig_t)
story.append(Spacer(1, 4*mm))
# ── APPENDIX: HYPERTENSION QUICK REFERENCE ──────────────────────────────
story.append(section_header("APPENDIX : QUICK REFERENCE - ANTIHYPERTENSIVE DRUG CLASSES"))
drug_ref = [
["Class", "Prototype Drug(s)", "Compelling Indications", "Contraindications / Cautions"],
["ACE Inhibitors\n(ACEi)", "Enalapril, Ramipril,\nLisinopril",
"DM, CKD proteinuria,\nHF, post-MI, LVD",
"Bilateral RAS, Pregnancy,\nHyperkalaemia, Angioedema Hx"],
["ARBs", "Losartan, Valsartan,\nTelmisartan",
"ACEi intolerant, DM,\nCKD, HF",
"Bilateral RAS, Pregnancy,\nHyperkalaemia"],
["CCBs\n(Dihydropyridine)", "Amlodipine, Nifedipine",
"Elderly ISH, Angina,\nAfro-Caribbean patients",
"Severe aortic stenosis\n(caution)"],
["CCBs\n(Non-DHP)", "Diltiazem, Verapamil",
"Angina, AF rate control,\nSVT",
"Heart failure (EF reduced),\nAV block, Bradycardia"],
["Thiazide Diuretics", "Hydrochlorothiazide,\nChlorthalidone, Indapamide",
"Elderly, ISH,\nOsteoporosis",
"Gout, Hypokalaemia,\nGlucose intolerance"],
["Beta-Blockers", "Atenolol, Metoprolol,\nCarvedilol, Bisoprolol",
"Post-MI, Angina, HF,\nAF rate control, Pregnancy (labetalol)",
"Asthma, AV block,\nPeripheral arterial disease"],
["Aldosterone Antag.", "Spironolactone,\nEplerenone",
"Resistant HTN, HF,\nConn's syndrome",
"Hyperkalaemia, CKD\n(severe), renal failure"],
["Alpha-Blockers", "Prazosin, Doxazosin",
"Resistant HTN,\nBPH, Phaeochromocytoma",
"Postural hypotension,\n1st dose effect"],
["Central Agents", "Methyldopa, Clonidine",
"Pregnancy (methyldopa)\nRefractory HTN",
"Depression, Rebound HTN\non withdrawal (clonidine)"],
]
drug_ref_cw = [(W-3*cm)*f for f in [0.18, 0.22, 0.28, 0.32]]
drug_ref_t = Table(drug_ref, colWidths=drug_ref_cw)
drug_ref_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,-1), 8),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "TOP"),
("FONTNAME", (0,1),(0,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,1),(0,-1), MED_BLUE),
]))
story.append(drug_ref_t)
story.append(Spacer(1, 3*mm))
# Keith-Wagener-Barker Fundoscopy grading
story.append(Paragraph("<b>Keith-Wagener-Barker Fundoscopy Grading:</b>", SUBSEC))
kwb = [
["Grade", "Features"],
["Grade I", "Mild narrowing / tortuosity of arteries, increased light reflex (copper wire)"],
["Grade II", "More severe narrowing, A-V nipping (Gunn's sign), silver wire appearance"],
["Grade III","Flame haemorrhages, dot & blot haemorrhages, cotton wool spots (soft exudates)"],
["Grade IV", "Grade III features + Papilloedema (bilateral disc oedema)"],
]
kwb_cw = [(W-3*cm)*f for f in [0.10, 0.90]]
kwb_t = Table(kwb, colWidths=kwb_cw)
kwb_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), MED_BLUE),
("TEXTCOLOR", (0,0),(-1,0), WHITE),
("FONTNAME", (0,0),(-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0),(-1,-1), 8.5),
("ROWBACKGROUNDS",(0,1),(-1,-1), [WHITE, GREY_BG]),
("GRID", (0,0),(-1,-1), 0.4, LINE_GREY),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("FONTNAME", (0,1),(0,-1), "Helvetica-Bold"),
("TEXTCOLOR", (0,1),(0,-1), ACCENT),
]))
story.append(kwb_t)
story.append(Spacer(1, 3*mm))
story.append(Paragraph(
"<b>References:</b> ACC/AHA 2017 Hypertension Guidelines | "
"ESC/ESH 2018 HTN Guidelines | JNC 8 | WHO/ISH 2020 | "
"KDIGO 2021 BP in CKD | ADA Standards of Medical Care 2024",
SMALL))
story.append(Spacer(1, 3*mm))
story.append(HRFlowable(width="100%", thickness=0.5, color=LINE_GREY))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
"This proforma is intended for academic/educational use. "
"Clinical decisions must be guided by current institutional protocols and attending physician judgement.",
NOTE))
# Build
doc.build(story, canvasmaker=HeaderFooterCanvas)
print(f"PDF generated: {OUTPUT_PATH}")
if __name__ == "__main__":
import os
os.makedirs("/home/daytona/workspace/hypertension-proforma", exist_ok=True)
build()
cd /home/daytona/workspace/hypertension-proforma && python3 generate_proforma.py 2>&1
ls -lh /home/daytona/workspace/hypertension-proforma/Hypertension_Case_Proforma.pdf
Hypertension Case Proforma
PDF Document · PDF
| Section | Content |
|---|---|
| 1 | Patient Particulars (demographics, IP/OP No., socioeconomic status) |
| 2 | Chief Complaints (tabulated with duration) |
| 3 | History of Present Illness - onset, symptoms checklist, drug history, compliance |
| 4 | Past History (DM, CAD, MI, stroke, CKD, phaeochromocytoma, etc.) |
| 5 | Family History (6-generation table for HTN, DM, heart disease, stroke, renal) |
| 6 | Personal / Social History (smoking, alcohol, diet, exercise, stress) |
| 7 | General Physical Examination - vital signs (both arms BP), BMI, waist-hip ratio |
| 8 | Systemic Examination - CVS, respiratory, abdomen, full neurological, fundoscopy |
| 9 | Investigations - baseline labs (20 parameters), cardiac/imaging, secondary HTN workup |
| 10 | BP Classification table (ACC/AHA 2017 - Normal to Hypertensive Crisis) |
| 11 | Target Organ Damage (heart, brain, kidney, eye, peripheral vessels) |
| 12 | CV Risk Stratification (Low / Moderate / High / Very High) |
| 13 | Diagnosis block with ICD-10 space |
| 14 | Management - lifestyle modifications, drug table, target BP goals by comorbidity |
| 15 | Daily Progress Notes table |
| 16 | Discharge Summary + discharge medications + follow-up plan |
| 17 | Signatures (student, resident, consultant, unit chief) |
| Appendix | Antihypertensive drug class quick reference + Keith-Wagener-Barker grading |