Jaundice case sheet for medicine
mkdir -p /home/daytona/workspace/jaundice-case-sheet && ls /home/daytona/workspace/
~/jaundice-case-sheet/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import PageBreak
OUTPUT = "/home/daytona/workspace/jaundice-case-sheet/Jaundice_Case_Sheet.pdf"
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=1.8*cm,
rightMargin=1.8*cm,
topMargin=1.5*cm,
bottomMargin=1.5*cm,
)
W, H = A4
PAGE_W = W - 3.6*cm
styles = getSampleStyleSheet()
# ── Custom styles ──────────────────────────────────────────────────────────
GOLD = colors.HexColor("#B8860B")
NAVY = colors.HexColor("#1A237E")
TEAL = colors.HexColor("#00695C")
LGRAY = colors.HexColor("#F5F5F5")
MGRAY = colors.HexColor("#BDBDBD")
WHITE = colors.white
BLACK = colors.black
RED = colors.HexColor("#B71C1C")
def ps(name, parent="Normal", **kw):
return ParagraphStyle(name, parent=styles[parent], **kw)
title_style = ps("TitleS", fontSize=18, leading=22, alignment=TA_CENTER,
textColor=WHITE, fontName="Helvetica-Bold", spaceAfter=2)
subtitle_style = ps("SubTitle", fontSize=10, leading=13, alignment=TA_CENTER,
textColor=colors.HexColor("#CFD8DC"), fontName="Helvetica",
spaceAfter=0)
sec_style = ps("SecHead", fontSize=11, leading=14, textColor=WHITE,
fontName="Helvetica-Bold", spaceBefore=4, spaceAfter=2)
sub_style = ps("SubHead", fontSize=9.5, leading=12, textColor=NAVY,
fontName="Helvetica-Bold", spaceBefore=3, spaceAfter=1)
body_style = ps("Body", fontSize=8.5, leading=11, textColor=BLACK,
fontName="Helvetica", spaceAfter=1)
small_style = ps("Small", fontSize=7.5, leading=10, textColor=colors.HexColor("#424242"),
fontName="Helvetica")
label_style = ps("Label", fontSize=8, leading=10, textColor=NAVY,
fontName="Helvetica-Bold")
note_style = ps("Note", fontSize=7.5, leading=10,
textColor=colors.HexColor("#5D4037"), fontName="Helvetica-Oblique")
# ── Helpers ────────────────────────────────────────────────────────────────
def section_header(text, bg=NAVY):
tbl = Table([[Paragraph(text, sec_style)]], colWidths=[PAGE_W])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("ROUNDEDCORNERS", [3,3,3,3]),
]))
return tbl
def field_row(label, width=8*cm, gap=0.3*cm, total=PAGE_W):
"""Single labelled underline field."""
line_w = total - width - gap
row = Table(
[[Paragraph(label, label_style), ""]],
colWidths=[width, line_w]
)
row.setStyle(TableStyle([
("LINEBELOW", (1,0), (1,0), 0.5, MGRAY),
("VALIGN", (0,0), (-1,-1), "BOTTOM"),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("TOPPADDING", (0,0), (-1,-1), 1),
]))
return row
def two_fields(l1, l2, w1=4*cm, w2=4*cm):
half = PAGE_W / 2 - 0.2*cm
r1 = field_row(l1, width=w1, total=half)
r2 = field_row(l2, width=w2, total=half)
tbl = Table([[r1, r2]], colWidths=[half, half])
tbl.setStyle(TableStyle([("LEFTPADDING",(1,0),(1,0),6)]))
return tbl
def checkbox_row(items, cols=3):
"""A row of checkbox items."""
n = len(items)
col_w = PAGE_W / cols
# pad to full rows
while len(items) % cols:
items.append("")
rows = [items[i:i+cols] for i in range(0, len(items), cols)]
data = [[Paragraph(f"☐ {x}" if x else "", small_style) for x in r] for r in rows]
tbl = Table(data, colWidths=[col_w]*cols)
tbl.setStyle(TableStyle([
("TOPPADDING",(0,0),(-1,-1),2),
("BOTTOMPADDING",(0,0),(-1,-1),2),
]))
return tbl
def yes_no_row(label):
tbl = Table(
[[Paragraph(label, body_style),
Paragraph("☐ Yes", small_style),
Paragraph("☐ No", small_style), ""]],
colWidths=[PAGE_W*0.50, 1.5*cm, 1.5*cm, PAGE_W*0.35]
)
tbl.setStyle(TableStyle([
("LINEBELOW",(3,0),(3,0),0.5,MGRAY),
("VALIGN",(0,0),(-1,-1),"MIDDLE"),
("BOTTOMPADDING",(0,0),(-1,-1),2),
("TOPPADDING",(0,0),(-1,-1),1),
]))
return tbl
def lined_area(lines=3, label=None):
rows = []
if label:
rows.append([Paragraph(label, label_style)])
for _ in range(lines):
rows.append([""])
tbl = Table(rows, colWidths=[PAGE_W])
style_cmds = [
("TOPPADDING",(0,0),(-1,-1),0),
("BOTTOMPADDING",(0,0),(-1,-1),7),
]
start = 1 if label else 0
for i in range(start, len(rows)):
style_cmds.append(("LINEBELOW",(0,i),(0,i),0.4,MGRAY))
tbl.setStyle(TableStyle(style_cmds))
return tbl
def sp(n=4):
return Spacer(1, n)
# ── Build content ──────────────────────────────────────────────────────────
story = []
# ═══════════════════════════════════════════════════════════════════
# TITLE BANNER
# ═══════════════════════════════════════════════════════════════════
banner_data = [[
Paragraph("JAUNDICE", title_style),
Paragraph("Complete History & Examination Proforma", subtitle_style),
]]
banner = Table([[
Paragraph("JAUNDICE", title_style),
]], colWidths=[PAGE_W])
banner.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,-1), NAVY),
("TOPPADDING",(0,0),(-1,-1),10),
("BOTTOMPADDING",(0,0),(-1,-1),4),
("LEFTPADDING",(0,0),(-1,-1),12),
]))
sub_banner = Table([[
Paragraph("Complete History & Examination Proforma | Medicine Department", subtitle_style),
Paragraph("Date: _________________ IP/OP No: _______________", subtitle_style),
]], colWidths=[PAGE_W*0.6, PAGE_W*0.4])
sub_banner.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,-1), TEAL),
("TOPPADDING",(0,0),(-1,-1),4),
("BOTTOMPADDING",(0,0),(-1,-1),4),
("LEFTPADDING",(0,0),(-1,-1),12),
]))
story += [banner, sub_banner, sp(8)]
# ═══════════════════════════════════════════════════════════════════
# 1. PATIENT IDENTIFICATION
# ═══════════════════════════════════════════════════════════════════
story += [section_header("1. PATIENT IDENTIFICATION"), sp(4)]
id_data = [
[Paragraph("Name:", label_style), "", Paragraph("Age:", label_style), "",
Paragraph("Sex:", label_style), ""],
[Paragraph("Address:", label_style), "", Paragraph("Occupation:", label_style), "",
Paragraph("Religion:", label_style), ""],
[Paragraph("Referred by:", label_style), "", Paragraph("Ward/Bed:", label_style), "",
Paragraph("Informant:", label_style), ""],
]
cw = [2.5*cm, 4.8*cm, 2*cm, 3.5*cm, 1.8*cm, 3.5*cm]
id_tbl = Table(id_data, colWidths=cw)
id_tbl.setStyle(TableStyle([
("LINEBELOW",(1,r),(1,r),0.5,MGRAY) for r in range(3)
] + [
("LINEBELOW",(3,r),(3,r),0.5,MGRAY) for r in range(3)
] + [
("LINEBELOW",(5,r),(5,r),0.5,MGRAY) for r in range(3)
] + [
("BOTTOMPADDING",(0,0),(-1,-1),6),
("TOPPADDING",(0,0),(-1,-1),2),
("VALIGN",(0,0),(-1,-1),"BOTTOM"),
]))
story += [id_tbl, sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 2. CHIEF COMPLAINT
# ═══════════════════════════════════════════════════════════════════
story += [section_header("2. CHIEF COMPLAINT"), sp(4)]
story.append(Paragraph("Yellowish discolouration of:", sub_style))
story.append(checkbox_row(["Eyes (Scleral icterus)", "Skin", "Mucous membranes", "Urine (dark)", "Stool (pale/clay)", "Nails"], cols=3))
story += [sp(3), field_row("Duration of jaundice:", width=6*cm), sp(3)]
story.append(lined_area(lines=2, label="Other chief complaints (with duration):"))
story += [sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 3. HISTORY OF PRESENT ILLNESS
# ═══════════════════════════════════════════════════════════════════
story += [section_header("3. HISTORY OF PRESENT ILLNESS"), sp(4)]
story.append(Paragraph("A. Onset & Progression", sub_style))
onset_items = [
("Onset:", ["☐ Sudden", "☐ Gradual"]),
("Progression:", ["☐ Progressive", "☐ Intermittent", "☐ Remitting"]),
("Course:", ["☐ Worsening", "☐ Improving", "☐ Static"]),
]
for lbl, opts in onset_items:
row_data = [[Paragraph(lbl, label_style)] + [Paragraph(o, small_style) for o in opts] + [""]]
cws = [3*cm] + [3.2*cm]*len(opts) + [PAGE_W - 3*cm - 3.2*cm*len(opts)]
rt = Table(row_data, colWidths=cws)
rt.setStyle(TableStyle([("BOTTOMPADDING",(0,0),(-1,-1),3),("TOPPADDING",(0,0),(-1,-1),1)]))
story.append(rt)
story += [sp(4), Paragraph("B. Characterisation of Jaundice", sub_style)]
char_items = [
"Preceded by prodrome (nausea, malaise, anorexia)",
"Associated with fever / rigors",
"Associated with severe RUQ pain → cholangitis / choledocholithiasis",
"Associated with pruritus → cholestasis",
"Associated with pale/clay-coloured stools → obstructive",
"Associated with dark urine (bilirubin)",
"Associated with weight loss / anorexia → malignancy",
"Associated with arthralgias / myalgias → viral hepatitis",
"Associated with rash / urticaria",
"History of similar episodes in the past",
]
for item in char_items:
story.append(yes_no_row(item))
story += [sp(4), Paragraph("C. Pain Assessment (if present)", sub_style)]
pain_data = [
[Paragraph("Site:", label_style), "", Paragraph("Radiation:", label_style), ""],
[Paragraph("Character:", label_style), "", Paragraph("Severity (0–10):", label_style), ""],
[Paragraph("Aggravating:", label_style), "", Paragraph("Relieving:", label_style), ""],
]
pcw = [3*cm, 6.5*cm, 3.5*cm, PAGE_W-13*cm]
ptbl = Table(pain_data, colWidths=pcw)
ptbl.setStyle(TableStyle(
[("LINEBELOW",(1,r),(1,r),0.5,MGRAY) for r in range(3)] +
[("LINEBELOW",(3,r),(3,r),0.5,MGRAY) for r in range(3)] +
[("BOTTOMPADDING",(0,0),(-1,-1),6),("TOPPADDING",(0,0),(-1,-1),1),("VALIGN",(0,0),(-1,-1),"BOTTOM")]
))
story += [ptbl, sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 4. CAUSATIVE / RISK FACTOR HISTORY
# ═══════════════════════════════════════════════════════════════════
story += [section_header("4. CAUSATIVE / RISK FACTOR HISTORY"), sp(4)]
story.append(Paragraph("A. Pre-hepatic (Haemolytic) Risk Factors", sub_style))
prehepa = [
"Known haemolytic anaemia (SCA, thalassaemia, G6PD deficiency)",
"Recent blood transfusion / transfusion reaction",
"Malaria / septicaemia",
"Snake bite / toxin exposure",
"Autoimmune haemolytic anaemia",
"Prosthetic heart valve (mechanical haemolysis)",
]
story.append(checkbox_row(prehepa, cols=2))
story += [sp(3), Paragraph("B. Hepatocellular Risk Factors", sub_style)]
hepato = [
"Viral hepatitis (Hep A/B/C/D/E) — known or suspected",
"IV drug use / needle sharing",
"Multiple sexual partners / unprotected sex",
"Tattoos / body piercing",
"Recent blood/blood product transfusion",
"Alcohol consumption (units/day: ______)",
"Hepatotoxic drugs (paracetamol OD, INH, rifampicin, statins)",
"Herbal / traditional / alternative medicines",
"Recent anaesthetic exposure (halothane)",
"Autoimmune hepatitis (F, young)",
"Wilson's disease (young patient, neuropsychiatric features)",
"NAFLD / metabolic syndrome (obesity, DM, dyslipidaemia)",
]
story.append(checkbox_row(hepato, cols=2))
story += [sp(3), Paragraph("C. Cholestatic / Obstructive Risk Factors", sub_style)]
obstruct = [
"Gallstones (prior biliary colic)",
"Prior biliary / hepatic surgery",
"ERCP / biliary instrumentation",
"Pancreatic pathology (pain radiating to back, weight loss)",
"Cholangiocarcinoma risk (PSC, liver flukes, choledochal cyst)",
"Drugs causing cholestasis (OCP, chlorpromazine, anabolic steroids)",
"Pregnancy (intrahepatic cholestasis of pregnancy)",
"Congenital biliary atresia (neonate)",
]
story.append(checkbox_row(obstruct, cols=2))
story += [sp(3), Paragraph("D. Epidemiological / Travel History", sub_style)]
epi_items = [
("Recent travel to endemic area (HAV/HEV):", ["☐ Yes", "☐ No"]),
("Contaminated food/water (shellfish, street food):", ["☐ Yes", "☐ No"]),
("Close contact with jaundiced person:", ["☐ Yes", "☐ No"]),
("Occupational exposure (healthcare, farming, sewage):", ["☐ Yes", "☐ No"]),
]
for lbl, opts in epi_items:
row_data = [[Paragraph(lbl, body_style)] + [Paragraph(o, small_style) for o in opts] + [""]]
cws2 = [PAGE_W*0.55, 1.8*cm, 1.8*cm, PAGE_W*0.28]
rt2 = Table(row_data, colWidths=cws2)
rt2.setStyle(TableStyle([("BOTTOMPADDING",(0,0),(-1,-1),3),("TOPPADDING",(0,0),(-1,-1),1),
("LINEBELOW",(3,0),(3,0),0.4,MGRAY)]))
story.append(rt2)
story += [sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 5. PAST HISTORY
# ═══════════════════════════════════════════════════════════════════
story += [section_header("5. PAST HISTORY"), sp(4)]
past = [
("Previous jaundice episodes:", True),
("Liver disease / cirrhosis:", True),
("Hepatitis B / C (carrier status):", True),
("Gallstones / biliary disease:", True),
("Pancreatitis:", True),
("Malignancy:", True),
("Haematological disorders:", True),
("Inflammatory bowel disease:", True),
("Cardiac failure / Budd-Chiari:", True),
("Diabetes / hypertension / dyslipidaemia:", True),
("Previous abdominal surgery:", True),
("Blood transfusions / organ transplant:", True),
]
for item, yn in past:
story.append(yes_no_row(item))
story += [sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 6. DRUG & ALLERGY HISTORY
# ═══════════════════════════════════════════════════════════════════
story += [section_header("6. DRUG & ALLERGY HISTORY"), sp(4)]
story.append(lined_area(lines=2, label="Current medications (include OTC, herbal, supplements):"))
story += [sp(3)]
story.append(lined_area(lines=1, label="Allergies (drug / food / environmental):"))
story.append(Paragraph("Alcohol:", sub_style))
alc_row = Table([[
Paragraph("☐ Non-drinker", small_style),
Paragraph("☐ Social drinker", small_style),
Paragraph("☐ Regular (units/wk: ___)", small_style),
Paragraph("☐ Dependent (CAGE score: ___)", small_style),
]], colWidths=[PAGE_W/4]*4)
story += [alc_row, sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 7. PERSONAL & SOCIAL HISTORY
# ═══════════════════════════════════════════════════════════════════
story += [section_header("7. PERSONAL & SOCIAL HISTORY"), sp(4)]
personal_items = [
"Diet: ☐ Vegetarian ☐ Mixed ☐ Vegan",
"Smoking: ☐ Never ☐ Current (pack-years: ___) ☐ Ex-smoker",
"Recreational drugs: ☐ None ☐ IV drugs ☐ Oral ☐ Inhaled",
"Sexual history: ☐ Monogamous ☐ Multiple partners ☐ MSM",
"Marital status: ☐ Single ☐ Married ☐ Widowed",
"Socioeconomic status: ☐ Low ☐ Middle ☐ High",
"Sanitation: ☐ Adequate ☐ Inadequate (open defecation area)",
"Water source: ☐ Piped ☐ Borewell ☐ River/pond/open",
]
for pi in personal_items:
story.append(Paragraph(pi, small_style))
story.append(sp(2))
story += [sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 8. FAMILY HISTORY
# ═══════════════════════════════════════════════════════════════════
story += [section_header("8. FAMILY HISTORY"), sp(4)]
fam_items = [
"Liver disease / cirrhosis",
"Haemolytic anaemia / haemoglobinopathy",
"Wilson's disease / haemochromatosis",
"Gilbert's syndrome",
"Malignancy (GI / hepatic / pancreatic)",
"Hepatitis B/C in household contacts",
]
story.append(checkbox_row(fam_items, cols=2))
story += [sp(4), lined_area(lines=1, label="Details:"), sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 9. REVIEW OF SYSTEMS
# ═══════════════════════════════════════════════════════════════════
story += [section_header("9. REVIEW OF SYSTEMS"), sp(4)]
ros_data = [
["GI:", "☐ Nausea ☐ Vomiting ☐ Haematemesis ☐ Malaena ☐ Diarrhoea ☐ Constipation"],
["Hepatic:", "☐ Abdominal distension ☐ Ascites ☐ Haematemesis (varices) ☐ Encephalopathy (flap)"],
["Constitutional:", "☐ Fever ☐ Rigors ☐ Weight loss ☐ Anorexia ☐ Fatigue ☐ Night sweats"],
["Urine:", "☐ Dark (bilirubinuria) ☐ Frothy ☐ Decreased output ☐ Haematuria"],
["Stool:", "☐ Pale/clay ☐ Steatorrhoea ☐ Acholic ☐ Normal colour"],
["CVS:", "☐ Palpitations ☐ Oedema ☐ Dyspnoea ☐ Chest pain"],
["Skin:", "☐ Pruritus ☐ Xanthomas ☐ Spider naevi ☐ Purpura/bruising"],
["Neuro:", "☐ Confusion ☐ Tremor (flap) ☐ Altered personality ☐ Drowsiness"],
["MSK:", "☐ Arthralgia ☐ Myalgia ☐ Muscle wasting"],
]
ros_tbl = Table(ros_data, colWidths=[2.3*cm, PAGE_W-2.3*cm])
ros_tbl.setStyle(TableStyle([
("FONTNAME",(0,0),(0,-1),"Helvetica-Bold"),
("FONTNAME",(1,0),(1,-1),"Helvetica"),
("FONTSIZE",(0,0),(-1,-1),8),
("BOTTOMPADDING",(0,0),(-1,-1),3),
("TOPPADDING",(0,0),(-1,-1),1),
("ROWBACKGROUNDS",(0,0),(-1,-1),[WHITE, LGRAY]),
]))
story += [ros_tbl, sp(6)]
# ═══════════════════════════════════════════════════════════════════
# PAGE BREAK → EXAMINATION
# ═══════════════════════════════════════════════════════════════════
story.append(PageBreak())
# ═══════════════════════════════════════════════════════════════════
# 10. GENERAL EXAMINATION
# ═══════════════════════════════════════════════════════════════════
story += [section_header("10. GENERAL EXAMINATION"), sp(4)]
story.append(Paragraph("Vital Signs", sub_style))
vitals_data = [
["Pulse:", "___ /min Reg/Irreg Vol:", "BP:", "___ / ___ mmHg Arm: R/L"],
["Temp:", "___ °F / °C Oral/Axillary", "RR:", "___ /min"],
["SpO₂:", "___ % RA / O₂", "Wt:", "___ kg Ht: ___ cm BMI: ___"],
]
vtbl = Table(vitals_data, colWidths=[1.8*cm, PAGE_W/2-1.8*cm, 1.5*cm, PAGE_W/2-1.5*cm])
vtbl.setStyle(TableStyle([
("FONTNAME",(0,0),(0,-1),"Helvetica-Bold"),("FONTNAME",(2,0),(2,-1),"Helvetica-Bold"),
("FONTNAME",(1,0),(1,-1),"Helvetica"),("FONTNAME",(3,0),(3,-1),"Helvetica"),
("FONTSIZE",(0,0),(-1,-1),8.5),
("BOTTOMPADDING",(0,0),(-1,-1),5),("TOPPADDING",(0,0),(-1,-1),2),
("BOX",(0,0),(-1,-1),0.5,MGRAY),("INNERGRID",(0,0),(-1,-1),0.3,MGRAY),
("BACKGROUND",(0,0),(0,-1),LGRAY),("BACKGROUND",(2,0),(2,-1),LGRAY),
]))
story += [vtbl, sp(5)]
story.append(Paragraph("Icterus Assessment", sub_style))
ic_data = [
["Grade:", "☐ 0 – Absent", "☐ 1+ – Scleral only (serum bili 2–4 mg/dL)"],
["", "☐ 2+ – Scleral + skin", "☐ 3+ – Deep jaundice (bili >10 mg/dL)"],
]
ict = Table(ic_data, colWidths=[1.8*cm, 5.5*cm, PAGE_W-7.3*cm])
ict.setStyle(TableStyle([
("FONTNAME",(0,0),(0,-1),"Helvetica-Bold"),
("FONTSIZE",(0,0),(-1,-1),8.5),
("BOTTOMPADDING",(0,0),(-1,-1),3),
]))
story += [ict, sp(4)]
story.append(Paragraph("Stigmata of Chronic Liver Disease", sub_style))
cld_signs = [
"Palmar erythema", "Leuconychia / Terry's nails", "Clubbing",
"Dupuytren's contracture", "Spider naevi (count: ___)", "Gynaecomastia",
"Testicular atrophy", "Parotid enlargement", "Caput medusae",
"Flapping tremor (asterixis)", "Fetor hepaticus", "Muscle wasting",
"Loss of body hair", "Xanthelasma / xanthomas",
]
story.append(checkbox_row(cld_signs, cols=3))
story += [sp(4)]
story.append(Paragraph("Other General Signs", sub_style))
other_gen = [
"Pallor (anaemia → haemolytic/hepatic)", "Cyanosis", "Clubbing (chronic liver / IBD)",
"Lymphadenopathy: ☐ Cervical ☐ Axillary ☐ Inguinal ☐ Virchow's node",
"Oedema: ☐ Pedal ☐ Pitting grade: ___",
"Skin rash / urticaria / purpura", "Xanthomas / xanthelasmas",
"Kayser-Fleischer rings (slit-lamp if Wilson's suspected)",
]
story.append(checkbox_row(other_gen, cols=2))
story += [sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 11. SYSTEMIC EXAMINATION
# ═══════════════════════════════════════════════════════════════════
story += [section_header("11. SYSTEMIC EXAMINATION"), sp(4)]
# ABDOMEN
story.append(Paragraph("A. Abdomen", sub_style))
abd_data = [
["Inspection:", "☐ Distended ☐ Flat ☐ Scaphoid ☐ Visible veins (caput medusae) ☐ Surgical scars"],
["Palpation – Liver:", "Size: ___ cm below costal margin / cm below xiphoid"],
["", "Consistency: ☐ Normal ☐ Firm ☐ Hard/nodular ☐ Tender"],
["", "Surface: ☐ Smooth ☐ Irregular/nodular"],
["", "Edge: ☐ Sharp ☐ Rounded ☐ Irregular"],
["", "Pulsatility: ☐ Present (TR) ☐ Absent"],
["Palpation – Spleen:", "☐ Not palpable ☐ Palpable ___ cm below LCM Ballottement: ☐ Yes ☐ No"],
["Palpation – GB:", "Murphy's sign: ☐ Positive ☐ Negative Courvoisier's sign: ☐ Positive ☐ Negative"],
["Palpation – Other:", "☐ Masses: ___ ☐ Tenderness (site): ___ ☐ Guarding / Rigidity"],
["Percussion:", "Liver span: ___ cm Shifting dullness: ☐ Yes ☐ No Fluid thrill: ☐ Yes ☐ No"],
["Auscultation:", "Bowel sounds: ☐ Normal ☐ Increased ☐ Absent Bruit: ☐ Present ☐ Absent"],
]
atbl = Table(abd_data, colWidths=[3.8*cm, PAGE_W-3.8*cm])
atbl.setStyle(TableStyle([
("FONTNAME",(0,0),(0,-1),"Helvetica-Bold"),
("FONTSIZE",(0,0),(-1,-1),8.5),
("BOTTOMPADDING",(0,0),(-1,-1),4),("TOPPADDING",(0,0),(-1,-1),1),
("ROWBACKGROUNDS",(0,0),(-1,-1),[WHITE, LGRAY]),
("BOX",(0,0),(-1,-1),0.5,MGRAY),("INNERGRID",(0,0),(-1,-1),0.3,MGRAY),
("BACKGROUND",(0,0),(0,-1),colors.HexColor("#E3F2FD")),
]))
story += [atbl, sp(5)]
# CVS
story.append(Paragraph("B. Cardiovascular System", sub_style))
cvs_data = [
["JVP:", "☐ Normal ☐ Elevated (cm above angle of Louis): ___ ☐ Pulsatile liver"],
["Apex beat:", "Position: ___ Character: ___"],
["Heart sounds:", "☐ S1 S2 normal ☐ Murmur: ___ ☐ Pericardial rub"],
]
ctbl = Table(cvs_data, colWidths=[2.8*cm, PAGE_W-2.8*cm])
ctbl.setStyle(TableStyle([
("FONTNAME",(0,0),(0,-1),"Helvetica-Bold"),
("FONTSIZE",(0,0),(-1,-1),8.5),
("BOTTOMPADDING",(0,0),(-1,-1),4),("TOPPADDING",(0,0),(-1,-1),1),
("ROWBACKGROUNDS",(0,0),(-1,-1),[WHITE, LGRAY]),
("BOX",(0,0),(-1,-1),0.5,MGRAY),("INNERGRID",(0,0),(-1,-1),0.3,MGRAY),
]))
story += [ctbl, sp(4)]
# RS
story.append(Paragraph("C. Respiratory System", sub_style))
rs_data = [
["Inspection:", "☐ Normal chest wall ☐ Pleural effusion signs ☐ Hepatic hydrothorax"],
["Percussion:", "☐ Resonant ☐ Dull (site): ___"],
["Auscultation:", "☐ Normal breath sounds ☐ Reduced (site): ___ ☐ Crepitations"],
]
rstbl = Table(rs_data, colWidths=[2.8*cm, PAGE_W-2.8*cm])
rstbl.setStyle(TableStyle([
("FONTNAME",(0,0),(0,-1),"Helvetica-Bold"),
("FONTSIZE",(0,0),(-1,-1),8.5),
("BOTTOMPADDING",(0,0),(-1,-1),4),("TOPPADDING",(0,0),(-1,-1),1),
("ROWBACKGROUNDS",(0,0),(-1,-1),[WHITE, LGRAY]),
("BOX",(0,0),(-1,-1),0.5,MGRAY),("INNERGRID",(0,0),(-1,-1),0.3,MGRAY),
]))
story += [rstbl, sp(4)]
# CNS
story.append(Paragraph("D. Central Nervous System (Hepatic Encephalopathy Grading)", sub_style))
enc_data = [
["Grade 0:", "No abnormality detected"],
["Grade 1:", "Trivial lack of awareness, euphoria/anxiety, impaired attention, short attention span"],
["Grade 2:", "Lethargy, disorientation (time), personality change, inappropriate behaviour"],
["Grade 3:", "Somnolence–stupor but responsive to stimuli, confusion, gross disorientation"],
["Grade 4:", "Coma – unresponsive to pain"],
]
enc_box = Table(enc_data, colWidths=[2.2*cm, PAGE_W-2.2*cm])
enc_box.setStyle(TableStyle([
("FONTNAME",(0,0),(0,-1),"Helvetica-Bold"),
("FONTSIZE",(0,0),(-1,-1),8),
("BOTTOMPADDING",(0,0),(-1,-1),3),("TOPPADDING",(0,0),(-1,-1),1),
("ROWBACKGROUNDS",(0,0),(-1,-1),[WHITE, LGRAY]),
("BOX",(0,0),(-1,-1),0.5,MGRAY),("INNERGRID",(0,0),(-1,-1),0.3,MGRAY),
]))
story += [enc_box, sp(3)]
story.append(field_row("Current Grade: ___ GCS: E_V_M_ = ___/15", width=PAGE_W*0.6))
story += [sp(4)]
cns_extra = [
"Flapping tremor (asterixis): ☐ Present ☐ Absent",
"Fetor hepaticus: ☐ Present ☐ Absent",
"Plantar response: ☐ Flexor (B/L) ☐ Extensor",
"Pupillary reflexes: ☐ Normal ☐ Abnormal",
]
story.append(checkbox_row(cns_extra, cols=2))
story += [sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 12. PROVISIONAL DIAGNOSIS
# ═══════════════════════════════════════════════════════════════════
story += [section_header("12. CLINICAL CLASSIFICATION & PROVISIONAL DIAGNOSIS", bg=TEAL), sp(4)]
story.append(Paragraph("Type of Jaundice (Circle / Tick):", sub_style))
type_data = [
[Paragraph("PRE-HEPATIC\n(Haemolytic)", ParagraphStyle("th", parent=small_style, alignment=TA_CENTER)),
Paragraph("HEPATIC\n(Hepatocellular)", ParagraphStyle("th2", parent=small_style, alignment=TA_CENTER)),
Paragraph("POST-HEPATIC\n(Obstructive/Cholestatic)", ParagraphStyle("th3", parent=small_style, alignment=TA_CENTER))],
[Paragraph("• ↑ Unconjugated bili\n• Normal LFTs\n• ↑ Reticulocytes\n• Haemolytic screen +ve", small_style),
Paragraph("• ↑ Mixed bili\n• ↑ AST/ALT\n• Variable ALP\n• Abnormal LFTs", small_style),
Paragraph("• ↑ Conjugated bili\n• ↑ ALP/GGT\n• Pale stools, dark urine\n• Dilated ducts on imaging", small_style)],
["☐ Pre-hepatic", "☐ Hepatocellular", "☐ Obstructive"],
]
type_tbl = Table(type_data, colWidths=[PAGE_W/3]*3)
type_tbl.setStyle(TableStyle([
("BOX",(0,0),(-1,-1),1,NAVY),
("INNERGRID",(0,0),(-1,-1),0.5,MGRAY),
("BACKGROUND",(0,0),(-1,0),NAVY),
("TEXTCOLOR",(0,0),(-1,0),WHITE),
("BACKGROUND",(0,2),(-1,2),colors.HexColor("#E8F5E9")),
("FONTNAME",(0,2),(-1,2),"Helvetica-Bold"),
("FONTSIZE",(0,2),(-1,2),9),
("ALIGN",(0,0),(-1,-1),"CENTER"),
("VALIGN",(0,0),(-1,-1),"MIDDLE"),
("TOPPADDING",(0,0),(-1,-1),5),
("BOTTOMPADDING",(0,0),(-1,-1),5),
]))
story += [type_tbl, sp(5)]
story.append(lined_area(lines=2, label="Provisional Diagnosis:"))
story += [sp(3), lined_area(lines=1, label="Differential Diagnoses:"), sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 13. INVESTIGATIONS
# ═══════════════════════════════════════════════════════════════════
story += [section_header("13. INVESTIGATIONS PLANNED / ORDERED"), sp(4)]
story.append(Paragraph("A. Haematology", sub_style))
haem = ["CBC with differential", "Peripheral blood smear", "Reticulocyte count",
"Direct Coombs test (DAT)", "Sickling test / Hb electrophoresis",
"G6PD assay", "PT / INR", "APTT", "Bleeding time / Clotting time"]
story.append(checkbox_row(haem, cols=3))
story += [sp(3), Paragraph("B. Liver Function Tests (LFT)", sub_style)]
lft_data = [
["Test", "Value", "Normal Range", "Test", "Value", "Normal Range"],
["Total Bilirubin", "", "0.2–1.2 mg/dL", "Direct (Conjugated)", "", "0–0.3 mg/dL"],
["Indirect (Unconjugated)", "", "0.1–0.9 mg/dL", "ALT (SGPT)", "", "7–56 U/L"],
["AST (SGOT)", "", "10–40 U/L", "ALP", "", "44–147 U/L"],
["GGT", "", "8–61 U/L", "Total Protein", "", "6–8 g/dL"],
["Serum Albumin", "", "3.5–5.0 g/dL", "Serum Globulin", "", "2–3.5 g/dL"],
["A/G Ratio", "", "1.2–2.2", "LDH", "", "140–280 U/L"],
]
lft_tbl = Table(lft_data, colWidths=[PAGE_W*0.22, PAGE_W*0.11, PAGE_W*0.17,
PAGE_W*0.22, PAGE_W*0.11, PAGE_W*0.17])
lft_tbl.setStyle(TableStyle([
("BACKGROUND",(0,0),(-1,0),NAVY),("TEXTCOLOR",(0,0),(-1,0),WHITE),
("FONTNAME",(0,0),(-1,0),"Helvetica-Bold"),
("FONTSIZE",(0,0),(-1,-1),7.5),
("ALIGN",(0,0),(-1,-1),"CENTER"),("VALIGN",(0,0),(-1,-1),"MIDDLE"),
("ROWBACKGROUNDS",(0,1),(-1,-1),[WHITE, LGRAY]),
("BOX",(0,0),(-1,-1),0.5,NAVY),("INNERGRID",(0,0),(-1,-1),0.3,MGRAY),
("TOPPADDING",(0,0),(-1,-1),3),("BOTTOMPADDING",(0,0),(-1,-1),3),
]))
story += [lft_tbl, sp(3)]
story.append(Paragraph("C. Renal & Metabolic", sub_style))
renal = ["Urea / BUN", "Serum Creatinine", "Electrolytes (Na, K, Cl, HCO3)",
"Serum Uric Acid", "Blood Glucose (FBS/RBS)", "Lipid Profile",
"Serum Ammonia", "Serum Copper / Ceruloplasmin (Wilson's)",
"Serum Ferritin / Iron / TIBC (Haemochromatosis)"]
story.append(checkbox_row(renal, cols=3))
story += [sp(3), Paragraph("D. Urine & Stool", sub_style)]
urine = ["Urine bilirubin (dipstick)", "Urobilinogen", "Urine protein", "Urine microscopy",
"Urine bile salts", "Stool colour documentation", "Stool for occult blood"]
story.append(checkbox_row(urine, cols=3))
story += [sp(3), Paragraph("E. Serology / Microbiology", sub_style)]
sero = ["HBsAg", "Anti-HBs", "Anti-HBc (IgM/IgG)", "HBeAg / Anti-HBe",
"HCV antibody (Anti-HCV)", "HCV RNA (PCR)", "Anti-HAV IgM",
"Anti-HEV IgM", "Weil-Felix / Leptospira serology",
"Malaria RDT / Peripheral smear", "Blood culture", "ANA / AMA / ASMA (Autoimmune)"]
story.append(checkbox_row(sero, cols=3))
story += [sp(3), Paragraph("F. Tumour Markers", sub_style)]
tumour = ["AFP (α-fetoprotein)", "CA 19-9", "CEA", "CA 125"]
story.append(checkbox_row(tumour, cols=4))
story += [sp(3), Paragraph("G. Imaging", sub_style)]
imaging = ["Ultrasound abdomen (1st line)", "CECT abdomen", "MRI liver",
"MRCP (biliary tree)", "ERCP (diagnostic / therapeutic)",
"Hepatobiliary scintigraphy (HIDA scan)", "EUS (pancreatic head)",
"Chest X-ray"]
story.append(checkbox_row(imaging, cols=3))
story += [sp(3), Paragraph("H. Special / Invasive", sub_style)]
special = ["Liver biopsy", "Bone marrow biopsy", "Upper GI endoscopy",
"Laparoscopy / diagnostic", "Ascitic fluid analysis (if ascites)"]
story.append(checkbox_row(special, cols=3))
story += [sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 14. SUMMARY & MANAGEMENT PLAN
# ═══════════════════════════════════════════════════════════════════
story += [section_header("14. CLINICAL SUMMARY & MANAGEMENT PLAN"), sp(4)]
story.append(lined_area(lines=4, label="Clinical Summary:"))
story += [sp(4), lined_area(lines=2, label="Management Plan:"), sp(4)]
story.append(Paragraph("Disposition:", sub_style))
disp = Table([[
Paragraph("☐ Ward admission", small_style),
Paragraph("☐ ICU/HDU", small_style),
Paragraph("☐ Day care / OPD follow-up", small_style),
Paragraph("☐ Refer to Gastroenterology / Surgery / Oncology", small_style),
]], colWidths=[PAGE_W/4]*4)
story += [disp, sp(6)]
# ═══════════════════════════════════════════════════════════════════
# 15. SIGNATURE
# ═══════════════════════════════════════════════════════════════════
story += [section_header("15. CLINICIAN'S SIGNATURE"), sp(4)]
sig_data = [
[Paragraph("Name of examiner:", label_style), "",
Paragraph("Designation:", label_style), ""],
[Paragraph("Signature:", label_style), "",
Paragraph("Date & Time:", label_style), ""],
[Paragraph("Consultant's review:", label_style), "",
Paragraph("Signature:", label_style), ""],
]
scw = [3.5*cm, PAGE_W/2-3.5*cm, 3*cm, PAGE_W/2-3*cm]
stbl = Table(sig_data, colWidths=scw)
stbl.setStyle(TableStyle(
[("LINEBELOW",(1,r),(1,r),0.5,MGRAY) for r in range(3)] +
[("LINEBELOW",(3,r),(3,r),0.5,MGRAY) for r in range(3)] +
[("BOTTOMPADDING",(0,0),(-1,-1),8),("TOPPADDING",(0,0),(-1,-1),2),
("VALIGN",(0,0),(-1,-1),"BOTTOM")]
))
story += [stbl, sp(8)]
# Footer note
story.append(HRFlowable(width=PAGE_W, thickness=0.5, color=MGRAY))
story.append(sp(3))
story.append(Paragraph(
"References: Harrison's Principles of Internal Medicine 22E (2025) | "
"Sleisenger & Fordtran's Gastrointestinal and Liver Disease | "
"Goldman-Cecil Medicine | Schwartz's Principles of Surgery 11E",
ParagraphStyle("Footer", parent=small_style, fontSize=7, textColor=colors.HexColor("#9E9E9E"),
alignment=TA_CENTER)
))
# ═══════════════════════════════════════════════════════════════════
# BUILD
# ═══════════════════════════════════════════════════════════════════
doc.build(story)
print(f"PDF generated: {OUTPUT}")
cd /home/daytona/workspace/jaundice-case-sheet && python generate_pdf.py 2>&1
Jaundice Case Sheet
PDF Document · PDF
| # | Section | Key Content |
|---|---|---|
| 1 | Patient Identification | Name, age, sex, ward, occupation, referral |
| 2 | Chief Complaint | Scleral icterus, dark urine, pale stools with duration |
| 3 | History of Present Illness | Onset/progression, characterisation (10 yes/no items), pain SOCRATES |
| 4 | Causative / Risk Factor History | Pre-hepatic (haemolytic), hepatocellular, obstructive, epidemiological |
| 5 | Past History | 12 structured yes/no fields |
| 6 | Drug & Allergy History | Medications, allergies, alcohol CAGE |
| 7 | Personal & Social History | Diet, smoking, IV drugs, sexual history, sanitation |
| 8 | Family History | Wilson's, haemoglobinopathy, hepatitis contacts |
| 9 | Review of Systems | GI, hepatic, constitutional, urine, stool, skin, neuro |
| 10 | General Examination | Vitals, icterus grading, full CLD stigmata checklist |
| 11 | Systemic Examination | Abdomen (Courvoisier's/Murphy's), CVS, RS, CNS with West Haven encephalopathy grading |
| 12 | Clinical Classification | Pre-hepatic / Hepatocellular / Obstructive comparison table + provisional DX |
| 13 | Investigations | 8 sub-sections: LFT table, haematology, serology (HAV/HBV/HCV/HEV/leptospira/malaria), tumour markers, imaging, special |
| 14 | Management Plan | Summary + disposition |
| 15 | Signature | Examiner + consultant sign-off |