~/mbbs-cases/generate_pdf.py
#!/usr/bin/env python3
"""
MBBS Clinical Cases Study Guide PDF Generator
Topics: Jaundice & Diabetes Biochemistry
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib import colors
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak, HRFlowable, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import ListFlowable, ListItem
import os
OUTPUT_PATH = "/home/daytona/workspace/mbbs-cases/MBBS_ClinicalCases_Jaundice_Diabetes.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
C_DARK_BLUE = colors.HexColor("#1a3a5c")
C_MED_BLUE = colors.HexColor("#2e6da4")
C_LIGHT_BLUE = colors.HexColor("#dbeaf7")
C_TEAL = colors.HexColor("#1a6b6b")
C_LIGHT_TEAL = colors.HexColor("#d4f0f0")
C_GOLD = colors.HexColor("#c8860a")
C_LIGHT_GOLD = colors.HexColor("#fef6e4")
C_RED = colors.HexColor("#c0392b")
C_LIGHT_RED = colors.HexColor("#fdecea")
C_GREEN = colors.HexColor("#1e7a34")
C_LIGHT_GREEN = colors.HexColor("#e9f7ec")
C_PURPLE = colors.HexColor("#6c3483")
C_LIGHT_PURPLE= colors.HexColor("#f4ecf7")
C_GREY_LIGHT = colors.HexColor("#f5f5f5")
C_GREY_MID = colors.HexColor("#cccccc")
C_BLACK = colors.HexColor("#1a1a1a")
C_WHITE = colors.white
# ── Styles ───────────────────────────────────────────────────────────────────
def build_styles():
base = getSampleStyleSheet()
styles = {}
styles["cover_title"] = ParagraphStyle(
"cover_title", fontName="Helvetica-Bold", fontSize=28,
textColor=C_WHITE, alignment=TA_CENTER, spaceAfter=8, leading=34
)
styles["cover_sub"] = ParagraphStyle(
"cover_sub", fontName="Helvetica", fontSize=14,
textColor=colors.HexColor("#cce0f5"), alignment=TA_CENTER, spaceAfter=6
)
styles["cover_meta"] = ParagraphStyle(
"cover_meta", fontName="Helvetica", fontSize=11,
textColor=colors.HexColor("#a0c4e8"), alignment=TA_CENTER, spaceAfter=4
)
styles["h_part"] = ParagraphStyle(
"h_part", fontName="Helvetica-Bold", fontSize=18,
textColor=C_WHITE, alignment=TA_CENTER, spaceAfter=4, spaceBefore=6, leading=22
)
styles["h_section"] = ParagraphStyle(
"h_section", fontName="Helvetica-Bold", fontSize=14,
textColor=C_DARK_BLUE, spaceAfter=4, spaceBefore=14, leading=18
)
styles["h_case"] = ParagraphStyle(
"h_case", fontName="Helvetica-Bold", fontSize=13,
textColor=C_WHITE, spaceAfter=2, spaceBefore=2, leading=17
)
styles["h_sub"] = ParagraphStyle(
"h_sub", fontName="Helvetica-Bold", fontSize=11,
textColor=C_MED_BLUE, spaceAfter=3, spaceBefore=8, leading=14
)
styles["h_sub2"] = ParagraphStyle(
"h_sub2", fontName="Helvetica-Bold", fontSize=10,
textColor=C_TEAL, spaceAfter=2, spaceBefore=6, leading=13
)
styles["body"] = ParagraphStyle(
"body", fontName="Helvetica", fontSize=9.5,
textColor=C_BLACK, spaceAfter=4, leading=14, alignment=TA_JUSTIFY
)
styles["body_bold"] = ParagraphStyle(
"body_bold", fontName="Helvetica-Bold", fontSize=9.5,
textColor=C_BLACK, spaceAfter=4, leading=14
)
styles["bullet"] = ParagraphStyle(
"bullet", fontName="Helvetica", fontSize=9.5,
textColor=C_BLACK, spaceAfter=2, leading=13,
leftIndent=12, firstLineIndent=-10
)
styles["quote"] = ParagraphStyle(
"quote", fontName="Helvetica-Oblique", fontSize=9,
textColor=colors.HexColor("#444444"), spaceAfter=4, leading=13,
leftIndent=14, rightIndent=14, borderPad=4
)
styles["code"] = ParagraphStyle(
"code", fontName="Courier", fontSize=8.5,
textColor=C_DARK_BLUE, spaceAfter=3, leading=13,
leftIndent=10, backColor=C_GREY_LIGHT
)
styles["table_hdr"] = ParagraphStyle(
"table_hdr", fontName="Helvetica-Bold", fontSize=8.5,
textColor=C_WHITE, alignment=TA_CENTER, leading=11
)
styles["table_cell"] = ParagraphStyle(
"table_cell", fontName="Helvetica", fontSize=8.5,
textColor=C_BLACK, alignment=TA_LEFT, leading=11
)
styles["table_cell_c"] = ParagraphStyle(
"table_cell_c", fontName="Helvetica", fontSize=8.5,
textColor=C_BLACK, alignment=TA_CENTER, leading=11
)
styles["source"] = ParagraphStyle(
"source", fontName="Helvetica-Oblique", fontSize=8,
textColor=colors.HexColor("#666666"), spaceAfter=2, leading=11
)
styles["footnote"] = ParagraphStyle(
"footnote", fontName="Helvetica", fontSize=8,
textColor=colors.HexColor("#777777"), spaceAfter=2, leading=11
)
return styles
# ── Helper flowables ─────────────────────────────────────────────────────────
def part_banner(text, color, styles):
tbl = Table([[Paragraph(text, styles["h_part"])]], colWidths=[17*cm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("ROUNDEDCORNERS", [6]),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
]))
return tbl
def case_banner(text, color, styles):
tbl = Table([[Paragraph(text, styles["h_case"])]], colWidths=[17*cm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("ROUNDEDCORNERS", [4]),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
return tbl
def info_box(text, bg_color, border_color, styles, label=None):
content = []
if label:
content.append(Paragraph(f"<b>{label}</b>", styles["body_bold"]))
content.append(Paragraph(text, styles["body"]))
tbl = Table([content], colWidths=[17*cm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg_color),
("BOX", (0,0), (-1,-1), 1.5, border_color),
("TOPPADDING", (0,0), (-1,-1), 7),
("BOTTOMPADDING", (0,0), (-1,-1), 7),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
return tbl
def quote_box(text, source, styles):
tbl = Table([
[Paragraph(f'<i>"{text}"</i>', styles["quote"])],
[Paragraph(f"— {source}", styles["source"])]
], colWidths=[17*cm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_GREY_LIGHT),
("LINEAFTER", (0,0), (0,-1), 3, C_MED_BLUE),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 14),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
return tbl
def make_table(headers, rows, styles, col_widths=None, header_color=None):
if header_color is None:
header_color = C_DARK_BLUE
header_row = [Paragraph(h, styles["table_hdr"]) for h in headers]
data = [header_row]
for i, row in enumerate(rows):
data.append([Paragraph(str(cell), styles["table_cell"]) for cell in row])
if col_widths is None:
n = len(headers)
col_widths = [17*cm / n] * n
tbl = Table(data, colWidths=col_widths, repeatRows=1)
ts = [
("BACKGROUND", (0,0), (-1,0), header_color),
("TEXTCOLOR", (0,0), (-1,0), C_WHITE),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C_WHITE, C_GREY_LIGHT]),
("BOX", (0,0), (-1,-1), 0.8, C_GREY_MID),
("INNERGRID", (0,0), (-1,-1), 0.4, C_GREY_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]
tbl.setStyle(TableStyle(ts))
return tbl
def pathway_box(lines, styles):
"""Monospaced pathway/reaction box."""
content = [Paragraph(line.replace(" ", " "), styles["code"]) for line in lines]
tbl = Table([[c] for c in content], colWidths=[17*cm])
tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_GREY_LIGHT),
("BOX", (0,0), (-1,-1), 1, C_MED_BLUE),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
]))
return tbl
def sp(n=1):
return Spacer(1, n * 4 * mm)
def hr(color=C_GREY_MID, thickness=0.5):
return HRFlowable(width="100%", thickness=thickness, color=color, spaceAfter=4, spaceBefore=4)
# ── Cover page ───────────────────────────────────────────────────────────────
def build_cover(styles):
els = []
# Full-width cover banner via a large table
cover_data = [[
Paragraph("MBBS BIOCHEMISTRY", styles["cover_title"]),
]]
cover = Table(cover_data, colWidths=[17*cm])
cover.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_DARK_BLUE),
("TOPPADDING", (0,0), (-1,-1), 30),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
els.append(cover)
sub_data = [[Paragraph("Clinical Cases Study Guide", styles["cover_sub"])]]
sub = Table(sub_data, colWidths=[17*cm])
sub.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_DARK_BLUE),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 20),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
]))
els.append(sub)
els.append(sp(3))
topic_data = [
[Paragraph("TOPICS COVERED", ParagraphStyle("tc", fontName="Helvetica-Bold",
fontSize=11, textColor=C_MED_BLUE, alignment=TA_CENTER))],
[Paragraph("Jaundice | Diabetes Mellitus | Biochemistry Integration",
ParagraphStyle("tcs", fontName="Helvetica", fontSize=12,
textColor=C_DARK_BLUE, alignment=TA_CENTER, leading=18))],
]
topic_tbl = Table(topic_data, colWidths=[17*cm])
topic_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_LIGHT_BLUE),
("BOX", (0,0), (-1,-1), 1.5, C_MED_BLUE),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 10),
]))
els.append(topic_tbl)
els.append(sp(3))
# Year labels
year_rows = [
["1st & 2nd Year", "Biochemistry • Pathway Mechanisms • Physiology of Insulin & Bilirubin"],
["3rd Year (MBBS)", "Clinical Cases • Diagnosis • Management • Exam-Ready Discussions"],
]
year_tbl = Table(year_rows, colWidths=[5*cm, 12*cm])
year_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,-1), C_MED_BLUE),
("BACKGROUND", (1,0), (1,-1), C_LIGHT_BLUE),
("TEXTCOLOR", (0,0), (0,-1), C_WHITE),
("FONTNAME", (0,0), (0,-1), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 10),
("TOPPADDING", (0,0), (-1,-1), 9),
("BOTTOMPADDING", (0,0), (-1,-1), 9),
("LEFTPADDING", (0,0), (-1,-1), 12),
("INNERGRID", (0,0), (-1,-1), 0.5, C_WHITE),
("BOX", (0,0), (-1,-1), 1, C_MED_BLUE),
]))
els.append(year_tbl)
els.append(sp(3))
# Contents list
toc_items = [
"PART 1 — 3rd Year MBBS: Clinical Cases",
" J1 Prehepatic Jaundice (Autoimmune Hemolytic Anemia)",
" J2 Hepatocellular Jaundice (Viral Hepatitis A)",
" J3 Obstructive / Posthepatic Jaundice (Ca Pancreas)",
" J4 Gilbert's Syndrome",
" D1 Type 1 DM — DKA (New Onset)",
" D2 Type 2 DM — Incidental Detection",
" D3 Hyperosmolar Hyperglycemic State (HHS)",
" D4 Drug-induced Liver Injury in a Diabetic",
" D5 Metformin-associated Lactic Acidosis",
"",
"PART 2 — 1st & 2nd Year MBBS: Biochemistry Deep Dives",
" J1 Bilirubin Pathway — Full Mechanism & Gilbert's Syndrome",
" J2 Physiological Neonatal Jaundice & Kernicterus",
" J3 Obstructive Jaundice — Why Pale Stools & Dark Urine?",
" D1 Insulin Biosynthesis — Gene to Granule",
" D2 Beta-cell Secretion — K_ATP Channel & Sulfonylureas",
" D3 Insulin's Metabolic Actions — GLUT4, Glycogen, Lipogenesis",
" D4 DKA Biochemistry — Ketone Bodies & Anion Gap",
" D5 HbA1c — Glycation, Amadori Product, Interpretation",
"",
"PART 3 — Quick Reference Tables (Exam Ready)",
]
toc_data = [[Paragraph("CONTENTS", ParagraphStyle("toc_h", fontName="Helvetica-Bold",
fontSize=11, textColor=C_DARK_BLUE))]]
for item in toc_items:
style = ParagraphStyle("toc_i", fontName="Helvetica-Bold" if "PART" in item else "Helvetica",
fontSize=9, textColor=C_DARK_BLUE if "PART" in item else C_BLACK, leading=14)
toc_data.append([Paragraph(item, style)])
toc_tbl = Table(toc_data, colWidths=[17*cm])
toc_tbl.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), C_LIGHT_BLUE),
("BACKGROUND", (0,1), (-1,-1), C_WHITE),
("BOX", (0,0), (-1,-1), 1, C_MED_BLUE),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 12),
("RIGHTPADDING", (0,0), (-1,-1), 12),
]))
els.append(toc_tbl)
els.append(sp(2))
meta = Table([[
Paragraph("Based on: Harrison's 22nd Ed • Goldman-Cecil Medicine • Schwartz's Surgery • "
"Harper's Biochemistry • Tietz Lab Medicine • Medical Physiology",
ParagraphStyle("src", fontName="Helvetica-Oblique", fontSize=8,
textColor=colors.HexColor("#555"), alignment=TA_CENTER))
]], colWidths=[17*cm])
meta.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_GREY_LIGHT),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
]))
els.append(meta)
els.append(PageBreak())
return els
# ── PAGE HEADER / FOOTER ─────────────────────────────────────────────────────
def on_page(canvas, doc):
canvas.saveState()
w, h = A4
# Header bar
canvas.setFillColor(C_DARK_BLUE)
canvas.rect(doc.leftMargin, h - doc.topMargin + 4*mm,
w - doc.leftMargin - doc.rightMargin, 6*mm, fill=1, stroke=0)
canvas.setFont("Helvetica-Bold", 8)
canvas.setFillColor(C_WHITE)
canvas.drawString(doc.leftMargin + 3*mm, h - doc.topMargin + 6*mm,
"MBBS Biochemistry — Jaundice & Diabetes Clinical Cases")
canvas.drawRightString(w - doc.rightMargin - 3*mm, h - doc.topMargin + 6*mm,
"Quick Revision Study Guide")
# Footer
canvas.setFillColor(C_MED_BLUE)
canvas.rect(doc.leftMargin, doc.bottomMargin - 4*mm,
w - doc.leftMargin - doc.rightMargin, 5*mm, fill=1, stroke=0)
canvas.setFont("Helvetica", 7.5)
canvas.setFillColor(C_WHITE)
canvas.drawCentredString(w/2, doc.bottomMargin - 2*mm, f"Page {doc.page}")
canvas.restoreState()
# ── CONTENT BUILDERS ─────────────────────────────────────────────────────────
def section_3rd_year(styles):
els = []
# ── PART 1 BANNER ──
els.append(part_banner("PART 1 — 3rd Year MBBS: Clinical Cases", C_DARK_BLUE, styles))
els.append(sp(2))
els.append(Paragraph(
"Each case presents a real-world clinical vignette followed by investigations, "
"diagnosis, and a structured biochemical discussion mapped to the MBBS curriculum.",
styles["body"]))
els.append(sp(1))
els.append(hr(C_MED_BLUE, 1.5))
# ────────────────────────────────────────────────────────────────
# JAUNDICE CASES
# ────────────────────────────────────────────────────────────────
els.append(Paragraph("JAUNDICE — CLINICAL CASES", styles["h_section"]))
# ── Case J1 ──────────────────────────────────────────────────────
els.append(case_banner("Case J1 — Prehepatic Jaundice | Autoimmune Hemolytic Anemia", C_MED_BLUE, styles))
els.append(sp(1))
els.append(info_box(
"<b>Presenting Complaint:</b> 22-year-old male with yellowish eyes and skin for 3 days. "
"Dark urine. No abdominal pain, no fever, no alcohol use. Cousin has similar history.",
C_LIGHT_BLUE, C_MED_BLUE, styles))
els.append(sp(1))
els.append(Paragraph("Investigations", styles["h_sub"]))
els.append(make_table(
["Test", "Result", "Interpretation"],
[
["Total bilirubin", "5.8 mg/dL", "Elevated"],
["Direct (conjugated) bilirubin", "0.6 mg/dL", "Normal"],
["Indirect (unconjugated) bilirubin", "5.2 mg/dL", "Markedly elevated"],
["Urine bilirubin", "ABSENT", "Key finding — acholuric jaundice"],
["Urine urobilinogen", "Markedly elevated", "Excess bile in gut"],
["Haemoglobin", "7.8 g/dL", "Anaemia"],
["Reticulocyte count", "8%", "Elevated — active haemolysis"],
["Peripheral smear", "Spherocytes, polychromasia", "Haemolytic pattern"],
["Direct Coombs (DCT)", "Positive", "Immune-mediated haemolysis"],
["AST/ALT/ALP", "Near normal", "Liver intact"],
],
styles, [5.5*cm, 5*cm, 6.5*cm]
))
els.append(sp(1))
els.append(info_box("<b>Diagnosis:</b> Autoimmune Haemolytic Anaemia → Prehepatic (Haemolytic) Jaundice",
C_LIGHT_GREEN, C_GREEN, styles))
els.append(sp(1))
els.append(Paragraph("Biochemical Explanation", styles["h_sub"]))
for line in [
"• Excess RBC breakdown → massive unconjugated (indirect) bilirubin load",
"• Unconjugated bilirubin is <b>water-insoluble</b> (bound to albumin) → cannot be filtered by glomeruli → <b>no bilirubin in urine</b> (acholuric jaundice)",
"• Liver conjugates and excretes excess bile → more urobilinogen in gut → reabsorbed → excreted in urine → <b>urine urobilinogen very high</b>",
"• Stools are dark (excess stercobilin)",
"• ALT/AST are normal — the liver itself is undamaged",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(sp(1))
els.append(quote_box(
"Prehepatic jaundice as a result of elevated levels of unconjugated bilirubin occurs from "
"faulty prehepatic metabolism and usually arises from conditions that interfere with proper "
"conjugation of bilirubin in the hepatocyte.",
"Schwartz's Principles of Surgery, 11th Ed."
))
els.append(sp(2))
# ── Case J2 ──────────────────────────────────────────────────────
els.append(case_banner("Case J2 — Hepatocellular Jaundice | Acute Viral Hepatitis A", C_TEAL, styles))
els.append(sp(1))
els.append(info_box(
"<b>Presenting Complaint:</b> 19-year-old male, jaundice 5 days. Prodrome: fatigue, nausea, "
"anorexia, low-grade fever for 2 weeks. Recently returned from rural camp. "
"Clay-colored stools, dark urine.",
C_LIGHT_TEAL, C_TEAL, styles))
els.append(sp(1))
els.append(Paragraph("Investigations", styles["h_sub"]))
els.append(make_table(
["Test", "Result", "Interpretation"],
[
["Total bilirubin", "9.2 mg/dL", "Elevated"],
["Direct bilirubin", "6.4 mg/dL", "Predominantly conjugated"],
["Indirect bilirubin", "2.8 mg/dL", "Also elevated"],
["Urine bilirubin", "Positive (3+)", "Conjugated spills into urine"],
["ALT", "1840 U/L", "Markedly elevated — hepatocellular damage"],
["AST", "1240 U/L", "Elevated; ALT > AST (viral pattern)"],
["ALP", "180 U/L", "Mildly elevated"],
["INR", "1.4", "Mildly prolonged synthetic dysfunction"],
["Anti-HAV IgM", "Positive", "Confirms Hepatitis A"],
],
styles, [5.5*cm, 5*cm, 6.5*cm]
))
els.append(sp(1))
els.append(info_box("<b>Diagnosis:</b> Acute Hepatitis A — Hepatocellular Jaundice",
C_LIGHT_GREEN, C_GREEN, styles))
els.append(sp(1))
els.append(Paragraph("Biochemical Explanation", styles["h_sub"]))
for line in [
"• Viral injury → hepatocyte damage → impaired bilirubin uptake, conjugation AND excretion",
"• Both conjugated and unconjugated bilirubin rise",
"• Conjugated bilirubin (water-soluble) leaks into blood → filtered by kidneys → <b>bilirubinuria (dark urine)</b>",
"• <b>ALT > AST</b> pattern: ALT is cytoplasmic and highly specific for hepatocytes; AST is mitochondrial (rises later, also in cardiac/muscle injury)",
"• AST:ALT ratio >2 suggests alcoholic hepatitis; <1 favours viral hepatitis",
"• Mildly prolonged INR: liver impairment reduces synthesis of clotting factors (II, VII, IX, X)",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(sp(1))
els.append(quote_box(
"In hepatocellular dysfunction caused by viral hepatitis, aminotransferase levels are elevated, "
"with the serum ALT level higher than the AST level.",
"Goldman-Cecil Medicine, International Ed."
))
els.append(sp(2))
# ── Case J3 ──────────────────────────────────────────────────────
els.append(case_banner("Case J3 — Obstructive Jaundice | Carcinoma Head of Pancreas", C_GOLD, styles))
els.append(sp(1))
els.append(info_box(
"<b>Presenting Complaint:</b> 55-year-old male, progressive deep jaundice 3 weeks, "
"clay-colored stools, dark urine, <b>intense pruritus</b>, 6 kg weight loss. "
"No fever. <b>Courvoisier's sign positive</b> (palpable non-tender gallbladder).",
C_LIGHT_GOLD, C_GOLD, styles))
els.append(sp(1))
els.append(Paragraph("Investigations", styles["h_sub"]))
els.append(make_table(
["Test", "Result", "Interpretation"],
[
["Total bilirubin", "18 mg/dL", "Markedly elevated"],
["Direct bilirubin", "15.8 mg/dL", "Predominantly conjugated"],
["ALP", "680 U/L", "Markedly elevated — cholestatic"],
["GGT", "420 U/L", "Markedly elevated — cholestatic"],
["ALT/AST", "120/90 U/L", "Mildly elevated (secondary)"],
["Urine bilirubin", "4+", "Bilirubinuria"],
["Urine urobilinogen", "ABSENT", "No bile reaching gut"],
["CA 19-9", "Markedly elevated", "Pancreatic tumour marker"],
["USG / CECT Abdomen", "Dilated CBD, pancreatic head mass", "Confirms obstruction"],
],
styles, [5.5*cm, 5*cm, 6.5*cm]
))
els.append(sp(1))
els.append(info_box("<b>Diagnosis:</b> Carcinoma Head of Pancreas → Obstructive (Posthepatic) Jaundice",
C_LIGHT_RED, C_RED, styles))
els.append(sp(1))
els.append(Paragraph("Biochemical Explanation", styles["h_sub"]))
for line in [
"• CBD obstruction → conjugated bilirubin cannot enter duodenum → backs up into bloodstream",
"• Water-soluble conjugated bilirubin → filtered by kidneys → <b>bilirubinuria (dark urine)</b>",
"• No bile in gut → no urobilinogen → <b>absent urine urobilinogen</b> → pale/clay stools",
"• <b>ALP and GGT markedly elevated</b>: back-pressure induces ALP synthesis in bile duct epithelium",
"• <b>Pruritus</b>: bile salt deposition in skin stimulates cutaneous nerve fibres",
"• <b>Prolonged PT</b>: bile blockage → Vitamin K malabsorption → reduced synthesis of factors II, VII, IX, X",
"• Courvoisier's sign = palpable non-tender gallbladder + jaundice → malignancy until proved otherwise",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(sp(2))
# ── Case J4 ──────────────────────────────────────────────────────
els.append(case_banner("Case J4 — Gilbert's Syndrome | Hereditary Unconjugated Hyperbilirubinaemia", C_PURPLE, styles))
els.append(sp(1))
els.append(info_box(
"<b>Presenting Complaint:</b> 24-year-old medical student, incidental yellow eyes during physical exam. "
"Episodes with prolonged fasting and exam stress. Father also has 'yellow eyes'. "
"No dark urine, no abdominal symptoms.",
C_LIGHT_PURPLE, C_PURPLE, styles))
els.append(sp(1))
els.append(make_table(
["Test", "Result"],
[
["Total bilirubin", "3.2 mg/dL"],
["Direct bilirubin", "0.3 mg/dL (indirect = 2.9 mg/dL)"],
["LFTs, albumin, INR", "All NORMAL"],
["CBC, reticulocytes, peripheral smear", "All NORMAL"],
["Urine bilirubin", "Absent"],
],
styles, [8*cm, 9*cm]
))
els.append(sp(1))
els.append(info_box(
"<b>Diagnosis:</b> Gilbert's Syndrome — benign hereditary unconjugated hyperbilirubinaemia. "
"Defect: TA-repeat polymorphism in UGT1A1 promoter → ~30% reduced UDP-glucuronosyltransferase "
"activity. Autosomal recessive. Affects 4-7% of population. No treatment needed.",
C_LIGHT_GREEN, C_GREEN, styles))
els.append(sp(2))
# ────────────────────────────────────────────────────────────────
# DIABETES CASES
# ────────────────────────────────────────────────────────────────
els.append(hr(C_DARK_BLUE, 1.5))
els.append(Paragraph("DIABETES MELLITUS — CLINICAL CASES", styles["h_section"]))
# ── Case D1 ──────────────────────────────────────────────────────
els.append(case_banner("Case D1 — Type 1 DM | New-onset Diabetic Ketoacidosis (DKA)", C_RED, styles))
els.append(sp(1))
els.append(info_box(
"<b>Presenting Complaint:</b> 14-year-old girl, 4 weeks of polyuria, polydipsia, polyphagia, "
"5 kg weight loss despite eating well. Today: drowsy, deep rapid breathing, fruity breath. "
"BMI 17. BP 90/60, HR 122, RR 28/min (Kussmaul breathing).",
C_LIGHT_RED, C_RED, styles))
els.append(sp(1))
els.append(Paragraph("Investigations", styles["h_sub"]))
els.append(make_table(
["Test", "Result", "Significance"],
[
["Blood glucose", "520 mg/dL", "Hyperglycaemia"],
["Serum Na", "128 mEq/L", "Pseudohyponatraemia (osmotic shift)"],
["Serum K", "5.4 mEq/L", "Initially high; total body deficit"],
["Serum HCO₃", "10 mEq/L", "Low — metabolic acidosis"],
["Arterial pH", "7.16", "Acidosis (severe if <7.0)"],
["Anion gap", "22 mEq/L", "High anion gap = ketoacids"],
["Serum ketones", "Strongly positive", "Ketoacidosis confirmed"],
["HbA1c", "11.2%", "Chronic uncontrolled hyperglycaemia"],
["C-peptide", "Very low", "Absent endogenous insulin"],
["Anti-GAD antibody", "Positive", "Autoimmune T1DM confirmed"],
],
styles, [5*cm, 4.5*cm, 7.5*cm]
))
els.append(sp(1))
els.append(info_box("<b>Diagnosis:</b> New-onset Type 1 DM presenting as Diabetic Ketoacidosis (DKA)",
C_LIGHT_RED, C_RED, styles))
els.append(sp(1))
els.append(Paragraph("DKA Diagnostic Criteria", styles["h_sub"]))
els.append(make_table(
["Parameter", "Mild", "Moderate", "Severe"],
[
["Blood glucose", ">250 mg/dL", ">250 mg/dL", ">250 mg/dL"],
["Arterial pH", "7.25 – 7.30", "7.00 – 7.24", "<7.00"],
["Serum HCO₃", "15 – 18 mEq/L", "10 – 15 mEq/L", "<10 mEq/L"],
["Ketones (serum/urine)", "Positive", "Positive", "Positive"],
["Anion gap", ">10", ">12", ">12"],
],
styles, [5*cm, 4*cm, 4*cm, 4*cm], header_color=C_RED
))
els.append(sp(1))
els.append(Paragraph("Biochemical Pathogenesis of DKA", styles["h_sub"]))
for line in [
"<b>1. No insulin → hyperglycaemia:</b> No GLUT4 translocation → glucose not taken up by muscle/fat → hyperglycaemia → osmotic diuresis → polyuria → dehydration",
"<b>2. No insulin → ketogenesis:</b> Glucagon dominates → activates hormone-sensitive lipase → FFAs flood liver → β-oxidation → excess acetyl-CoA → ketone bodies → metabolic acidosis",
"<b>3. No insulin → protein catabolism:</b> Muscle proteolysis → gluconeogenic amino acids → even more hyperglycaemia",
"<b>Kussmaul breathing:</b> Respiratory compensation for metabolic acidosis — hyperventilation blows off CO₂ to raise blood pH",
"<b>Fruity breath:</b> From acetone (spontaneous decarboxylation of acetoacetate) — acetone is volatile and exhaled",
"<b>Pseudohyponatraemia:</b> High glucose is osmotically active → draws water into vascular space → dilutes sodium",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(sp(1))
els.append(quote_box(
"The clinical history of DKA typically involves deterioration during several hours to days, with "
"progressive polyuria, polydipsia... physical findings include dry skin and mucous membranes, "
"reduced jugular venous pressure, tachycardia, orthostatic hypotension, depressed mental "
"function, and deep rapid respirations (Kussmaul breathing).",
"Goldman-Cecil Medicine, International Ed."
))
els.append(sp(2))
# ── Case D2 ──────────────────────────────────────────────────────
els.append(case_banner("Case D2 — Type 2 DM | Incidental Detection + Metabolic Syndrome", C_TEAL, styles))
els.append(sp(1))
els.append(info_box(
"<b>Presenting Complaint:</b> 52-year-old obese man for routine check-up. Fatigue, nocturia. "
"Hypertension on amlodipine. Father had diabetes. BMI 29, waist 98 cm. "
"<b>Acanthosis nigricans</b> at neck and axillae (skin marker of insulin resistance).",
C_LIGHT_TEAL, C_TEAL, styles))
els.append(sp(1))
els.append(make_table(
["Test", "Result", "Reference / Significance"],
[
["Fasting plasma glucose (×2)", "148 mg/dL", "Diagnostic: ≥126 mg/dL"],
["2-hr OGTT (75g)", "230 mg/dL", "Diagnostic: ≥200 mg/dL"],
["HbA1c", "8.1%", "Diagnostic: ≥6.5%"],
["Fasting insulin", "Elevated", "Insulin resistance"],
["Urine microalbumin/creatinine", "38 mg/g", "Early nephropathy (>30 = microalbuminuria)"],
["TG / HDL", "290 / 32 mg/dL", "Dyslipidaemia — metabolic syndrome"],
],
styles, [5.5*cm, 4*cm, 7.5*cm]
))
els.append(sp(1))
els.append(info_box(
"<b>Diagnosis:</b> Type 2 DM with early diabetic nephropathy. Metabolic Syndrome "
"(HTN + central obesity + dyslipidaemia + impaired glucose).",
C_LIGHT_GREEN, C_GREEN, styles))
els.append(sp(1))
els.append(Paragraph("ADA Diagnostic Criteria for Diabetes (any one of the following):", styles["h_sub"]))
for line in [
"• FPG ≥126 mg/dL (7.0 mmol/L) on 2 occasions",
"• 2-hr plasma glucose ≥200 mg/dL during 75g OGTT",
"• HbA1c ≥6.5% (48 mmol/mol)",
"• Random glucose ≥200 mg/dL with classic symptoms",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(sp(2))
# ── Case D3 ──────────────────────────────────────────────────────
els.append(case_banner("Case D3 — Hyperosmolar Hyperglycaemic State (HHS)", C_GOLD, styles))
els.append(sp(1))
els.append(info_box(
"<b>Presenting Complaint:</b> 70-year-old man, known T2DM, brought in confused and lethargic. "
"Poor oral intake + vomiting × 5 days (gastroenteritis). Missed medications × 3 days. "
"GCS 11/15. Severe dehydration. No Kussmaul breathing, no fruity odour.",
C_LIGHT_GOLD, C_GOLD, styles))
els.append(sp(1))
els.append(Paragraph("DKA vs HHS — Key Differences", styles["h_sub"]))
els.append(make_table(
["Feature", "DKA", "HHS"],
[
["Usual diabetes type", "Type 1 (also Type 2)", "Type 2"],
["Age", "Younger", "Older (>60 yrs)"],
["Blood glucose", "250–600 mg/dL", ">600 mg/dL (often >800)"],
["Ketones", "Marked (4+)", "Absent / trace"],
["Arterial pH", "<7.30", "Normal (>7.35)"],
["Serum HCO₃", "<18 mEq/L", "Normal (>18 mEq/L)"],
["Serum osmolality", "Variable / <320", ">320 mOsm/kg"],
["Onset", "Hours to days", "Days to weeks"],
["Kussmaul breathing", "Present", "Absent"],
["Mortality", "~1–5%", "~10–20%"],
],
styles, [5*cm, 6*cm, 6*cm], header_color=C_GOLD
))
els.append(sp(1))
els.append(info_box(
"<b>Why no ketosis in HHS?</b> In T2DM, residual insulin secretion is sufficient to suppress "
"hormone-sensitive lipase and halt lipolysis/ketogenesis — but insufficient to prevent severe "
"hyperglycaemia. In T1DM (DKA), absolute zero insulin → unrestrained lipolysis.",
C_LIGHT_GOLD, C_GOLD, styles))
els.append(sp(2))
# ── Cases D4 and D5 (shorter) ────────────────────────────────────
els.append(case_banner("Case D4 — Drug-induced Liver Injury (DILI) in a Diabetic Patient", C_PURPLE, styles))
els.append(sp(1))
els.append(info_box(
"<b>Scenario:</b> 45-yr-old T2DM on metformin + statin, jaundice × 10 days after starting "
"herbal supplement. No fever, normal USG (no ductal dilatation). Viral hepatitis screen: negative. "
"ALT 960 U/L, Total Bilirubin 7.2 mg/dL, Direct 5.8, INR 1.9. HbA1c 9.8%.",
C_LIGHT_PURPLE, C_PURPLE, styles))
els.append(sp(1))
for line in [
"<b>Diagnosis:</b> Drug/herbal supplement-induced hepatocellular jaundice",
"• Diabetics are at higher risk of NAFLD and hepatotoxicity — fatty liver as background",
"• Hepatocellular pattern: ALT >> ALP; both conjugated and unconjugated bilirubin elevated",
"• Impaired synthetic function: raised INR (reduced clotting factor synthesis)",
"• Always take full drug/supplement history in any jaundice case",
"• Poor HbA1c (9.8%) suggests chronic hyperglycaemia promoting hepatic steatosis",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(sp(2))
els.append(case_banner("Case D5 — Metformin-associated Lactic Acidosis (MALA)", C_RED, styles))
els.append(sp(1))
els.append(info_box(
"<b>Scenario:</b> 58-yr-old T2DM on metformin 2g/day + CKD (eGFR 28). Recent IV contrast CT. "
"Presents: breathlessness, confusion, abdominal pain. "
"ABG: pH 7.08, HCO₃ 8, Lactate 14 mmol/L. Glucose 180 mg/dL. <b>Ketones: absent.</b>",
C_LIGHT_RED, C_RED, styles))
els.append(sp(1))
for line in [
"<b>Diagnosis:</b> Metformin-associated Lactic Acidosis — HIGH ANION GAP metabolic acidosis",
"<b>Mechanism:</b> Metformin inhibits mitochondrial Complex I (NADH dehydrogenase) → pyruvate cannot enter TCA → accumulates → converted to <b>lactate (anaerobic glycolysis)</b>",
"• In renal failure, metformin accumulates (renally excreted) → toxicity worsens",
"• Contrast-induced nephropathy further precipitates AKI → even less metformin clearance",
"<b>Rule:</b> Metformin contraindicated when eGFR <30 mL/min; withhold before contrast procedures",
"<b>Differentiates from DKA:</b> glucose only mildly elevated, NO ketones, very high lactate",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(PageBreak())
return els
def section_1st_2nd_year(styles):
els = []
els.append(part_banner("PART 2 — 1st & 2nd Year MBBS: Biochemistry Deep Dives", C_TEAL, styles))
els.append(sp(2))
els.append(Paragraph(
"Mechanism-focused cases linking biochemical pathways to clinical presentations. "
"Ideal for 1st and 2nd year students preparing for theory examinations.",
styles["body"]))
els.append(sp(1))
els.append(hr(C_TEAL, 1.5))
# ────────────────────────────────────────────────────────────────
# JAUNDICE BIOCHEMISTRY
# ────────────────────────────────────────────────────────────────
els.append(Paragraph("JAUNDICE — BIOCHEMISTRY", styles["h_section"]))
# J1 Bilirubin pathway
els.append(case_banner("J1 — The Complete Bilirubin Pathway (Gilbert's Syndrome as Model)", C_MED_BLUE, styles))
els.append(sp(1))
els.append(Paragraph("Step 1 — Bilirubin Formation (Reticuloendothelial System)", styles["h_sub"]))
els.append(pathway_box([
"Haemoglobin → [globin released + heme extracted]",
"Heme (ferroprotoporphyrin IX)",
" ↓ heme oxygenase (microsomal, ER of macrophages in spleen/liver/BM)",
" ↓ → CO released (1 mol/mol heme) + Fe²⁺ released (recycled to transferrin)",
"Biliverdin (green pigment)",
" ↓ biliverdin reductase (cytosol)",
"Bilirubin (yellow-orange pigment) ← water-INSOLUBLE",
]))
els.append(sp(1))
els.append(Paragraph(
"80–85% of bilirubin comes from senescent RBC breakdown (RBC lifespan ~120 days). "
"Remaining 15–20% from premature erythroid cell destruction in bone marrow and turnover "
"of myoglobin, cytochromes.",
styles["body"]))
els.append(sp(1))
els.append(quote_box(
"The formation of bilirubin occurs in reticuloendothelial cells, primarily in the spleen and liver. "
"The first reaction, catalyzed by the microsomal enzyme heme oxygenase, oxidatively cleaves the "
"alpha bridge of the porphyrin group. The second reaction, catalyzed by biliverdin reductase, "
"reduces the central methylene bridge of biliverdin and converts it to bilirubin.",
"Harrison's Principles of Internal Medicine, 22nd Ed. (2025)"
))
els.append(sp(1))
els.append(Paragraph("Step 2 — Transport in Blood", styles["h_sub"]))
for line in [
"• Bilirubin is <b>water-insoluble</b> — internal hydrogen bonds between propionic acid groups and imino/lactam groups make it hydrophobic",
"• Binds reversibly and non-covalently to <b>albumin</b> for transport → called <b>unconjugated (indirect) bilirubin</b>",
"• Protein-bound → too large to be glomerularly filtered → <b>NEVER appears in normal urine</b>",
"• Clinically: urine bilirubin ABSENT in prehepatic/unconjugated jaundice = 'acholuric jaundice'",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(sp(1))
els.append(Paragraph("Step 3 — Hepatic Uptake and Conjugation", styles["h_sub"]))
els.append(pathway_box([
"Unconjugated bilirubin–albumin complex arrives at hepatocyte sinusoidal membrane",
" ↓ carrier-mediated uptake (OATP transporters) — bilirubin taken in, albumin stays in blood",
" ↓ intracellular binding to ligandin (glutathione-S-transferase) — prevents back-diffusion",
"Smooth ER: Bilirubin + UDP-glucuronic acid",
" ↓ UDP-glucuronosyltransferase (UGT1A1) ← KEY ENZYME",
"Bilirubin monoglucuronide → Bilirubin diglucuronide (water-SOLUBLE)",
" ↓ MRP2 transporter (active, ATP-dependent) at canalicular membrane",
"Secreted into bile canaliculi → bile ducts → duodenum",
]))
els.append(sp(1))
els.append(info_box(
"<b>Gilbert's Syndrome:</b> TA-repeat polymorphism in UGT1A1 promoter → ~30% reduced UGT1A1 "
"activity. Autosomal recessive. Fasting/stress → FFAs compete for albumin, displace bilirubin, "
"flooding the already-reduced conjugation system. Result: mild unconjugated hyperbilirubinaemia "
"during fasting/illness. Completely benign — no treatment. 4–7% population prevalence.",
C_LIGHT_BLUE, C_MED_BLUE, styles))
els.append(sp(1))
els.append(Paragraph("Step 4 — Intestinal Fate and Van den Bergh Reaction", styles["h_sub"]))
els.append(pathway_box([
"Conjugated bilirubin in intestine",
" ↓ bacterial beta-glucuronidases (distal ileum/colon)",
"Unconjugated bilirubin → gut bacterial reduction",
"Urobilinogens (colourless tetrapyrroles)",
" ↓ oxidation in stool → STERCOBILIN (brown stool colour)",
" ↓ 10–20% reabsorbed → portal blood → liver → re-excreted (enterohepatic circulation)",
" ↓ small fraction → kidney → URINE UROBILINOGEN (normal ≤1 mg/dL)",
]))
els.append(sp(1))
els.append(Paragraph(
"<b>Van den Bergh reaction</b> (still used in labs): Bilirubin + diazotized sulfanilic acid "
"→ purple azopigment (absorbance at 540 nm). "
"<b>Direct fraction</b> = reacts without accelerator = conjugated bilirubin. "
"<b>Indirect fraction</b> = reacts after adding alcohol (breaks albumin bond) = unconjugated bilirubin. "
"Normal total bilirubin: 0.2–1.0 mg/dL. Jaundice clinically visible at >3 mg/dL.",
styles["body"]))
els.append(sp(2))
# J2 Neonatal jaundice
els.append(case_banner("J2 — Physiological Neonatal Jaundice and Kernicterus", C_GOLD, styles))
els.append(sp(1))
els.append(info_box(
"<b>Vignette:</b> 3-day-old full-term male, jaundice extending to chest (Kramer zone II–III). "
"Total bilirubin 12 mg/dL, direct 0.4 mg/dL. DCT negative. Hb normal. Breastfeeding well.",
C_LIGHT_GOLD, C_GOLD, styles))
els.append(sp(1))
els.append(Paragraph("Why Does Physiological Jaundice Occur?", styles["h_sub"]))
els.append(make_table(
["Reason", "Explanation"],
[
["High RBC breakdown", "Fetal RBCs have shorter lifespan (70–90 days). Massive bilirubin load postnatally"],
["Immature UGT1A1", "Neonatal UGT1A1 only ~1% of adult activity at birth. Reaches adult levels by 4–8 weeks"],
["High enterohepatic circulation", "High beta-glucuronidase in gut; sterile gut (no bacteria); slow motility → more reabsorption"],
["Low albumin", "Reduced binding capacity → more free unconjugated bilirubin circulates"],
],
styles, [5*cm, 12*cm]
))
els.append(sp(1))
els.append(Paragraph("Kernicterus", styles["h_sub"]))
for line in [
"• Unconjugated bilirubin (lipophilic + unbound to albumin = 'free bilirubin') crosses the blood-brain barrier",
"• Neonatal BBB is immature → especially vulnerable",
"• Deposits in: <b>basal ganglia (globus pallidus)</b>, cochlear nuclei, cerebellum",
"• Results in: opisthotonos, high-pitched cry, sensorineural hearing loss, choreoathetosis, intellectual disability",
"<b>Phototherapy:</b> Blue light (420–480 nm) converts bilirubin to water-soluble photo-isomers (lumirubin) → excreted in bile/urine without conjugation",
]:
els.append(Paragraph(line, styles["bullet"]))
els.append(sp(1))
els.append(Paragraph("Physiological vs Pathological Neonatal Jaundice", styles["h_sub"]))
els.append(make_table(
["Feature", "Physiological", "Pathological"],
[
["Onset", "Day 2–3", "Within 24 hours of birth"],
["Duration", "Resolves by day 7–10 (term)", "Persists >14 days"],
["Rate of rise", "<5 mg/dL/day", ">5 mg/dL/day"],
["Bilirubin type", "Unconjugated only", "Conjugated (always pathological)"],
["Common causes", "Normal physiology", "Haemolysis, infection, metabolic"],
],
styles, [4*cm, 6.5*cm, 6.5*cm], header_color=C_GOLD
))
els.append(sp(2))
# J3 Obstructive biochemistry
els.append(case_banner("J3 — Obstructive Jaundice: Why Pale Stools and Dark Urine?", C_TEAL, styles))
els.append(sp(1))
els.append(info_box(
"<b>Vignette:</b> 40-year-old obese woman, Charcot's triad: RUQ pain + fever + jaundice. "
"Cola urine, pale stools, intense pruritus. "
"USG: gallstones, dilated CBD 12 mm, stone at CBD. Diagnosis: Choledocholithiasis.",
C_LIGHT_TEAL, C_TEAL, styles))
els.append(sp(1))
els.append(pathway_box([
"NORMAL: Conjugated bilirubin → bile → intestine → urobilinogen → stercobilin → BROWN stool",
" ↓ (enterhepatic) → trace urobilinogen in urine",
"",
"OBSTRUCTION:",
" Conjugated bilirubin BLOCKED → backs up into bloodstream (high direct bilirubin)",
" Water-soluble → filtered by kidney → DARK URINE (bilirubinuria, +4)",
" No bile reaches gut → NO urobilinogen formed → ABSENT urine urobilinogen",
" No stercobilin → PALE / CLAY-COLOURED STOOLS",
]))
els.append(sp(1))
els.append(Paragraph("Why is ALP markedly elevated in obstruction?", styles["h_sub"]))
els.append(Paragraph(
"Back-pressure from bile induces synthesis of <b>alkaline phosphatase (ALP)</b> in cholangiocytes "
"(bile duct epithelial cells). Bile acids also solubilize ALP from hepatocyte canalicular membranes. "
"GGT rises by the same mechanism. Pattern: ALP ↑↑↑, GGT ↑↑↑, ALT/AST mildly elevated = "
"<b>cholestatic (obstructive) pattern</b>.",
styles["body"]))
els.append(sp(1))
els.append(Paragraph("Why is PT prolonged?", styles["h_sub"]))
els.append(Paragraph(
"Bile is required for absorption of <b>fat-soluble vitamins A, D, E, K</b>. "
"Vitamin K activates (gamma-carboxylates) clotting factors <b>II, VII, IX, X</b> via carboxylase enzyme. "
"Bile blockage → Vit K deficiency → prolonged PT. "
"<b>Key test:</b> PT corrects with parenteral Vit K in obstructive jaundice but NOT in hepatocellular "
"(liver too damaged to synthesise factors regardless of Vit K supply).",
styles["body"]))
els.append(sp(2))
# ────────────────────────────────────────────────────────────────
# DIABETES BIOCHEMISTRY
# ────────────────────────────────────────────────────────────────
els.append(hr(C_TEAL, 1.5))
els.append(Paragraph("DIABETES — BIOCHEMISTRY", styles["h_section"]))
# D1 Insulin biosynthesis
els.append(case_banner("D1 — Insulin: From Gene to Secretory Granule", C_MED_BLUE, styles))
els.append(sp(1))
els.append(pathway_box([
"Insulin gene (short arm chromosome 11)",
" ↓ transcription → mRNA",
" ↓ translation on ribosomes → PREPROINSULIN (110 amino acids)",
" [contains: signal peptide + B chain + C peptide + A chain]",
" ↓ signal peptidase cleaves 24-aa leader in ER lumen",
"PROINSULIN (86 aa) = B + C + A [linear chain; 3 disulfide bonds form]",
" ↓ packaged into secretory granules (trans-Golgi)",
" ↓ PC1/3 and PC2 proteases cleave at 2 sites",
"INSULIN (51 aa: A-chain 21 aa + B-chain 30 aa) + C-PEPTIDE (31 aa)",
" ↓ stored in granule with Zinc",
" ↓ glucose stimulus → exocytosis → both secreted 1:1 into portal blood",
]))
els.append(sp(1))
els.append(make_table(
["Component", "Size", "Key Point"],
[
["Preproinsulin", "110 amino acids", "Initial translation product; enters RER"],
["Proinsulin", "86 amino acids", "After signal peptide cleavage; folds in RER"],
["C-peptide", "31 amino acids", "Cleaved in Golgi; co-secreted 1:1 with insulin"],
["Insulin (mature)", "51 amino acids", "A-chain (21 aa) + B-chain (30 aa); 2 interchain + 1 intrachain disulfide bonds"],
],
styles, [3.5*cm, 4*cm, 9.5*cm]
))
els.append(sp(1))
els.append(info_box(
"<b>C-peptide clinical use:</b> Measures endogenous insulin secretion. "
"Low C-peptide = Type 1 DM (beta cells destroyed). "
"High insulin + low C-peptide = exogenous insulin injection. "
"High insulin + high C-peptide = insulinoma (autonomous secretion).",
C_LIGHT_BLUE, C_MED_BLUE, styles))
els.append(sp(1))
els.append(quote_box(
"Insulin is initially synthesized as a single-chain 86-amino-acid precursor polypeptide, preproinsulin. "
"Cleavage of an internal 31-residue fragment from proinsulin generates C-peptide with the A (21 amino "
"acids) and B (30 amino acids) chains connected by disulfide bonds. The mature insulin molecule and "
"C-peptide are stored together and co-secreted from secretory granules in the beta cells.",
"Harrison's Principles of Internal Medicine, 22nd Ed. (2025)"
))
els.append(sp(2))
# D2 Beta cell secretion
els.append(case_banner("D2 — Beta-cell Insulin Secretion: K_ATP Channel and Sulfonylureas", C_TEAL, styles))
els.append(sp(1))
els.append(pathway_box([
"Glucose rises in blood post-meal",
" ↓ enters beta cell via GLUT2 (constitutive; not insulin-dependent)",
" ↓ GLUCOKINASE phosphorylates glucose → Glucose-6-phosphate ← RATE-LIMITING STEP",
" ↓ glycolysis + oxidative phosphorylation → ATP generated (ATP:ADP ratio rises)",
" ↓ ATP-sensitive K⁺ channel (K_ATP) CLOSES",
" ↓ K⁺ cannot leave → membrane DEPOLARISES (–70 mV → 0 mV)",
" ↓ Voltage-gated Ca²⁺ channels OPEN",
" ↓ Ca²⁺ influx into beta cell",
" ↓ Secretory granules fuse with plasma membrane → EXOCYTOSIS",
" ↓ Insulin + C-peptide + proinsulin released into portal circulation",
]))
els.append(sp(1))
els.append(info_box(
"<b>How do sulfonylureas work?</b> They bind directly to the <b>SUR1 subunit</b> of the K_ATP channel, "
"keeping it closed regardless of glucose levels — mimicking ATP inhibition. This causes membrane "
"depolarisation → Ca²⁺ influx → insulin release. "
"Risk: hypoglycaemia even at normal blood glucose (unlike metformin).",
C_LIGHT_TEAL, C_TEAL, styles))
els.append(sp(1))
els.append(Paragraph("Two-Phase Insulin Secretion", styles["h_sub"]))
els.append(make_table(
["Phase", "Timing", "Source", "Lost in T2DM?"],
[
["First phase (rapid spike)", "0–10 min", "Pre-formed granules already docked at membrane", "YES — lost early"],
["Second phase (sustained)", "10–60 min", "Newly synthesized + mobilized granules", "Partially preserved initially"],
],
styles, [3.5*cm, 3*cm, 6*cm, 4.5*cm]
))
els.append(sp(1))
els.append(info_box(
"<b>Key exam point:</b> Loss of first-phase insulin secretion is one of the earliest detectable "
"abnormalities in Type 2 DM. It causes the post-meal glucose spike that eventually drives microvascular "
"and macrovascular complications.",
C_LIGHT_GOLD, C_GOLD, styles))
els.append(sp(2))
# D3 DKA biochemistry
els.append(case_banner("D3 — DKA Biochemistry: Ketone Bodies, Anion Gap, Oxaloacetate Depletion", C_RED, styles))
els.append(sp(1))
els.append(Paragraph("Ketone Body Formation — Full Pathway", styles["h_sub"]))
els.append(pathway_box([
"No insulin → glucagon dominates → activates hormone-sensitive lipase (adipose)",
"Triglycerides → Glycerol + Free Fatty Acids (FFAs)",
" ↓ FFAs enter liver",
" ↓ Carnitine acyltransferase I (CPTI) transports FAs into mitochondria",
" ↓ Beta-oxidation → massive Acetyl-CoA production",
"",
"WHY can't Acetyl-CoA enter TCA? → Oxaloacetate (OAA) is depleted!",
" (OAA is drained for gluconeogenesis via PEPCK → PEP)",
"",
"Acetyl-CoA overflow → KETOGENESIS:",
" 2× Acetyl-CoA → Acetoacetyl-CoA",
" + Acetyl-CoA → HMG-CoA [HMG-CoA synthase — mitochondrial]",
" HMG-CoA → Acetoacetate + Acetyl-CoA [HMG-CoA lyase]",
" Acetoacetate → Beta-hydroxybutyrate [NADH-dependent; beta-HB dehydrogenase]",
" Acetoacetate → Acetone + CO₂ [spontaneous decarboxylation → FRUITY BREATH]",
]))
els.append(sp(1))
els.append(info_box(
"<b>Why can the liver make but not use ketones?</b> The liver lacks "
"<b>3-ketoacid CoA transferase (thiophorase)</b> — the enzyme needed to convert acetoacetate back "
"to acetoacetyl-CoA. So the liver makes ketones and ships them to muscle and brain (which have "
"thiophorase) to use as fuel.",
C_LIGHT_RED, C_RED, styles))
els.append(sp(1))
els.append(Paragraph("Anion Gap Calculation", styles["h_sub"]))
els.append(Paragraph(
"Anion Gap = Na⁺ − (Cl⁻ + HCO₃⁻) | Normal = 8–12 mEq/L",
styles["body_bold"]))
els.append(Paragraph(
"In DKA: Ketoacids (acetoacetate + beta-hydroxybutyrate) are unmeasured anions → gap widens. "
"Example: Na 130, Cl 96, HCO₃ 9 → AG = 130 − (96+9) = <b>25 mEq/L</b> (high). "
"The 'missing' anions = ketoacids.",
styles["body"]))
els.append(sp(1))
els.append(quote_box(
"During prolonged starvation, or whenever carbohydrate metabolism is severely impaired as in "
"untreated type 1 diabetes mellitus, the formation of acetyl-CoA exceeds the supply of oxaloacetate. "
"The resulting excess acetyl-CoA is diverted to form acetoacetic acid, beta-hydroxybutyric acid, "
"and acetone — three compounds known collectively as ketone bodies.",
"Tietz Textbook of Laboratory Medicine, 7th Ed."
))
els.append(sp(2))
# D4 HbA1c
els.append(case_banner("D4 — HbA1c: Glycation, Amadori Product, Clinical Interpretation", C_PURPLE, styles))
els.append(sp(1))
els.append(pathway_box([
"Step 1 — Schiff Base (reversible, hours):",
" Glucose (aldehyde group) + NH₂-terminal valine of Hb beta chain",
" → Unstable Schiff base (aldimine)",
"",
"Step 2 — Amadori Rearrangement (irreversible):",
" Schiff base undergoes molecular rearrangement",
" → Stable ketoamine = HbA1c ← persists for life of RBC",
]))
els.append(sp(1))
els.append(Paragraph(
"Rate of HbA1c formation is directly proportional to ambient glucose concentration (mass-action). "
"Reflects <b>weighted average blood glucose over 8–12 weeks</b> (recent weeks contribute more "
"due to RBC age distribution). This is <b>non-enzymatic glycosylation (glycation)</b> — no enzyme involved.",
styles["body"]))
els.append(sp(1))
els.append(make_table(
["HbA1c Value", "Interpretation", "Action"],
[
["<5.7% (<39 mmol/mol)", "Normal", "No action"],
["5.7–6.4% (39–46 mmol/mol)", "Prediabetes (high risk)", "Lifestyle modification"],
["≥6.5% (≥48 mmol/mol)", "Diabetes mellitus", "Diagnostic + treat"],
["<7.0%", "Target for most T2DM patients", "Good control"],
[">8.0%", "Poor glycaemic control", "Intensify therapy"],
],
styles, [4.5*cm, 6.5*cm, 6*cm], header_color=C_PURPLE
))
els.append(sp(1))
els.append(Paragraph("Conditions Affecting HbA1c Accuracy", styles["h_sub"]))
els.append(make_table(
["Falsely LOW HbA1c", "Falsely HIGH HbA1c"],
[
["Haemolytic anaemia (RBCs destroyed early)", "Iron deficiency anaemia (older RBCs; more time for glycation)"],
["Recent blood transfusion (new donor RBCs)", "Asplenia (prolonged RBC lifespan)"],
["Pregnancy (increased RBC turnover)", "Renal failure (carbamylated Hb interferes)"],
["HbS, HbC variants (assay interference)", "Alcoholism"],
],
styles, [8.5*cm, 8.5*cm], header_color=C_PURPLE
))
els.append(PageBreak())
return els
def section_quick_reference(styles):
els = []
els.append(part_banner("PART 3 — Quick Reference Tables (Exam Ready)", C_RED, styles))
els.append(sp(2))
# Jaundice comparison mega-table
els.append(Paragraph("Jaundice — Master Comparison Table", styles["h_section"]))
els.append(make_table(
["Feature", "Prehepatic", "Hepatocellular", "Posthepatic"],
[
["Serum bilirubin", "Unconjugated (indirect) ↑↑", "Both ↑ (conjugated + unconjugated)", "Conjugated (direct) ↑↑"],
["Urine bilirubin", "ABSENT (acholuric)", "PRESENT (+1 to +3)", "PRESENT (+4)"],
["Urine urobilinogen", "↑↑↑ (excess production)", "Variable (↑ then ↓)", "ABSENT"],
["Stool colour", "Dark (excess stercobilin)", "Normal or pale", "Pale / clay-coloured"],
["ALT / AST", "Normal", "↑↑↑ (ALT > AST viral)", "Mildly elevated"],
["ALP / GGT", "Normal", "Mild ↑", "↑↑↑ (markedly)"],
["Serum albumin", "Normal", "Low in chronic disease", "Normal initially"],
["Prothrombin time", "Normal", "Prolonged (hepatocellular)", "Prolonged; corrects with Vit K"],
["Splenomegaly", "Common (haemolysis)", "May occur", "Absent usually"],
["Example causes", "Haemolysis, Gilbert's, G6PD", "Viral hepatitis, drugs, alcohol", "Gallstones, Ca pancreas, PSC"],
],
styles, [4*cm, 4.3*cm, 4.3*cm, 4.4*cm]
))
els.append(sp(2))
# Bilirubin pathway enzyme table
els.append(Paragraph("Bilirubin Pathway — Enzymes and Defects", styles["h_section"]))
els.append(make_table(
["Step", "Enzyme", "Product", "Disease if Defective"],
[
["Heme → Biliverdin", "Heme oxygenase", "Biliverdin + CO + Fe²⁺", "—"],
["Biliverdin → Bilirubin", "Biliverdin reductase", "Unconjugated bilirubin", "—"],
["Bilirubin → Glucuronide", "UGT1A1 (UDP-glucuronosyltransferase)", "Conjugated bilirubin", "Gilbert's (30% ↓), Crigler-Najjar (absent)"],
["Hepatocyte → Bile", "MRP2 transporter (ABCC2)", "Bilirubin excreted in bile", "Dubin-Johnson syndrome"],
["Gut deconjugation", "Bacterial beta-glucuronidase", "Urobilinogen", "—"],
],
styles, [3.5*cm, 5*cm, 4.5*cm, 4*cm]
))
els.append(sp(2))
# Diabetes comparison
els.append(Paragraph("Diabetes — Type 1 vs Type 2 vs DKA vs HHS", styles["h_section"]))
els.append(make_table(
["Feature", "Type 1 DM", "Type 2 DM"],
[
["Pathophysiology", "Autoimmune destruction of beta cells → absolute insulin deficiency", "Insulin resistance + relative beta cell failure"],
["Age of onset", "Usually <30 years (peak childhood)", "Usually >40 years (now increasingly younger)"],
["Body habitus", "Thin / normal BMI", "Obese (typically)"],
["Autoantibodies", "Anti-GAD, anti-IA2, anti-islet", "Absent"],
["C-peptide", "Very low / absent", "Normal or high initially"],
["Insulin needed", "Always (absolute requirement)", "May not require initially"],
["Acute complication", "Diabetic Ketoacidosis (DKA)", "Hyperosmolar Hyperglycaemic State (HHS)"],
["Ketosis", "Yes (no insulin → unrestrained lipolysis)", "No (residual insulin suppresses lipolysis)"],
["HLA association", "HLA-DR3, DR4", "Not HLA-associated"],
],
styles, [4.5*cm, 6.3*cm, 6.2*cm]
))
els.append(sp(2))
# Insulin biosynthesis quick ref
els.append(Paragraph("Insulin Biosynthesis — Quick Reference", styles["h_section"]))
els.append(make_table(
["Fact", "Answer"],
[
["Insulin gene chromosome", "Short arm of chromosome 11"],
["Initial translation product", "Preproinsulin (110 amino acids)"],
["After signal peptide cleavage", "Proinsulin (86 amino acids)"],
["Mature insulin size", "51 amino acids (A-chain 21 + B-chain 30)"],
["Number of disulfide bonds", "3 (2 interchain A-B + 1 intrachain in A chain)"],
["Co-secreted with insulin", "C-peptide (31 aa) in 1:1 molar ratio"],
["C-peptide clinical use", "Measures endogenous insulin secretion"],
["Beta cell glucose sensor enzyme", "Glucokinase (hexokinase IV)"],
["Channel closed by ATP in beta cell", "K_ATP channel (SUR1 + Kir6.2 subunits)"],
["Sulfonylurea binding site", "SUR1 subunit of K_ATP channel"],
["Insulin-dependent glucose transporter", "GLUT4 (in muscle and adipose)"],
["Constitutive glucose transporters", "GLUT1 (brain/RBC), GLUT2 (liver/beta cell), GLUT3 (neurons)"],
],
styles, [7*cm, 10*cm]
))
els.append(sp(2))
# Ketone bodies table
els.append(Paragraph("Ketone Bodies — Key Facts", styles["h_section"]))
els.append(make_table(
["Ketone Body", "Formation", "Smell", "Detected By"],
[
["Acetoacetate", "First formed — from HMG-CoA cleavage", "Slightly fruity", "Nitroprusside (Ketostix) — detects this"],
["Beta-hydroxybutyrate", "Reduction of acetoacetate (NADH-dependent)", "None", "NOT detected by Ketostix! Specific assay needed"],
["Acetone", "Spontaneous decarboxylation of acetoacetate", "Fruity / pear drops", "Exhaled in breath; Ketostix weakly positive"],
],
styles, [4.5*cm, 5*cm, 3*cm, 4.5*cm]
))
els.append(sp(1))
els.append(info_box(
"<b>Exam trap:</b> In severe DKA, beta-hydroxybutyrate may predominate over acetoacetate "
"(because NADH is high, driving the equilibrium). Ketostix (nitroprusside) tests only detect "
"acetoacetate — so severe DKA may appear 'ketone-negative' on dipstick. "
"Always measure serum beta-hydroxybutyrate directly.",
C_LIGHT_RED, C_RED, styles))
els.append(sp(2))
# HbA1c
els.append(Paragraph("HbA1c — Diagnostic Reference", styles["h_section"]))
els.append(make_table(
["HbA1c %", "mmol/mol", "Meaning", "Estimated Average Glucose (eAG)"],
[
["<5.7%", "<39", "Normal", "<117 mg/dL"],
["5.7–6.4%", "39–46", "Prediabetes", "117–137 mg/dL"],
["6.5%", "48", "Diabetes (diagnostic cut-off)", "140 mg/dL"],
["7.0%", "53", "Target (most T2DM)", "154 mg/dL"],
["8.0%", "64", "Poor control — intensify", "183 mg/dL"],
["10.0%", "86", "Very poor control", "240 mg/dL"],
],
styles, [3*cm, 3.5*cm, 5.5*cm, 5*cm]
))
els.append(sp(1))
els.append(Paragraph(
"eAG formula: eAG (mg/dL) = (28.7 × HbA1c%) − 46.7",
styles["body_bold"]))
els.append(sp(2))
# Sources footer
els.append(hr(C_DARK_BLUE, 1))
els.append(Paragraph("Primary Sources", styles["h_sub"]))
sources = [
"Harrison's Principles of Internal Medicine, 22nd Ed. (2025, McGraw-Hill) — Bilirubin metabolism, Insulin biosynthesis/secretion",
"Goldman-Cecil Medicine, International Ed. — Jaundice diagnosis, DKA clinical features and management",
"Schwartz's Principles of Surgery, 11th Ed. — Surgical jaundice, biliary obstruction",
"Harper's Illustrated Biochemistry, 32nd Ed. — Liver function tests, aminotransferases",
"Tietz Textbook of Laboratory Medicine, 7th Ed. — Ketogenesis, HbA1c, diabetes diagnosis",
"Medical Physiology (Boron & Boulpaep) — Beta-cell physiology, insulin synthesis, GLUT transporters",
]
for s in sources:
els.append(Paragraph(f"• {s}", styles["source"]))
return els
# ── Main build ────────────────────────────────────────────────────────────────
def build_pdf():
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
doc = SimpleDocTemplate(
OUTPUT_PATH,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.2*cm, bottomMargin=2.2*cm,
title="MBBS Clinical Cases Study Guide — Jaundice & Diabetes",
author="Orris Medical Study Guide",
subject="Biochemistry | Jaundice | Diabetes | MBBS",
)
styles = build_styles()
story = []
story += build_cover(styles)
story += section_3rd_year(styles)
story += section_1st_2nd_year(styles)
story += section_quick_reference(styles)
doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"PDF created: {OUTPUT_PATH}")
if __name__ == "__main__":
build_pdf()