Jaundice case sheet for medicine

Asking for Preferences
Reading File
Reading File
Finding Sources
Reading File
Reading File
Finding Sources
Reading File
Reading File
Running Command

mkdir -p /home/daytona/workspace/jaundice-case-sheet && ls /home/daytona/workspace/

Writing File

~/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}")
Running Command

cd /home/daytona/workspace/jaundice-case-sheet && python generate_pdf.py 2>&1

Your Jaundice Case Sheet PDF is ready:

Jaundice Case Sheet

PDF Document · PDF


What's included (15 sections across 2 pages):

#SectionKey Content
1Patient IdentificationName, age, sex, ward, occupation, referral
2Chief ComplaintScleral icterus, dark urine, pale stools with duration
3History of Present IllnessOnset/progression, characterisation (10 yes/no items), pain SOCRATES
4Causative / Risk Factor HistoryPre-hepatic (haemolytic), hepatocellular, obstructive, epidemiological
5Past History12 structured yes/no fields
6Drug & Allergy HistoryMedications, allergies, alcohol CAGE
7Personal & Social HistoryDiet, smoking, IV drugs, sexual history, sanitation
8Family HistoryWilson's, haemoglobinopathy, hepatitis contacts
9Review of SystemsGI, hepatic, constitutional, urine, stool, skin, neuro
10General ExaminationVitals, icterus grading, full CLD stigmata checklist
11Systemic ExaminationAbdomen (Courvoisier's/Murphy's), CVS, RS, CNS with West Haven encephalopathy grading
12Clinical ClassificationPre-hepatic / Hepatocellular / Obstructive comparison table + provisional DX
13Investigations8 sub-sections: LFT table, haematology, serology (HAV/HBV/HCV/HEV/leptospira/malaria), tumour markers, imaging, special
14Management PlanSummary + disposition
15SignatureExaminer + consultant sign-off
Content sourced from Harrison's Principles of Internal Medicine 22E, Sleisenger & Fordtran's GI & Liver Disease, Goldman-Cecil Medicine, and Schwartz's Principles of Surgery.
This is a shared conversation. Sign in to Orris to start your own chat.