Make it the same way ...most comprehensive with all required notes fo final year mbbs student...into booklet types notes into left amd rigjt of every page along with sample notes I attached...
cd /home/daytona/workspace/attachments/0099bcdf-8d21-4b64-ba2c-5093cff3528d/ && …
cd /home/daytona/workspace/attachments/0099bcdf-8d21-4b64-ba2c-5093cff3528d/ && …
mkdir -p /home/daytona/workspace/gi_notes_preview && pdftoppm -r 150 "/home/dayt…
cd /home/daytona/workspace/gi_notes_preview && python3 -c " from PIL import Imag…
Fast
mkdir -p /home/daytona/workspace/upper_gi_notes && python3 -c "from reportlab.li…
Failed to create session: bad request: failed to resolve container IP after 3 attempts: no IP address found. Is the Sandbox started?
We encountered an error while processing your request. Please try again or reach out to us for help.
What happened
Go
mkdir -p /home/daytona/workspace/upper_gi_notes && python3 -c "from reportlab.li…
~/upper_gi_notes/generate_notes.py
#!/usr/bin/env python3
"""
Comprehensive Upper GI MBBS Notes - Booklet Style
Mimics handwritten note format: cream background, two-column layout,
circled numbers, underlined headings, color highlights
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import os
# ─── COLORS ───────────────────────────────────────────────────────────────────
CREAM = HexColor("#FDFBF3")
DARK_CREAM = HexColor("#F5F0DC")
BORDER_GRAY = HexColor("#9E9E9E")
INK_BLACK = HexColor("#1A1A1A")
BLUE_INK = HexColor("#1B3A8C")
RED_INK = HexColor("#B22222")
GREEN_INK = HexColor("#1A5C2A")
ORANGE_INK = HexColor("#CC6600")
LIGHT_BLUE = HexColor("#DCE8F5")
LIGHT_YELL = HexColor("#FFF9C4")
DIVIDER = HexColor("#C0B080")
W, H = A4 # 595 x 842 pt
# ─── LAYOUT CONSTANTS ─────────────────────────────────────────────────────────
MARGIN_LEFT = 18*mm
MARGIN_RIGHT = 18*mm
MARGIN_TOP = 28*mm
MARGIN_BOT = 18*mm
COL_GAP = 8*mm
# Left column: 58% of usable width, Right column: 38%
USABLE_W = W - MARGIN_LEFT - MARGIN_RIGHT
LEFT_W = USABLE_W * 0.60
RIGHT_W = USABLE_W * 0.38
LEFT_X = MARGIN_LEFT
RIGHT_X = MARGIN_LEFT + LEFT_W + COL_GAP
OUTPUT = "/home/daytona/workspace/upper_gi_notes/Upper_GI_MBBS_Notes.pdf"
# ─── FONT SIZES ───────────────────────────────────────────────────────────────
FS_TITLE = 13
FS_HEAD = 10.5
FS_SUB = 9.5
FS_BODY = 8.8
FS_SMALL = 8.0
FS_TINY = 7.5
LINE_H = 5.2*mm # normal line height
HEAD_H = 6.5*mm # heading line height
PARA_GAP = 2.8*mm # gap after paragraph/list block
# ─── CANVAS SETUP ─────────────────────────────────────────────────────────────
c = canvas.Canvas(OUTPUT, pagesize=A4)
# ─── HELPERS ──────────────────────────────────────────────────────────────────
def new_page(c, topic, date_str="", page_num=1):
"""Draw page background, header box, divider line."""
c.setFillColor(CREAM)
c.rect(0, 0, W, H, fill=1, stroke=0)
# faint ruled lines
c.setStrokeColor(HexColor("#E8E0C8"))
c.setLineWidth(0.3)
y = H - MARGIN_TOP - 2*mm
while y > MARGIN_BOT:
c.line(MARGIN_LEFT - 4*mm, y, W - MARGIN_RIGHT + 4*mm, y)
y -= LINE_H
# header area
c.setFillColor(DARK_CREAM)
c.roundRect(MARGIN_LEFT, H - 22*mm, LEFT_W * 0.72, 14*mm, 3*mm, fill=1, stroke=1)
c.setStrokeColor(BORDER_GRAY)
c.setLineWidth(1.2)
c.roundRect(MARGIN_LEFT, H - 22*mm, LEFT_W * 0.72, 14*mm, 3*mm, fill=0, stroke=1)
c.setFont("Helvetica-Bold", FS_TITLE)
c.setFillColor(BLUE_INK)
c.drawString(MARGIN_LEFT + 4*mm, H - 14*mm, topic)
# date box top right
if date_str:
dx = W - MARGIN_RIGHT - 42*mm
c.setFillColor(DARK_CREAM)
c.roundRect(dx, H - 22*mm, 42*mm, 14*mm, 3*mm, fill=1, stroke=1)
c.setStrokeColor(BORDER_GRAY)
c.setLineWidth(1.2)
c.roundRect(dx, H - 22*mm, 42*mm, 14*mm, 3*mm, fill=0, stroke=1)
c.setFont("Helvetica-Bold", 9.5)
c.setFillColor(INK_BLACK)
c.drawString(dx + 3*mm, H - 13.5*mm, f"DATE: {date_str}")
# thick divider line under header
c.setStrokeColor(DIVIDER)
c.setLineWidth(2.0)
c.line(MARGIN_LEFT - 4*mm, H - 26*mm, W - MARGIN_RIGHT + 4*mm, H - 26*mm)
# vertical divider between columns
c.setStrokeColor(HexColor("#D0C8A0"))
c.setLineWidth(0.8)
mid_x = LEFT_X + LEFT_W + COL_GAP / 2
c.line(mid_x, H - 28*mm, mid_x, MARGIN_BOT + 4*mm)
# watermark
c.setFont("Helvetica", FS_TINY)
c.setFillColor(HexColor("#AAAAAA"))
c.drawRightString(W - MARGIN_RIGHT, 10*mm, "Created by NOSHEEN SQ")
# page number
c.setFont("Helvetica", FS_TINY)
c.setFillColor(HexColor("#888888"))
c.drawCentredString(W/2, 10*mm, str(page_num))
def draw_heading(c, x, y, text, color=BLUE_INK, fs=FS_HEAD, underline=True, max_w=None):
"""Draw an underlined heading. Returns new y."""
c.setFont("Helvetica-Bold", fs)
c.setFillColor(color)
c.drawString(x, y, text)
if underline:
tw = c.stringWidth(text, "Helvetica-Bold", fs)
if max_w:
tw = min(tw, max_w)
c.setStrokeColor(color)
c.setLineWidth(0.8)
c.line(x, y - 1.2*mm, x + tw, y - 1.2*mm)
return y - HEAD_H
def draw_sub_heading(c, x, y, text, color=INK_BLACK, fs=FS_SUB):
"""Draw a sub-heading (bold, no underline). Returns new y."""
c.setFont("Helvetica-Bold", fs)
c.setFillColor(color)
c.drawString(x, y, text)
return y - HEAD_H * 0.9
def circled_num(c, x, y, n, color=INK_BLACK, fs=FS_BODY):
"""Draw a circled number at (x,y). Returns x after the circle."""
r = 2.8*mm
cx = x + r
cy = y + r * 0.25
c.setStrokeColor(color)
c.setFillColor(CREAM)
c.setLineWidth(0.8)
c.circle(cx, cy, r, fill=1, stroke=1)
c.setFillColor(color)
c.setFont("Helvetica-Bold", fs - 0.5)
c.drawCentredString(cx, cy - fs * 0.35, str(n))
return x + r * 2 + 1.5*mm
def draw_item(c, x, y, num, text, color=INK_BLACK, fs=FS_BODY, max_w=None, bold_first=False):
"""Draw a numbered list item (circled). Wraps text. Returns new y."""
if max_w is None:
max_w = LEFT_W - 10*mm
nx = circled_num(c, x, y, num, color, fs)
item_w = max_w - (nx - x)
c.setFont("Helvetica-Bold" if bold_first else "Helvetica", fs)
c.setFillColor(color)
# wrap text
words = text.split()
lines = []
cur = ""
for w in words:
test = (cur + " " + w).strip()
if c.stringWidth(test, "Helvetica", fs) <= item_w:
cur = test
else:
if cur:
lines.append(cur)
cur = w
if cur:
lines.append(cur)
for i, line in enumerate(lines):
if i == 0:
c.drawString(nx, y, line)
else:
c.drawString(nx, y - i * LINE_H, line)
c.setFont("Helvetica", fs)
return y - max(1, len(lines)) * LINE_H - 1*mm
def draw_bullet(c, x, y, text, color=INK_BLACK, fs=FS_BODY, max_w=None, indent=3*mm, arrow=False):
"""Draw a bullet/arrow item. Returns new y."""
if max_w is None:
max_w = LEFT_W - 10*mm
bullet = "→" if arrow else "•"
bx = x + indent
c.setFont("Helvetica", fs)
c.setFillColor(color)
c.drawString(bx, y, bullet)
tx = bx + 4*mm
item_w = max_w - (tx - x)
words = text.split()
lines = []
cur = ""
for w in words:
test = (cur + " " + w).strip()
if c.stringWidth(test, "Helvetica", fs) <= item_w:
cur = test
else:
if cur:
lines.append(cur)
cur = w
if cur:
lines.append(cur)
for i, line in enumerate(lines):
c.drawString(tx, y - i * LINE_H, line)
return y - max(1, len(lines)) * LINE_H - 0.5*mm
def draw_text(c, x, y, text, color=INK_BLACK, fs=FS_BODY, max_w=None, bold=False):
"""Draw wrapped text. Returns new y."""
if max_w is None:
max_w = LEFT_W - 4*mm
font = "Helvetica-Bold" if bold else "Helvetica"
c.setFont(font, fs)
c.setFillColor(color)
words = text.split()
lines = []
cur = ""
for w in words:
test = (cur + " " + w).strip()
if c.stringWidth(test, font, fs) <= max_w:
cur = test
else:
if cur:
lines.append(cur)
cur = w
if cur:
lines.append(cur)
for i, line in enumerate(lines):
c.drawString(x, y - i * LINE_H, line)
return y - max(1, len(lines)) * LINE_H - 1*mm
def highlight_box(c, x, y, text, bg=LIGHT_BLUE, text_color=BLUE_INK, fs=FS_SMALL, max_w=None):
"""Draw a highlighted box with text. Returns new y."""
if max_w is None:
max_w = LEFT_W - 4*mm
pad = 2*mm
# wrap
words = text.split()
lines = []
cur = ""
for w in words:
test = (cur + " " + w).strip()
if c.stringWidth(test, "Helvetica-Bold", fs) <= max_w - 2*pad:
cur = test
else:
if cur:
lines.append(cur)
cur = w
if cur:
lines.append(cur)
box_h = len(lines) * LINE_H + 2 * pad
c.setFillColor(bg)
c.setStrokeColor(text_color)
c.setLineWidth(0.5)
c.roundRect(x, y - box_h + pad, max_w, box_h, 2*mm, fill=1, stroke=1)
c.setFillColor(text_color)
c.setFont("Helvetica-Bold", fs)
ty = y - pad
for line in lines:
c.drawString(x + pad, ty, line)
ty -= LINE_H
return y - box_h - 1.5*mm
def draw_table_row(c, x, y, cells, widths, fs=FS_SMALL, header=False, bg=None):
"""Draw a simple table row. Returns new y."""
if bg:
c.setFillColor(bg)
c.rect(x, y - LINE_H - 1*mm, sum(widths), LINE_H + 2*mm, fill=1, stroke=0)
font = "Helvetica-Bold" if header else "Helvetica"
c.setFont(font, fs)
c.setFillColor(INK_BLACK if not header else BLUE_INK)
cx = x
for i, (cell, w) in enumerate(zip(cells, widths)):
c.drawString(cx + 1.5*mm, y, str(cell))
cx += w
# row border
c.setStrokeColor(BORDER_GRAY)
c.setLineWidth(0.4)
c.line(x, y - LINE_H - 0.5*mm, x + sum(widths), y - LINE_H - 0.5*mm)
return y - LINE_H - 1*mm
def gap(y, size=PARA_GAP):
return y - size
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 1 - GI BLEEDING: Overview, Causes, Clinical Presentation
# ══════════════════════════════════════════════════════════════════════════════
page = 1
new_page(c, "Upper GI - Dr. Liakat", "26/05/26", page)
ly = H - MARGIN_TOP - 2*mm # left column y
ry = H - MARGIN_TOP - 2*mm # right column y
# ── LEFT COLUMN ──────────────────────────────────────────────────────────────
ly = draw_heading(c, LEFT_X, ly, "GI Bleeding", BLUE_INK, FS_HEAD + 1)
ly = gap(ly, 2*mm)
# Classification tree (text-based)
c.setFont("Helvetica-Bold", FS_BODY)
c.setFillColor(INK_BLACK)
c.drawCentredString(LEFT_X + LEFT_W/2, ly, "GI Bleeding")
ly -= 3*mm
# branches
bx = LEFT_X + LEFT_W/2
c.setStrokeColor(INK_BLACK)
c.setLineWidth(1.0)
c.line(bx, ly, bx, ly - 4*mm)
c.line(bx - 30*mm, ly - 4*mm, bx + 30*mm, ly - 4*mm)
c.line(bx - 30*mm, ly - 4*mm, bx - 30*mm, ly - 8*mm)
c.line(bx + 30*mm, ly - 4*mm, bx + 30*mm, ly - 8*mm)
c.setFont("Helvetica-Bold", FS_BODY)
c.setFillColor(BLUE_INK)
c.drawCentredString(bx - 30*mm, ly - 11*mm, "Upper GI")
c.setFillColor(RED_INK)
c.drawCentredString(bx + 30*mm, ly - 11*mm, "Lower GI")
ly -= 16*mm
ly = draw_heading(c, LEFT_X, ly, "Causes of Upper GI Bleeding", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "COMMON:", BLUE_INK)
ly = draw_item(c, LEFT_X + 2*mm, ly, 1, "Peptic Ulcer Disease (PUD) – gastric & duodenal", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 2, "Gastritis / Erosive gastritis", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 3, "Ruptured Oesophageal Varices (portal HTN)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 1.5*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "OTHERS:", RED_INK)
ly = draw_item(c, LEFT_X + 2*mm, ly, 1, "Mallory-Weiss Tear", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 2, "Gastric Carcinoma", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 3, "Dieulafoy lesion (gastric arterial ectasia)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 4, "Coagulopathy / anticoagulant use", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 5, "Aorto-duodenal fistula", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 6, "NSAID / aspirin use", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 3*mm)
ly = draw_heading(c, LEFT_X, ly, "Clinical Presentation", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 1, "Haematemesis – vomiting blood (bright red or 'coffee ground')", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 2, "Melaena – black tarry stools (altered blood, >100 ml)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 3, "Circulatory collapse / Haemorrhagic shock", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 4, "Haematochezia – if brisk bleeding (>1000 ml/hr)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 5, "Features of underlying cause (jaundice, spider naevi, etc.)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 3*mm)
ly = draw_heading(c, LEFT_X, ly, "Assessment", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "① History:", INK_BLACK)
ly = draw_bullet(c, LEFT_X + 2*mm, ly, "Onset & duration of bleeding", INK_BLACK, FS_BODY, LEFT_W - 4*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 2*mm, ly, "Amount of blood loss (>150 ml = massive haematemesis)", INK_BLACK, FS_BODY, LEFT_W - 4*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 2*mm, ly, "Abdominal pain, dysphagia, distension, jaundice", INK_BLACK, FS_BODY, LEFT_W - 4*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 2*mm, ly, "Drug history: NSAIDs, aspirin, anticoagulants, steroids", INK_BLACK, FS_BODY, LEFT_W - 4*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 2*mm, ly, "Alcohol history, previous ulcer / liver disease", INK_BLACK, FS_BODY, LEFT_W - 4*mm, arrow=True)
# ── RIGHT COLUMN ─────────────────────────────────────────────────────────────
ry = draw_heading(c, RIGHT_X, ry, "Oesophageal Causes", RED_INK, FS_SUB)
ry = gap(ry, 1*mm)
for t in ["Oesophageal varices", "Reflux oesophagitis (GORD)", "Oesophageal carcinoma", "Mallory-Weiss tear"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 2*mm, ry, "• " + t)
ry -= LINE_H
ry = gap(ry, 5*mm)
ry = draw_heading(c, RIGHT_X, ry, "Gastric Causes", RED_INK, FS_SUB)
ry = gap(ry, 1*mm)
for t in ["Peptic ulcer (most common)", "Gastric erosions", "Gastric carcinoma", "Dieulafoy lesion", "Gastric antral vascular ectasia"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 2*mm, ry, "• " + t)
ry -= LINE_H
ry = gap(ry, 5*mm)
ry = draw_heading(c, RIGHT_X, ry, "Duodenal Causes", RED_INK, FS_SUB)
ry = gap(ry, 1*mm)
for t in ["Duodenal ulcer", "Duodenitis", "Aorto-duodenal fistula"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 2*mm, ry, "• " + t)
ry -= LINE_H
ry = gap(ry, 6*mm)
ry = highlight_box(c, RIGHT_X, ry, "⚠ MASSIVE HAEMATEMESIS: Blood loss >150 ml per episode", LIGHT_YELL, RED_INK, FS_SMALL, RIGHT_W)
ry = gap(ry, 5*mm)
ry = draw_heading(c, RIGHT_X, ry, "Coffee Ground Emesis", BLUE_INK, FS_SMALL)
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
ry -= 1*mm
c.drawString(RIGHT_X + 1*mm, ry, "= Digested blood (slow bleed)")
ry -= LINE_H
c.drawString(RIGHT_X + 1*mm, ry, "= Oxidised Hb → dark colour")
ry -= LINE_H * 1.5
ry = highlight_box(c, RIGHT_X, ry, "Melaena needs >100 ml of blood in upper GIT (digested by HCl)", LIGHT_BLUE, BLUE_INK, FS_SMALL, RIGHT_W)
ry = gap(ry, 5*mm)
ry = draw_heading(c, RIGHT_X, ry, "Stigmata of CLD", GREEN_INK, FS_SUB)
ry = gap(ry, 1*mm)
for t in ["Leuconychia (white nails)", "Clubbing", "Palmar erythema", "Spider naevi", "Caput medusae", "Gynaecomastia", "Jaundice", "Asterixis (flap)"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 2*mm, ry, "→ " + t)
ry -= LINE_H
c.showPage()
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 2 - ASSESSMENT: Examination, Investigations, Rockall Score
# ══════════════════════════════════════════════════════════════════════════════
page = 2
new_page(c, "Upper GI Bleeding – Assessment & Scoring", "", page)
ly = H - MARGIN_TOP - 2*mm
ry = H - MARGIN_TOP - 2*mm
# ── LEFT COLUMN ──────────────────────────────────────────────────────────────
ly = draw_heading(c, LEFT_X, ly, "② Examination", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "A. Haemodynamic Status:")
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Pulse rate (tachycardia → shock)", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Blood pressure (hypotension in haemorrhagic shock)", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Capillary refill time (>2 sec = impaired perfusion)", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Signs of anaemia (pallor, conjunctival pallor)", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = gap(ly, 2*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "B. Stigmata of CLD (= Oesophageal Varices):")
for s in ["Leuconychia", "Clubbing", "Palmar erythema", "Spider naevi", "Caput medusae", "Ascites", "Hepatosplenomegaly", "Jaundice"]:
c.setFont("Helvetica", FS_BODY)
c.setFillColor(INK_BLACK)
c.drawString(LEFT_X + 6*mm, ly, "→ " + s)
ly -= LINE_H
ly = gap(ly, 2*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "C. Systemic Examination:")
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Ascites → CLD → oesophageal varices", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Abdominal mass → gastric carcinoma", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Epigastric tenderness → PUD", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Duodenal point tenderness → duodenal ulcer", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = gap(ly, 3*mm)
ly = draw_heading(c, LEFT_X, ly, "③ Investigations", BLUE_INK)
ly = gap(ly, 1*mm)
invx = LEFT_X + 2*mm
ly = draw_sub_heading(c, invx, ly, "Bloods:")
ly = draw_bullet(c, invx + 2*mm, ly, "CBC – Hb%, haematocrit, platelets", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "LFTs – assess liver function / CLD", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "Coagulation profile – PT, APTT, INR", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "U&E, creatinine, electrolytes – renal status", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "Blood grouping & crossmatch (for transfusion)", RED_INK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "Serum urea – raised in UGIB (↑urea:creatinine ratio >100:1)", BLUE_INK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = gap(ly, 1.5*mm)
ly = draw_sub_heading(c, invx, ly, "Imaging:")
ly = draw_bullet(c, invx + 2*mm, ly, "CXR – perforation (free air under diaphragm)", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "Abdominal USS – liver, spleen, portal HTN", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "CT angiography – if bleeding >0.5 ml/min", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = gap(ly, 1.5*mm)
ly = draw_sub_heading(c, invx, ly, "Endoscopy (OGD – Gold Standard):")
ly = draw_bullet(c, invx + 2*mm, ly, "Diagnostic AND therapeutic within 24 hrs", RED_INK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "PU bleeding → clipping / adrenaline injection / APC", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, invx + 2*mm, ly, "Varices → banding (EVL) or sclerotherapy", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
# ── RIGHT COLUMN ─────────────────────────────────────────────────────────────
ry = draw_heading(c, RIGHT_X, ry, "Rockall Score", RED_INK, FS_SUB)
ry = gap(ry, 1*mm)
c.setFont("Helvetica", FS_SMALL - 0.5)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X, ry, "Pre-endoscopy (clinical) + Post-endoscopy")
ry -= LINE_H * 1.2
# table header
cols = ["Variable", "0", "1", "2", "3"]
widths = [22*mm, 8*mm, 8*mm, 10*mm, 8*mm]
ry = draw_table_row(c, RIGHT_X, ry, cols, widths, FS_TINY, header=True, bg=LIGHT_BLUE)
rows = [
["Age", "<60", "60-79", "≥80", ""],
["Shock", "None", "Tachy-cardia", "Hypo-tension", ""],
["Co-morbidity", "None", "", "CCF,IHD,any major", "CRF, liver failure, Ca"],
["Diagnosis", "Mallory-Weiss/no lesion", "", "All other", "GI malignancy"],
["Major SRH*", "None/dark spot", "", "Blood/visible vessel/clot", ""],
]
for r in rows:
ry = draw_table_row(c, RIGHT_X, ry, r, widths, FS_TINY)
ry = gap(ry, 1*mm)
c.setFont("Helvetica", FS_TINY)
c.setFillColor(RED_INK)
c.drawString(RIGHT_X, ry, "*SRH = Stigmata of recent haemorrhage")
ry -= LINE_H
c.setFont("Helvetica-Bold", FS_TINY)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X, ry, "Score ≥5 → High risk (mortality ~35%)")
ry -= LINE_H
c.drawString(RIGHT_X, ry, "Score 0 → Low risk (mortality ~0%)")
ry -= LINE_H * 2
ry = draw_heading(c, RIGHT_X, ry, "Glasgow-Blatchford Score", RED_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
items_gb = [
"Blood urea ≥6.5 mmol/L",
"Hb <13 g/dL (M) / <12 g/dL (F)",
"SBP <110 mmHg",
"Pulse ≥100 bpm",
"Melaena on presentation",
"Syncope",
"Hepatic disease / Cardiac failure"
]
for item in items_gb:
c.drawString(RIGHT_X + 2*mm, ry, "→ " + item)
ry -= LINE_H
ry = gap(ry, 1*mm)
c.setFont("Helvetica-Bold", FS_SMALL)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X, ry, "Score 0 = safe discharge (outpatient)")
ry -= LINE_H
c.drawString(RIGHT_X, ry, "Score ≥1 = needs admission")
ry -= LINE_H * 2
ry = highlight_box(c, RIGHT_X, ry, "Urea:Creatinine ratio >100 strongly suggests UGIB source", LIGHT_YELL, RED_INK, FS_TINY, RIGHT_W)
ry = gap(ry, 4*mm)
ry = draw_heading(c, RIGHT_X, ry, "Forrest Classification", BLUE_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
fc_rows = [
("Ia", "Spurting arterial bleed", "90%"),
("Ib", "Oozing bleed", "55%"),
("IIa", "Visible vessel (non-bleed)", "43%"),
("IIb", "Adherent clot", "22%"),
("IIc", "Haematin-covered flat spot", "10%"),
("III", "Clean-based ulcer", "5%"),
]
for fc, desc, risk in fc_rows:
c.setFont("Helvetica-Bold", FS_TINY)
c.setFillColor(RED_INK)
c.drawString(RIGHT_X, ry, fc)
c.setFont("Helvetica", FS_TINY)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 8*mm, ry, desc)
c.setFont("Helvetica-Bold", FS_TINY)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X + RIGHT_W - 8*mm, ry, risk)
ry -= LINE_H
c.showPage()
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 3 - MANAGEMENT: Resuscitation + Definitive Tx
# ══════════════════════════════════════════════════════════════════════════════
page = 3
new_page(c, "Upper GI Bleeding – Management", "", page)
ly = H - MARGIN_TOP - 2*mm
ry = H - MARGIN_TOP - 2*mm
# ── LEFT COLUMN ──────────────────────────────────────────────────────────────
ly = draw_heading(c, LEFT_X, ly, "Initial Resuscitation (ABC)", RED_INK)
ly = gap(ly, 1*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 1, "HOSPITALIZE – admit to HDU/ICU", RED_INK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 2, "Establish IV access × 2 (large bore 14-16G)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_bullet(c, LEFT_X + 6*mm, ly, "IV fluids → crystalloid (0.9% NaCl / Hartmann's) to maintain circulatory status", INK_BLACK, FS_BODY, LEFT_W - 10*mm, arrow=True)
ly = draw_item(c, LEFT_X + 2*mm, ly, 3, "Airway protection – consider intubation if GCS low / massive haematemesis", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 4, "Oxygen supplementation (target SpO₂ >95%)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 5, "Blood transfusion if Hb <7 g/dL (or <8 if IHD)", RED_INK, FS_BODY, LEFT_W - 4*mm)
ly = draw_bullet(c, LEFT_X + 6*mm, ly, "Target Hb 7-8 g/dL (restrictive strategy)", INK_BLACK, FS_BODY, LEFT_W - 10*mm, arrow=True)
ly = draw_item(c, LEFT_X + 2*mm, ly, 6, "Correct coagulopathy – FFP, platelets, Vit K if on warfarin", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 7, "Catheterize for urine output monitoring (target >0.5 ml/kg/hr)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 8, "NG tube insertion – if altered consciousness, assess bleed", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 3*mm)
ly = draw_heading(c, LEFT_X, ly, "Pharmacological Treatment", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "A. Peptic Ulcer Bleeding:", BLUE_INK)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "IV PPI (Proton Pump Inhibitor) – e.g. Omeprazole 80 mg bolus then 8 mg/hr infusion × 72 hr", RED_INK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Oral PPI after 72 hr → OD for 4-8 weeks", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "H. pylori eradication therapy (see PUD page)", BLUE_INK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = gap(ly, 2*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "B. Variceal Bleeding:", RED_INK)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "IV Terlipressin (vasopressin analogue) – vasoconstrictor; 2 mg IV 6-hourly × 72 hr", RED_INK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "OR Somatostatin / Octreotide infusion", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "IV Antibiotics (ciprofloxacin/norfloxacin) – prevent SBP in cirrhosis", BLUE_INK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Non-selective beta-blocker (propranolol) for secondary prophylaxis", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = gap(ly, 3*mm)
ly = draw_heading(c, LEFT_X, ly, "Post-Stabilisation – Endoscopy (OGD)", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 1, "Urgent OGD within 24 hrs (within 12 hr if high-risk)", RED_INK, FS_BODY, LEFT_W - 4*mm)
ly = draw_sub_heading(c, LEFT_X + 4*mm, ly, "For PU Bleeding:")
ly = draw_bullet(c, LEFT_X + 6*mm, ly, "Adrenaline (epinephrine) injection 1:10000", INK_BLACK, FS_BODY, LEFT_W - 10*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 6*mm, ly, "Mechanical clipping (hemo-clips)", INK_BLACK, FS_BODY, LEFT_W - 10*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 6*mm, ly, "Thermal methods: APC, heater probe", INK_BLACK, FS_BODY, LEFT_W - 10*mm, arrow=True)
ly = draw_sub_heading(c, LEFT_X + 4*mm, ly, "For Variceal Bleeding:")
ly = draw_bullet(c, LEFT_X + 6*mm, ly, "EVL (Endoscopic Variceal Ligation/Banding) – preferred", RED_INK, FS_BODY, LEFT_W - 10*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 6*mm, ly, "Sclerotherapy (ethanolamine injection)", INK_BLACK, FS_BODY, LEFT_W - 10*mm, arrow=True)
# ── RIGHT COLUMN ─────────────────────────────────────────────────────────────
ry = draw_heading(c, RIGHT_X, ry, "Balloon Tamponade", RED_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
ry = draw_text(c, RIGHT_X, ry, "Sengstaken-Blakemore tube (3-lumen) or Minnesota tube (4-lumen) – temporary bridge to definitive Rx", INK_BLACK, FS_SMALL, RIGHT_W)
c.setFont("Helvetica-Bold", FS_SMALL)
c.setFillColor(RED_INK)
c.drawString(RIGHT_X, ry, "⚠ Max 24 hrs use → risk of necrosis")
ry -= LINE_H * 2
ry = draw_heading(c, RIGHT_X, ry, "Surgical Procedures", BLUE_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
for t in ["Oversewing of bleeding vessel (PU bleeding)", "Partial gastrectomy (refractory ulcer)", "Oesophageal transection (varices)", "Sugiura procedure (devascularisation)", "Portosystemic shunts (portal HTN)"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 1*mm, ry, "→ " + t)
ry -= LINE_H
ry = gap(ry, 4*mm)
ry = draw_heading(c, RIGHT_X, ry, "TIPSS", BLUE_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
ry = draw_text(c, RIGHT_X, ry, "Transjugular Intrahepatic Portosystemic Shunt – reduces portal HTN; used when endoscopic Rx fails", INK_BLACK, FS_SMALL, RIGHT_W)
ry = gap(ry, 4*mm)
ry = highlight_box(c, RIGHT_X, ry, "SHOCK INDICES: HR/SBP >1 = class II shock; >1.5 = class III shock", LIGHT_YELL, RED_INK, FS_TINY, RIGHT_W)
ry = gap(ry, 4*mm)
ry = draw_heading(c, RIGHT_X, ry, "Blood Transfusion Triggers", RED_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
for t, v in [("Hb <7 g/dL", "transfuse"), ("Hb <8 g/dL", "transfuse if IHD/ACS"), ("SBP <90 mmHg", "urgent resus"), ("PR >100 bpm", "tachycardia – act")]:
c.setFont("Helvetica-Bold", FS_TINY)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X, ry, t)
c.setFont("Helvetica", FS_TINY)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 22*mm, ry, "→ " + v)
ry -= LINE_H
ry = gap(ry, 4*mm)
ry = draw_heading(c, RIGHT_X, ry, "Risk Categories", BLUE_INK, FS_SUB)
ry = gap(ry, 1*mm)
c.setFont("Helvetica-Bold", FS_SMALL)
c.setFillColor(RED_INK)
c.drawString(RIGHT_X, ry, "High Risk")
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 20*mm, ry, "→ Poor outcome; urgent intervention")
ry -= LINE_H * 1.2
c.setFont("Helvetica-Bold", FS_SMALL)
c.setFillColor(GREEN_INK)
c.drawString(RIGHT_X, ry, "Low Risk")
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 20*mm, ry, "→ Good outcome; outpatient possible")
ry -= LINE_H * 2
c.showPage()
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 4 - PEPTIC ULCER DISEASE
# ══════════════════════════════════════════════════════════════════════════════
page = 4
new_page(c, "Peptic Ulcer Disease (PUD)", "", page)
ly = H - MARGIN_TOP - 2*mm
ry = H - MARGIN_TOP - 2*mm
# ── LEFT COLUMN ──────────────────────────────────────────────────────────────
ly = draw_heading(c, LEFT_X, ly, "Definition", BLUE_INK)
ly = draw_text(c, LEFT_X + 2*mm, ly, "Breach in GI mucosa extending through muscularis mucosae, caused by acid-pepsin digestion. Most common in gastric antrum and first part of duodenum.", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 2*mm)
ly = draw_heading(c, LEFT_X, ly, "Aetiology / Causes", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 1, "H. pylori infection – most common (90% DU, 70% GU)", RED_INK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 2, "NSAIDs / Aspirin – inhibit COX-1 → ↓prostaglandins → ↓mucus/bicarb", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 3, "Zollinger-Ellison syndrome – gastrinoma → ↑↑ acid secretion", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 4, "Stress ulcers (Curling's ulcer – burns, Cushing's ulcer – head injury)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 5, "Smoking, alcohol, steroids, hypercalcaemia, CRF, COPD", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 2*mm)
ly = draw_heading(c, LEFT_X, ly, "Pathophysiology", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_text(c, LEFT_X + 2*mm, ly, "Imbalance between aggressive and defensive factors:", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 1*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Aggressive Factors ↑:", RED_INK)
for a in ["HCl acid (↑parietal cell mass)", "Pepsin", "H. pylori (urease, cytotoxins)", "Bile reflux", "NSAIDs"]:
c.setFont("Helvetica", FS_BODY)
c.setFillColor(INK_BLACK)
c.drawString(LEFT_X + 6*mm, ly, "→ " + a)
ly -= LINE_H
ly = gap(ly, 1*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Defensive Factors ↓:", GREEN_INK)
for d in ["Mucus layer", "Bicarbonate secretion", "Mucosal blood flow", "Prostaglandins (PGE₂)", "Cell renewal"]:
c.setFont("Helvetica", FS_BODY)
c.setFillColor(INK_BLACK)
c.drawString(LEFT_X + 6*mm, ly, "→ " + d)
ly -= LINE_H
ly = gap(ly, 2*mm)
ly = draw_heading(c, LEFT_X, ly, "Clinical Features", BLUE_INK)
ly = gap(ly, 1*mm)
# DU vs GU table
c.setFont("Helvetica-Bold", FS_SMALL)
c.setFillColor(BLUE_INK)
c.drawString(LEFT_X + 2*mm, ly, "Feature")
c.drawString(LEFT_X + 38*mm, ly, "Duodenal Ulcer")
c.drawString(LEFT_X + 70*mm, ly, "Gastric Ulcer")
ly -= LINE_H * 0.8
c.setStrokeColor(BORDER_GRAY)
c.setLineWidth(0.6)
c.line(LEFT_X, ly, LEFT_X + LEFT_W - 2*mm, ly)
ly -= LINE_H * 0.5
du_gu_data = [
("Location", "1st part duodenum", "Antrum / lesser curve"),
("Age", "35-45 yrs", "55-65 yrs"),
("Pain timing", "2-3 hrs after food", "During/after food"),
("Relief", "Food / antacids", "No relief / worsened"),
("Nocturnal pain", "Yes (common)", "Less common"),
("Weight", "Normal/↑", "↓ (malignancy risk)"),
("H.pylori", "90%", "70%"),
("Malignant risk", "Very rare", "5-10% (must biopsy)"),
]
for row in du_gu_data:
c.setFont("Helvetica-Bold", FS_SMALL - 0.5)
c.setFillColor(INK_BLACK)
c.drawString(LEFT_X + 2*mm, ly, row[0])
c.setFont("Helvetica", FS_SMALL - 0.5)
c.drawString(LEFT_X + 38*mm, ly, row[1])
c.drawString(LEFT_X + 70*mm, ly, row[2])
ly -= LINE_H * 0.95
# ── RIGHT COLUMN ─────────────────────────────────────────────────────────────
ry = draw_heading(c, RIGHT_X, ry, "Complications", RED_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
comp_data = [
("Bleeding", "Most common complication"),
("Perforation", "Board-like rigid abdomen"),
("Penetration", "Into pancreas → back pain"),
("Obstruction", "GOO – succussion splash"),
("Malignancy", "Gastric ulcer only"),
]
for comp, desc in comp_data:
c.setFont("Helvetica-Bold", FS_SMALL)
c.setFillColor(RED_INK)
c.drawString(RIGHT_X, ry, comp + ":")
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 22*mm, ry, desc)
ry -= LINE_H
ry = gap(ry, 4*mm)
ry = draw_heading(c, RIGHT_X, ry, "Investigations", BLUE_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
for inv in ["OGD (gold std) + biopsy × 6 (GU)", "H. pylori: CLO test, urea breath test, stool Ag", "Serum gastrin (if ZES suspected)", "Ba meal (if OGD refused)", "CT abdomen (perforation)", "CXR – free air sub-diaphragm"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 1*mm, ry, "→ " + inv)
ry -= LINE_H
ry = gap(ry, 4*mm)
ry = draw_heading(c, RIGHT_X, ry, "H. pylori Eradication", RED_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
c.setFont("Helvetica-Bold", FS_SMALL)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X, ry, "Triple Therapy × 7 days:")
ry -= LINE_H
for drug in ["PPI (Omeprazole 20 mg BD)", "+ Amoxicillin 1 g BD", "+ Clarithromycin 500 mg BD"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 2*mm, ry, drug)
ry -= LINE_H
ry -= 1*mm
c.setFont("Helvetica-Bold", FS_SMALL)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X, ry, "Quadruple Therapy (if resistance):")
ry -= LINE_H
for drug in ["PPI + Bismuth + Metronidazole + Tetracycline"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 2*mm, ry, drug)
ry -= LINE_H
ry = gap(ry, 4*mm)
ry = highlight_box(c, RIGHT_X, ry, "Johnson Classification of Gastric Ulcer: Type I (lesser curve) – ↓ acid. Type II (antrum + DU) – ↑ acid. Type III (prepyloric) – ↑ acid. Type IV (high lesser curve). Type V (NSAID-induced)", LIGHT_BLUE, BLUE_INK, FS_TINY, RIGHT_W)
c.showPage()
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 5 - PUD TREATMENT + OESOPHAGEAL VARICES
# ══════════════════════════════════════════════════════════════════════════════
page = 5
new_page(c, "PUD Treatment & Oesophageal Varices", "", page)
ly = H - MARGIN_TOP - 2*mm
ry = H - MARGIN_TOP - 2*mm
# ── LEFT COLUMN ──────────────────────────────────────────────────────────────
ly = draw_heading(c, LEFT_X, ly, "Medical Treatment of PUD", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 1, "Lifestyle: avoid NSAIDs, aspirin, smoking, alcohol, coffee", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 2, "PPI therapy (Omeprazole 20-40 mg OD × 4-8 weeks)", RED_INK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 3, "H₂ receptor antagonist (Ranitidine 150 mg BD) – 2nd line", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 4, "Antacids (symptomatic relief only)", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 5, "Sucralfate – mucosal protective agent", INK_BLACK, FS_BODY, LEFT_W - 4*mm)
ly = draw_item(c, LEFT_X + 2*mm, ly, 6, "H. pylori eradication (triple therapy)", BLUE_INK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 2*mm)
ly = draw_heading(c, LEFT_X, ly, "Surgical Treatment of PUD", BLUE_INK)
ly = gap(ly, 0.5*mm)
ly = draw_text(c, LEFT_X + 2*mm, ly, "Indications: failed medical Rx, perforation, obstruction, malignancy, massive/recurrent bleeding", RED_INK, FS_BODY, LEFT_W - 4*mm)
ly = gap(ly, 1.5*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Elective Surgery:", BLUE_INK)
ly = draw_item(c, LEFT_X + 4*mm, ly, 1, "Highly Selective Vagotomy (HSV/parietal cell vagotomy) – gold std for DU; preserves antral innervation", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 2, "Truncal vagotomy + drainage (pyloroplasty / gastrojejunostomy)", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 3, "Billroth I (gastroduodenostomy) – GU preferred", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 4, "Billroth II (gastrojejunostomy)", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = gap(ly, 2*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Emergency Surgery:", RED_INK)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Perforation → Graham's patch repair (omental patch)", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Bleeding → undersewing of vessel ± vagotomy", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = draw_bullet(c, LEFT_X + 4*mm, ly, "Obstruction → balloon dilation or gastrojejunostomy", INK_BLACK, FS_BODY, LEFT_W - 8*mm, arrow=True)
ly = gap(ly, 3*mm)
ly = draw_heading(c, LEFT_X, ly, "Oesophageal Varices", RED_INK)
ly = gap(ly, 1*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Definition:")
ly = draw_text(c, LEFT_X + 4*mm, ly, "Dilated sub-mucosal veins in lower oesophagus due to portal hypertension (portal pressure >12 mmHg)", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = gap(ly, 1.5*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Causes of Portal HTN:", BLUE_INK)
ly = draw_item(c, LEFT_X + 4*mm, ly, 1, "Prehepatic: portal vein thrombosis", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 2, "Intrahepatic: cirrhosis (most common – 80%), schistosomiasis", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 3, "Posthepatic: Budd-Chiari syndrome, RHF, constrictive pericarditis", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = gap(ly, 2*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Portosystemic Anastomoses (sites of varices):", BLUE_INK)
sites = [
("Lower oesophagus", "Oesophageal varices (most dangerous)"),
("Umbilicus", "Caput medusae"),
("Rectum", "Haemorrhoids"),
("Retroperitoneum", "Retroperitoneal varices"),
]
for site, desc in sites:
c.setFont("Helvetica-Bold", FS_BODY)
c.setFillColor(BLUE_INK)
c.drawString(LEFT_X + 4*mm, ly, site + ":")
c.setFont("Helvetica", FS_BODY)
c.setFillColor(INK_BLACK)
c.drawString(LEFT_X + 4*mm + c.stringWidth(site + ":", "Helvetica-Bold", FS_BODY) + 2*mm, ly, desc)
ly -= LINE_H
# ── RIGHT COLUMN ─────────────────────────────────────────────────────────────
ry = draw_heading(c, RIGHT_X, ry, "Postgastrectomy Syndromes", RED_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
syndromes = [
("Dumping (Early)", "30 min post-meal; vasomotor + GI symptoms"),
("Dumping (Late)", "2-3 hr post-meal; hypoglycaemia"),
("Afferent loop synd.", "Bilious vomiting; post-Billroth II"),
("Alkaline reflux gastritis", "Bile into stomach; epigastric pain"),
("Postvagotomy diarrhoea", "Post truncal vagotomy"),
("Nutritional def.", "Fe, B12, folate deficiency"),
]
for syn, desc in syndromes:
c.setFont("Helvetica-Bold", FS_TINY)
c.setFillColor(RED_INK)
c.drawString(RIGHT_X, ry, syn)
c.setFont("Helvetica", FS_TINY)
c.setFillColor(INK_BLACK)
ry2 = ry - LINE_H * 0.8
c.drawString(RIGHT_X + 2*mm, ry2, desc)
ry = ry2 - LINE_H * 0.6
ry = gap(ry, 4*mm)
ry = draw_heading(c, RIGHT_X, ry, "Child-Pugh Score (CLD severity)", BLUE_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
cp_headers = ["Parameter", "1 pt", "2 pts", "3 pts"]
cp_widths = [22*mm, 10*mm, 12*mm, 12*mm]
ry = draw_table_row(c, RIGHT_X, ry, cp_headers, cp_widths, FS_TINY, header=True, bg=LIGHT_BLUE)
cp_rows = [
["Bilirubin (µmol/L)", "<34", "34-51", ">51"],
["Albumin (g/L)", ">35", "28-35", "<28"],
["PT prolongation (s)", "<4", "4-6", ">6"],
["Ascites", "None", "Mild", "Moderate"],
["Encephalopathy", "None", "Gr 1-2", "Gr 3-4"],
]
for r in cp_rows:
ry = draw_table_row(c, RIGHT_X, ry, r, cp_widths, FS_TINY)
ry -= 1*mm
c.setFont("Helvetica-Bold", FS_TINY)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X, ry, "Class A: 5-6 pts | Class B: 7-9 pts | Class C: 10-15 pts")
ry = gap(ry - LINE_H, 3*mm)
ry = draw_heading(c, RIGHT_X, ry, "Varices – Primary Prophylaxis", GREEN_INK, FS_SUB)
ry = gap(ry, 0.5*mm)
for t in ["Non-selective BB (propranolol/nadolol) – reduce portal pressure", "Nitrates (ISMN) if BB contraindicated", "EVL – for medium/large varices"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 1*mm, ry, "→ " + t)
ry -= LINE_H
ry = gap(ry, 3*mm)
ry = highlight_box(c, RIGHT_X, ry, "MELD Score: 3.78×ln(bilirubin) + 11.2×ln(INR) + 9.57×ln(creatinine) + 6.43 → predicts 90-day mortality in CLD", LIGHT_BLUE, BLUE_INK, FS_TINY, RIGHT_W)
c.showPage()
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 6 - GORD + GASTRIC CA
# ══════════════════════════════════════════════════════════════════════════════
page = 6
new_page(c, "GORD & Gastric Carcinoma", "", page)
ly = H - MARGIN_TOP - 2*mm
ry = H - MARGIN_TOP - 2*mm
# ── LEFT COLUMN ──────────────────────────────────────────────────────────────
ly = draw_heading(c, LEFT_X, ly, "GORD (Gastro-Oesophageal Reflux Disease)", BLUE_INK)
ly = gap(ly, 1*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Definition:", INK_BLACK)
ly = draw_text(c, LEFT_X + 4*mm, ly, "Reflux of gastric contents into oesophagus causing symptoms/complications. LOS pressure <6 mmHg (normal 12-30 mmHg)", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = gap(ly, 1.5*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Predisposing Factors:", BLUE_INK)
factors = ["↓ LOS tone (most important)", "Hiatus hernia", "Obesity / pregnancy", "Smoking, alcohol, fatty diet", "Delayed gastric emptying", "Drugs: CCBs, nitrates, anticholinergics"]
for f in factors:
c.setFont("Helvetica", FS_BODY)
c.setFillColor(INK_BLACK)
c.drawString(LEFT_X + 4*mm, ly, "→ " + f)
ly -= LINE_H
ly = gap(ly, 2*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Symptoms:", RED_INK)
ly = draw_item(c, LEFT_X + 4*mm, ly, 1, "Heartburn – retrosternal burning (main symptom)", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 2, "Acid regurgitation – sour taste", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 3, "Dysphagia (if stricture develops)", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 4, "Atypical: cough, hoarseness, asthma, dental erosion", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = gap(ly, 2*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Complications:", RED_INK)
comps = [
("Oesophagitis", "Grade A-D (Los Angeles)"),
("Barrett's oesophagus", "Columnar metaplasia → 0.5%/yr → adenoCA"),
("Peptic stricture", "Progressive dysphagia"),
("Oesophageal adenoCA", "Most serious complication"),
]
for comp, note in comps:
c.setFont("Helvetica-Bold", FS_BODY)
c.setFillColor(RED_INK)
c.drawString(LEFT_X + 4*mm, ly, comp + ":")
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
ly -= LINE_H * 0.7
c.drawString(LEFT_X + 8*mm, ly, note)
ly -= LINE_H
ly = gap(ly, 2*mm)
ly = draw_sub_heading(c, LEFT_X + 2*mm, ly, "Treatment:", BLUE_INK)
ly = draw_item(c, LEFT_X + 4*mm, ly, 1, "Lifestyle: weight loss, elevate HOB, small meals, avoid triggers", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 2, "PPI (Omeprazole 20 mg OD) – mainstay of treatment", RED_INK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 3, "H₂ blockers (Ranitidine) – if PPI not tolerated", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 4, "Antacids + Alginates (Gaviscon) – symptom relief", INK_BLACK, FS_BODY, LEFT_W - 8*mm)
ly = draw_item(c, LEFT_X + 4*mm, ly, 5, "Surgery: Laparoscopic Nissen Fundoplication – definitive", BLUE_INK, FS_BODY, LEFT_W - 8*mm)
# ── RIGHT COLUMN ─────────────────────────────────────────────────────────────
ry = draw_heading(c, RIGHT_X, ry, "Gastric Carcinoma", RED_INK, FS_SUB + 1)
ry = gap(ry, 0.5*mm)
ry = draw_sub_heading(c, RIGHT_X, ry, "Risk Factors:", RED_INK)
for rf in ["H. pylori infection (6× risk)", "Diet: smoked/pickled/salted food, ↓Vit C", "Smoking", "Blood group A", "Pernicious anaemia / atrophic gastritis", "Previous gastric surgery (Billroth II)", "Family history / Lynch syndrome"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 1*mm, ry, "→ " + rf)
ry -= LINE_H
ry = gap(ry, 3*mm)
ry = draw_sub_heading(c, RIGHT_X, ry, "Symptoms (often late):", RED_INK)
for s in ["Weight loss (most common)", "Anorexia", "Epigastric pain", "Dysphagia (if GOJ/fundus)", "Vomiting (if pylorus)", "Haematemesis/melaena"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 1*mm, ry, "→ " + s)
ry -= LINE_H
ry = gap(ry, 3*mm)
ry = draw_sub_heading(c, RIGHT_X, ry, "Signs:", BLUE_INK)
signs = [
("Virchow's node", "L supraclavicular LN (Troisier's sign)"),
("Sister Mary Joseph nodule", "Umbilical mets"),
("Blumer's shelf", "Rectal shelf (peritoneal mets)"),
("Krukenberg tumour", "Ovarian mets"),
("Irish node", "L axillary LN"),
]
for sign, desc in signs:
c.setFont("Helvetica-Bold", FS_TINY)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X, ry, sign)
c.setFont("Helvetica", FS_TINY)
c.setFillColor(INK_BLACK)
ry -= LINE_H * 0.8
c.drawString(RIGHT_X + 2*mm, ry, desc)
ry -= LINE_H * 0.7
ry = gap(ry, 2*mm)
ry = draw_sub_heading(c, RIGHT_X, ry, "Surgery:", BLUE_INK)
for s in ["Total gastrectomy (proximal/body tumour)", "Subtotal gastrectomy (distal tumour)", "D2 lymphadenectomy (standard)", "Palliative resection / bypass"]:
c.setFont("Helvetica", FS_SMALL)
c.setFillColor(INK_BLACK)
c.drawString(RIGHT_X + 1*mm, ry, "→ " + s)
ry -= LINE_H
ry = gap(ry, 2*mm)
ry = highlight_box(c, RIGHT_X, ry, "ALARM symptoms (urgent OGD): Anaemia, Loss of weight, Anorexia, Recent onset dyspepsia >55yr, Malaena/haematemesis, Swallowing difficulty", LIGHT_YELL, RED_INK, FS_TINY, RIGHT_W)
c.showPage()
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 7 - QUICK REVISION: SUMMARY & KEY FACTS
# ══════════════════════════════════════════════════════════════════════════════
page = 7
new_page(c, "Upper GI – Quick Revision Summary", "", page)
ly = H - MARGIN_TOP - 2*mm
ry = H - MARGIN_TOP - 2*mm
# ── LEFT COLUMN ──────────────────────────────────────────────────────────────
ly = draw_heading(c, LEFT_X, ly, "High-Yield Facts for MBBS", RED_INK)
ly = gap(ly, 1*mm)
key_facts = [
("Most common cause of UGIB", "Peptic ulcer disease"),
("Most common cause of variceal bleed", "Liver cirrhosis → portal HTN"),
("Gold standard Ix for UGIB", "OGD (Oesophago-gastro-duodenoscopy)"),
("Treatment of variceal bleed", "Terlipressin + EVL banding"),
("Treatment of PU bleed", "IV PPI + endoscopic clipping"),
("H. pylori eradication", "Triple therapy × 7 days"),
("Coffee-ground vomit means", "Slow UGI bleed (oxidised Hb)"),
("Rockall score ≥5", "High mortality risk – intervene urgently"),
("Glasgow-Blatchford score 0", "Safe for outpatient management"),
("Barrett's oesophagus", "Columnar metaplasia → adenoCA risk"),
("Nissen fundoplication", "Surgical Rx for GORD"),
("Virchow's node", "Gastric Ca → L supraclavicular LN"),
("ZES (Zollinger-Ellison)", "Gastrinoma → ↑↑ acid → multiple ulcers"),
("Type III GU (prepyloric)", "Behaves like DU → ↑ acid"),
("TIPSS indication", "Refractory variceal bleed, ascites"),
]
for fact, ans in key_facts:
c.setFont("Helvetica-Bold", FS_BODY)
c.setFillColor(BLUE_INK)
c.drawString(LEFT_X + 2*mm, ly, "Q: " + fact)
ly -= LINE_H * 0.85
c.setFont("Helvetica", FS_BODY)
c.setFillColor(RED_INK)
c.drawString(LEFT_X + 4*mm, ly, "→ " + ans)
ly -= LINE_H * 1.05
ly = gap(ly, 3*mm)
ly = draw_heading(c, LEFT_X, ly, "Mnemonics", GREEN_INK)
ly = gap(ly, 1*mm)
mnemonics = [
("UGIB causes", "GEV-MDC-A", "Gastritis, Erosions, Varices, Mallory-Weiss, Duodenal ulcer, Ca, Aorto-duodenal fistula"),
("Complications PUD", "BPOM", "Bleeding, Perforation, Obstruction, Malignancy"),
("CLD stigmata", "JALAPCC", "Jaundice, Asterixis, Leuconychia, Ascites, Palmar erythema, Clubbing, Caput medusae, spider naevi"),
("ALARM symptoms", "ALARMS", "Anaemia, Loss of wt, Anorexia, Rectal bleed/haematemesis, Malaena, Swallowing difficulty"),
]
for title, short, expansion in mnemonics:
c.setFont("Helvetica-Bold", FS_BODY)
c.setFillColor(GREEN_INK)
c.drawString(LEFT_X + 2*mm, ly, title + ": ")
c.setFont("Helvetica-Bold", FS_BODY)
c.setFillColor(RED_INK)
c.drawString(LEFT_X + 2*mm + c.stringWidth(title + ": ", "Helvetica-Bold", FS_BODY), ly, short)
ly -= LINE_H * 0.9
c.setFont("Helvetica", FS_SMALL - 0.5)
c.setFillColor(INK_BLACK)
# wrap expansion
words = expansion.split()
cur = ""
exp_lines = []
for w in words:
test = (cur + " " + w).strip()
if c.stringWidth(test, "Helvetica", FS_SMALL - 0.5) <= LEFT_W - 8*mm:
cur = test
else:
exp_lines.append(cur)
cur = w
if cur:
exp_lines.append(cur)
for line in exp_lines:
c.drawString(LEFT_X + 6*mm, ly, line)
ly -= LINE_H * 0.85
ly -= 1*mm
# ── RIGHT COLUMN ─────────────────────────────────────────────────────────────
ry = draw_heading(c, RIGHT_X, ry, "Drugs Summary", BLUE_INK, FS_SUB)
ry = gap(ry, 1*mm)
drug_data = [
("Omeprazole", "PPI; UGI bleed, PUD, GORD; 20-40 mg OD"),
("Terlipressin", "Vasopressin analogue; variceal bleed; 2 mg IV 6-hrly"),
("Propranolol", "Non-selective BB; prophylaxis varices; reduce portal P"),
("Ranitidine", "H₂ blocker; PUD; 150 mg BD"),
("Sucralfate", "Mucosal protectant; binds to ulcer base; 1 g QID"),
("Misoprostol", "PGE₁ analogue; NSAID-induced ulcer prophylaxis"),
("Clarithromycin", "Antibiotic; part of triple therapy for H.pylori"),
("Amoxicillin", "Antibiotic; triple therapy; 1 g BD"),
("Metronidazole", "Quadruple therapy (if clarithromycin-resistant)"),
("Bismuth", "Quadruple therapy; also antibacterial vs H.pylori"),
("Norfloxacin", "SBP prophylaxis in cirrhosis with variceal bleed"),
("Octreotide", "Somatostatin analogue; ↓ splanchnic blood flow"),
("FFP", "Fresh frozen plasma; correct coagulopathy in UGIB"),
]
for drug, note in drug_data:
c.setFont("Helvetica-Bold", FS_TINY)
c.setFillColor(BLUE_INK)
c.drawString(RIGHT_X, ry, drug)
c.setFont("Helvetica", FS_TINY)
c.setFillColor(INK_BLACK)
tw = c.stringWidth(drug, "Helvetica-Bold", FS_TINY)
# wrap note
words = note.split()
cur = ""
nlines = []
avail = RIGHT_W - tw - 3*mm
for w in words:
test = (cur + " " + w).strip()
if c.stringWidth(test, "Helvetica", FS_TINY) <= avail:
cur = test
else:
nlines.append(cur)
cur = w
avail = RIGHT_W
if cur:
nlines.append(cur)
c.drawString(RIGHT_X + tw + 2*mm, ry, nlines[0] if nlines else "")
for nl in nlines[1:]:
ry -= LINE_H * 0.85
c.drawString(RIGHT_X + 2*mm, ry, nl)
ry -= LINE_H * 1.05
ry = gap(ry, 3*mm)
ry = highlight_box(c, RIGHT_X, ry, "REMEMBER: In cirrhosis + variceal bleed, always give prophylactic antibiotics (norfloxacin/ciprofloxacin) to prevent spontaneous bacterial peritonitis (SBP)", LIGHT_YELL, RED_INK, FS_TINY, RIGHT_W)
c.showPage()
# ── SAVE ─────────────────────────────────────────────────────────────────────
c.save()
print(f"PDF saved to {OUTPUT}")
print(f"Pages: 7")
cd /home/daytona/workspace/upper_gi_notes && python3 generate_notes.py
cd /home/daytona/workspace/upper_gi_notes && pdftoppm -r 120 Upper_GI_MBBS_Notes…
~/upper_gi_notes/generate_notes.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/upper_gi_notes/generate_notes.py'
cd /home/daytona/workspace/upper_gi_notes && python3 generate_notes.py && pdftop…
Upper GI MBBS Notes
PDF Document · PDF
| Page | Content |
|---|---|
| 1 | GI Bleeding - classification tree, causes (common + oesophageal/gastric/duodenal), clinical presentation, history taking |
| 2 | Assessment - examination (haemodynamic, CLD stigmata, systemic), investigations (bloods, imaging, OGD), Rockall Score + Glasgow-Blatchford Score, Forrest Classification |
| 3 | Management - resuscitation (8 steps), pharmacological Rx (PU bleed vs variceal), post-stabilisation endoscopy, surgical procedures, TIPSS, blood transfusion triggers |
| 4 | Peptic Ulcer Disease - definition, aetiology, pathophysiology (aggressive vs defensive factors), DU vs GU comparison table, complications, investigations, H. pylori eradication |
| 5 | PUD treatment (medical + surgical), oesophageal varices (portal HTN causes, portosystemic anastomoses), Child-Pugh score, MELD score |
| 6 | GORD (predisposing factors, symptoms, complications including Barrett's, treatment), Gastric Carcinoma (risk factors, ALARM signs, metastatic signs, surgery) |
| 7 | Quick revision - 15 high-yield Q&As, mnemonics (GEV-MDC-A, BPOM, JALAPCC, ALARMS), complete drugs summary |
No I want it like how u did dysphagia
**/*.pdf in /home/daytona/workspace
pdftoppm -r 120 /home/daytona/workspace/dysphagia-booklet/Dysphagia_Booklet_Note…
python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/…
pdftoppm -r 100 -f 1 -l 1 /home/daytona/workspace/dysphagia-booklet/Dysphagia_Bo…
python3 -c " from PIL import Image img = Image.open('/home/daytona/workspace/dys…
pdftoppm -r 100 -f 2 -l 2 /home/daytona/workspace/dysphagia-booklet/Dysphagia_Bo…
*.py in ~/dysphagia-booklet
build_booklet_v2.py in /home/daytona/workspace
~/dysphagia-booklet
Even the content...like what the history taking points osce pearls like something practical oriented and not again theoretical...something we can correlate clinically
~/upper_gi_notes/build_upper_gi_booklet.py
"""
Upper GI Bleeding — Final Year MBBS Booklet
Clinically oriented: History-taking, OSCE pearls, clinical correlations.
Same engine as Dysphagia v2 booklet.
"""
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.platypus import (
BaseDocTemplate, Frame, PageTemplate,
Paragraph, Spacer, Table, TableStyle,
HRFlowable, FrameBreak, PageBreak, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
# ── Page geometry ─────────────────────────────────────────────────────────────
PW, PH = landscape(A4)
MARGIN = 11 * mm
HEADER_H = 18 * mm
FOOTER_H = 9 * mm
GAP = 5 * mm
COL_W = (PW - 2*MARGIN - GAP) / 2
COL_H = PH - MARGIN - HEADER_H - FOOTER_H
LX = MARGIN
RX = MARGIN + COL_W + GAP
CY = FOOTER_H
# ── Colours ───────────────────────────────────────────────────────────────────
CREAM = colors.HexColor("#FDFBF2")
NAVY = colors.HexColor("#1B3A6B")
BLUE = colors.HexColor("#2E6DA4")
LBLUE = colors.HexColor("#D6E9F8")
GOLD = colors.HexColor("#C8960C")
AMBER = colors.HexColor("#FFF3CD")
PINK = colors.HexColor("#FCE4EC")
DKPINK = colors.HexColor("#AD1457")
GREEN = colors.HexColor("#E8F5E9")
DKGREEN = colors.HexColor("#2E7D32")
RED = colors.HexColor("#FFEBEE")
DKRED = colors.HexColor("#B71C1C")
MIDRED = colors.HexColor("#C62828")
DIVL = colors.HexColor("#BBBBBB")
WHITE = colors.white
ORANGE = colors.HexColor("#FFF8E1")
DKORANGE= colors.HexColor("#E65100")
TEAL = colors.HexColor("#E0F2F1")
DKTEAL = colors.HexColor("#00695C")
PURPLE = colors.HexColor("#F3E5F5")
DKPURP = colors.HexColor("#6A1B9A")
# ── Styles ────────────────────────────────────────────────────────────────────
_SS = getSampleStyleSheet()
def ps(name, **kw):
kw.setdefault("parent", _SS["Normal"])
return ParagraphStyle(name, **kw)
H1s = ps("H1s", fontSize=10, fontName="Helvetica-Bold", textColor=NAVY, spaceBefore=5, spaceAfter=2, leading=13)
H2s = ps("H2s", fontSize=9, fontName="Helvetica-Bold", textColor=BLUE, spaceBefore=4, spaceAfter=1, leading=12)
H3s = ps("H3s", fontSize=8.5, fontName="Helvetica-BoldOblique",textColor=NAVY, spaceBefore=3, spaceAfter=1, leading=11)
NOR = ps("NOR", fontSize=7.8, fontName="Helvetica", leading=11, spaceAfter=1, alignment=TA_JUSTIFY)
BUL = ps("BUL", fontSize=7.8, fontName="Helvetica", leading=11, spaceAfter=1, leftIndent=9, firstLineIndent=0)
BOD = ps("BOD", fontSize=7.5, fontName="Helvetica", leading=10.5, spaceAfter=1)
BHD = ps("BHD", fontSize=8, fontName="Helvetica-Bold", leading=11, textColor=WHITE, alignment=TA_CENTER)
SML = ps("SML", fontSize=7, fontName="Helvetica", leading=10, textColor=colors.HexColor("#555555"))
RED_TXT = ps("RED_TXT", fontSize=7.8, fontName="Helvetica-Bold", textColor=MIDRED, leading=11)
GRN_TXT = ps("GRN_TXT", fontSize=7.8, fontName="Helvetica-Bold", textColor=DKGREEN, leading=11)
CELL = ps("CELL", fontSize=7.5, fontName="Helvetica", leading=10.5, spaceAfter=0)
CELLB= ps("CELLB",fontSize=7.5, fontName="Helvetica-Bold", leading=10.5, spaceAfter=0, textColor=NAVY)
def P(t, s=NOR): return Paragraph(t, s)
def B(t): return Paragraph(f"<bullet>•</bullet> {t}", BUL)
def H1(t): return Paragraph(t, H1s)
def H2(t): return Paragraph(t, H2s)
def H3(t): return Paragraph(t, H3s)
def SP(n=2): return Spacer(1, n*mm)
def HR(): return HRFlowable(width="100%", thickness=0.5, color=DIVL, spaceAfter=2, spaceBefore=1)
def cbox(title, items, hbg=BLUE, bbg=LBLUE, w=None):
w = w or (COL_W - 3*mm)
data = [[Paragraph(title, BHD)]]
for it in items:
data.append([Paragraph(f"• {it}", BOD)])
ts = TableStyle([
("BACKGROUND", (0,0),(-1,0), hbg),
("BACKGROUND", (0,1),(-1,-1), bbg),
("BOX", (0,0),(-1,-1), 0.7, NAVY),
("GRID", (0,0),(-1,-1), 0.3, DIVL),
("TOPPADDING", (0,0),(-1,-1), 2),
("BOTTOMPADDING", (0,0),(-1,-1), 2),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
])
return Table(data, colWidths=[w], style=ts)
def hbox(text, bg=AMBER, border=GOLD, w=None):
w = w or (COL_W - 3*mm)
ts = TableStyle([
("BACKGROUND",(0,0),(-1,-1), bg),
("BOX",(0,0),(-1,-1), 1.0, border),
("TOPPADDING",(0,0),(-1,-1),3),
("BOTTOMPADDING",(0,0),(-1,-1),3),
("LEFTPADDING",(0,0),(-1,-1),6),
("RIGHTPADDING",(0,0),(-1,-1),6),
])
return Table([[Paragraph(text, BOD)]], colWidths=[w], style=ts)
def tbl(headers, rows, col_fracs, even_bg=LBLUE, odd_bg=WHITE, hbg=NAVY, w=None):
w = w or (COL_W - 3*mm)
col_w = [w * f for f in col_fracs]
data = [[Paragraph(h, BHD) for h in headers]]
for i, row in enumerate(rows):
bg = even_bg if i % 2 == 0 else odd_bg
data.append([Paragraph(str(c), CELL) for c in row])
ts = TableStyle([
("BACKGROUND",(0,0),(-1,0), hbg),
("BOX",(0,0),(-1,-1),0.7, NAVY),
("GRID",(0,0),(-1,-1),0.3, DIVL),
("TOPPADDING",(0,0),(-1,-1),2),
("BOTTOMPADDING",(0,0),(-1,-1),2),
("LEFTPADDING",(0,0),(-1,-1),4),
("RIGHTPADDING",(0,0),(-1,-1),4),
("ROWBACKGROUNDS",(0,1),(-1,-1),[even_bg, odd_bg]),
])
return Table(data, colWidths=col_w, style=ts)
def two_col_tbl(left_items, right_items, lhead, rhead, w=None):
"""Two-column table with separate header."""
w = w or (COL_W - 3*mm)
data = [[Paragraph(lhead, BHD), Paragraph(rhead, BHD)]]
maxr = max(len(left_items), len(right_items))
for i in range(maxr):
l = Paragraph(f"• {left_items[i]}", BOD) if i < len(left_items) else Paragraph("", BOD)
r = Paragraph(f"• {right_items[i]}", BOD) if i < len(right_items) else Paragraph("", BOD)
data.append([l, r])
ts = TableStyle([
("BACKGROUND",(0,0),(-1,0), NAVY),
("BACKGROUND",(0,1),(-1,-1), LBLUE),
("BOX",(0,0),(-1,-1),0.7, NAVY),
("GRID",(0,0),(-1,-1),0.3, DIVL),
("TOPPADDING",(0,0),(-1,-1),2),
("BOTTOMPADDING",(0,0),(-1,-1),2),
("LEFTPADDING",(0,0),(-1,-1),4),
("RIGHTPADDING",(0,0),(-1,-1),4),
])
return Table(data, colWidths=[w*0.5, w*0.5], style=ts)
# ── Page template with header/footer ─────────────────────────────────────────
TOPIC = "UPPER GI BLEEDING"
SUB = "Clinical Correlations & OSCE Notes"
SRC = "Sources: Bailey & Love · Sabiston Surgery · Robbins Pathology · Sleisenger & Fordtran"
def draw_page(canvas, doc):
canvas.saveState()
# header bar
canvas.setFillColor(NAVY)
canvas.rect(0, PH - HEADER_H, PW, HEADER_H, fill=1, stroke=0)
canvas.setFillColor(GOLD)
canvas.rect(0, PH - HEADER_H - 2.5, PW, 2.5, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 7)
canvas.drawString(8*mm, PH - 9*mm, f"Page {doc.page}")
canvas.setFont("Helvetica-Bold", 11)
canvas.drawCentredString(PW/2, PH - 9.5*mm, f"{TOPIC} — {SUB}")
canvas.setFont("Helvetica", 7)
canvas.drawRightString(PW - 8*mm, PH - 9*mm, "Final Year MBBS")
# column labels
canvas.setFillColor(GOLD)
canvas.setFont("Helvetica-Bold", 7)
canvas.drawString(LX, PH - HEADER_H + 1.5*mm, "◀ LEFT")
canvas.drawRightString(RX + COL_W, PH - HEADER_H + 1.5*mm, "RIGHT ▶")
# centre divider
canvas.setStrokeColor(DIVL)
canvas.setLineWidth(0.6)
canvas.line(PW/2, CY + 2*mm, PW/2, PH - HEADER_H - 2*mm)
# footer bar
canvas.setFillColor(NAVY)
canvas.rect(0, 0, PW, FOOTER_H - 1*mm, fill=1, stroke=0)
canvas.setFillColor(GOLD)
canvas.rect(0, FOOTER_H - 1*mm, PW, 1.5, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 6)
canvas.drawCentredString(PW/2, 2.5*mm, SRC)
canvas.restoreState()
def draw_cover(canvas, doc):
canvas.saveState()
canvas.setFillColor(NAVY)
canvas.rect(0, 0, PW, PH, fill=1, stroke=0)
canvas.setFillColor(GOLD)
canvas.rect(0, PH*0.47, PW, PH*0.08, fill=1, stroke=0)
canvas.setFillColor(colors.HexColor("#23508C"))
canvas.rect(0, 0, PW, PH*0.47, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 52)
canvas.drawCentredString(PW/2, PH*0.60, "UPPER GI BLEEDING")
canvas.setFillColor(GOLD)
canvas.setFont("Helvetica-Bold", 18)
canvas.drawCentredString(PW/2, PH*0.50, "Clinical OSCE Notes | Final Year MBBS")
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 13)
canvas.drawCentredString(PW/2, PH*0.40, "History Taking · Examination · Investigations · Management")
canvas.drawCentredString(PW/2, PH*0.35, "Scoring Systems · PUD · Varices · GORD · OSCE Tips")
canvas.setFont("Helvetica", 9)
canvas.setFillColor(colors.HexColor("#AAAAAA"))
canvas.drawCentredString(PW/2, PH*0.10, SRC)
canvas.restoreState()
# ── Build document ────────────────────────────────────────────────────────────
OUTPUT = "/home/daytona/workspace/upper_gi_notes/Upper_GI_Clinical_OSCE_Booklet.pdf"
doc = BaseDocTemplate(
OUTPUT, pagesize=landscape(A4),
leftMargin=0, rightMargin=0, topMargin=0, bottomMargin=0,
)
cover_frame = Frame(0, 0, PW, PH, id="cover")
left_frame = Frame(LX, CY, COL_W, COL_H, id="left", leftPadding=2, rightPadding=2)
right_frame = Frame(RX, CY, COL_W, COL_H, id="right", leftPadding=2, rightPadding=2)
doc.addPageTemplates([
PageTemplate(id="cover_tmpl", frames=[cover_frame], onPage=draw_cover),
PageTemplate(id="content", frames=[left_frame, right_frame], onPage=draw_page),
])
story = []
# ════════════════════════════════════════════════════════════════════════════════
# COVER PAGE
# ════════════════════════════════════════════════════════════════════════════════
story.append(NextPageTemplate("content"))
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════════════════════
# PAGE 1 — HISTORY TAKING (Left) | WHAT THE HISTORY TELLS YOU (Right)
# ════════════════════════════════════════════════════════════════════════════════
# --- LEFT: History Taking Script ---
story += [
H1("HISTORY TAKING — UGIB"),
HR(),
P("<b>Opening question:</b> <i>"Can you describe what happened? Did you vomit blood or pass black stools?"</i>", NOR),
SP(2),
H2("1. CHARACTER OF BLEED"),
tbl(["Ask This", "Clinical Meaning"],
[["Bright red vomit?", "Active arterial bleed — high risk"],
["Coffee-ground vomit?", "Slow/stopped bleed — oxidised Hb"],
["Melaena (black tarry stool)?","Blood digested in UGI → >100 mL loss"],
["Fresh rectal blood?", "Brisk UGIB (>1000 mL/hr) OR lower GI"],
["How much? (cupful/bowl?)","Estimate blood loss; >150 mL = massive"],
["How many episodes?", "Frequency tells you ongoing vs. resolved"]],
[0.42, 0.58], even_bg=LBLUE, odd_bg=WHITE),
SP(3),
H2("2. ASSOCIATED SYMPTOMS — 'VINDICATE THE DIAGNOSIS'"),
tbl(["Symptom", "Think of..."],
[["Epigastric pain relieved by food", "Duodenal ulcer"],
["Epigastric pain WORSE with food", "Gastric ulcer"],
["Pain radiates to back", "Penetrating ulcer → pancreas"],
["Dysphagia + weight loss", "Oesophageal/gastric Ca"],
["Projectile vomiting first THEN haematemesis","Mallory-Weiss tear"],
["Jaundice + haematemesis", "Oesophageal varices (CLD)"],
["Heartburn/regurgitation", "GORD, oesophagitis"],
["Early satiety + anorexia + wt loss", "Gastric carcinoma"]],
[0.48, 0.52], even_bg=AMBER, odd_bg=WHITE),
SP(3),
H2("3. DRUG HISTORY — ALWAYS ASK"),
cbox("High-Risk Drugs for UGIB",
["NSAIDs / Aspirin → inhibit COX-1 → ↓mucus → ulceration",
"Anticoagulants (warfarin, DOAC) → coagulopathy",
"Steroids → impair mucosal healing",
"SSRIs → impair platelet aggregation",
"Bisphosphonates (alendronate) → oesophageal ulceration",
"Iron tablets / activated charcoal → mimic melaena (not true bleed)"],
hbg=MIDRED, bbg=RED),
SP(3),
H2("4. SOCIAL HISTORY"),
B("<b>Alcohol:</b> How much, how long? Chronic → cirrhosis → varices"),
B("<b>Smoking:</b> Impairs mucosal healing, ↑ ulcer risk"),
B("<b>Stress / ICU admission:</b> Stress ulcers (Cushing's / Curling's)"),
B("<b>Family Hx of GI cancer:</b> Gastric Ca / Lynch syndrome"),
SP(3),
H2("5. PAST MEDICAL HISTORY"),
B("Previous peptic ulcer? Prior bleeds? → recurrence likely"),
B("Known liver disease / hepatitis / alcohol cirrhosis → varices"),
B("Renal failure / COPD / IHD → ↑ ulcer risk, poor tolerance of bleed"),
B("Previous abdominal aortic surgery → aorto-duodenal fistula"),
B("Burns / head injury in ICU → Curling's / Cushing's stress ulcer"),
]
story.append(FrameBreak())
# --- RIGHT: Clinical Correlations & OSCE Pearls ---
story += [
H1("CLINICAL CORRELATIONS & OSCE PEARLS"),
HR(),
hbox("🔑 KEY OSCE PRINCIPLE: Every haematemesis or melaena patient → ask for DRUG history first. NSAIDs + aspirin = most common preventable cause.", bg=AMBER, border=GOLD),
SP(3),
H2("THE CLASSIC CLINICAL SCENARIOS"),
tbl(["Patient Scenario", "Likely Diagnosis"],
[["50 yr man, NSAID use, epigastric pain 2 hrs post meal, melena", "Duodenal ulcer bleed"],
["Alcoholic, jaundice, spider naevi, massive haematemesis", "Ruptured oesophageal varices"],
["Violent vomiting → sudden haematemesis, no prior pain", "Mallory-Weiss tear"],
["Elderly on aspirin, weight loss, coffee-ground vomit", "Gastric carcinoma bleed"],
["Young man, recurrent heartburn, haematemesis after meals", "Erosive oesophagitis / GORD"],
["Post-aortic aneurysm repair → haematemesis", "Aorto-duodenal fistula"],
["ICU patient on ventilator → sudden melaena", "Stress ulcer / erosive gastritis"],
["Iron deficiency anaemia + dysphagia + post-cricoid web", "Plummer-Vinson — bleed from web"]],
[0.55, 0.45], even_bg=LBLUE, odd_bg=WHITE),
SP(3),
H2("WHY UREA RISES IN UGIB — CLINICAL PEARL"),
hbox("<b>Urea:Creatinine ratio >100:1</b> strongly suggests UGIB.<br/>Mechanism: blood in gut acts like a protein meal → absorbed → urea production ↑. Creatinine stays normal. This ratio helps distinguish UGIB from renal failure (both ↑ urea, but renal failure also ↑ creatinine).", bg=TEAL, border=DKTEAL),
SP(3),
H2("MELAENA vs COFFEE GROUND vs HAEMATEMESIS"),
tbl(["Feature", "Melaena", "Coffee Ground", "Haematemesis"],
[["Blood volume", ">100 mL", "Small–moderate", ">150 mL"],
["Mechanism", "Digested by HCl + bacteria", "Oxidised by HCl", "Active bleed"],
["Colour", "Jet black, tarry", "Dark brown, grainy", "Bright red / clots"],
["Smell", "Characteristic offensive", "Mild", "Metallic/fresh"],
["Urgency", "Urgent", "Semi-urgent", "Emergency"]],
[0.22, 0.26, 0.26, 0.26], even_bg=PINK, odd_bg=WHITE),
SP(3),
H2("MALLORY-WEISS vs OESOPHAGEAL VARICES"),
two_col_tbl(
["Sudden vomiting THEN blood", "No prior GI disease usually",
"Young-middle aged", "Alcohol binge common trigger",
"Bleeding stops spontaneously 90%", "Endoscopy: mucosal tear at GOJ"],
["Alcoholic / liver disease", "Stigmata of CLD on exam",
"Spider naevi, ascites, jaundice", "Massive haematemesis",
"Portal HTN confirmed on USS", "Endoscopy: dilated submucosal veins"],
"Mallory-Weiss Tear", "Oesophageal Varices"),
SP(3),
hbox("⚠ RED FLAG: If haematemesis + PREVIOUS AORTIC SURGERY → think aorto-duodenal fistula. DO NOT scope — go straight to CT angiography + vascular surgery.", bg=RED, border=DKRED),
]
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════════════════════
# PAGE 2 — EXAMINATION (Left) | SCORING SYSTEMS (Right)
# ════════════════════════════════════════════════════════════════════════════════
# --- LEFT: Clinical Examination ---
story += [
H1("CLINICAL EXAMINATION — UGIB"),
HR(),
H2("A. HAEMODYNAMIC STATUS — DO THIS FIRST"),
tbl(["Finding", "Grade of Shock", "Blood Loss"],
[["Normal vitals", "Class I", "<750 mL (15%)"],
["PR >100, normal BP, anxious", "Class II", "750–1500 mL (15–30%)"],
["PR >120, SBP <90, confused", "Class III","1500–2000 mL (30–40%)"],
["PR >140, SBP <70, unconscious", "Class IV", ">2000 mL (>40%)"]],
[0.50, 0.25, 0.25], even_bg=AMBER, odd_bg=WHITE),
SP(2),
hbox("<b>Shock Index = HR ÷ SBP.</b> Normal = 0.5–0.7. SI >1 = significant haemorrhage. SI >1.5 = life-threatening bleed.", bg=AMBER, border=GOLD),
SP(3),
H2("B. HANDS — LOOK FOR CLD STIGMATA (= Think Varices)"),
tbl(["Sign", "What It Means"],
[["Leuconychia (white nails)", "Hypoalbuminaemia → cirrhosis"],
["Koilonychia (spoon nails)", "Iron deficiency → chronic blood loss"],
["Clubbing", "Cirrhosis, IBD, GI lymphoma"],
["Palmar erythema", "Cirrhosis (↑ oestrogen)"],
["Dupuytren's contracture", "Alcohol-related cirrhosis"],
["Asterixis (liver flap)", "Hepatic encephalopathy → decompensated CLD"],
["Peripheral oedema", "Hypoalbuminaemia"]],
[0.42, 0.58], even_bg=LBLUE, odd_bg=WHITE),
SP(3),
H2("C. FACE & NECK"),
B("<b>Jaundice:</b> scleral icterus → hepatobiliary disease → varices"),
B("<b>Spider naevi (>5):</b> upper trunk, face → CLD (from ↑ oestrogen)"),
B("<b>Parotid enlargement:</b> alcohol excess"),
B("<b>Virchow's node (L supraclavicular):</b> gastric Ca metastasis"),
B("<b>Anaemia:</b> conjunctival pallor → chronic blood loss"),
SP(3),
H2("D. ABDOMEN — WHAT TO FIND & WHAT IT MEANS"),
tbl(["Finding", "Diagnosis to Consider"],
[["Epigastric tenderness", "PUD (gastric/duodenal)"],
["Epigastric mass", "Gastric carcinoma"],
["Hepatomegaly", "CLD, hepatoma, CCF"],
["Splenomegaly", "Portal hypertension → varices"],
["Ascites (shifting dullness)", "CLD, peritoneal mets"],
["Caput medusae", "Portal vein thrombosis / cirrhosis"],
["Succussion splash", "Gastric outlet obstruction (PUD complication)"],
["Board-like rigidity", "Perforation — DO NOT scope!"],
["Rectal exam: melaena/blood", "Confirm GI bleed source"]],
[0.42, 0.58], even_bg=GREEN, odd_bg=WHITE),
SP(2),
hbox("OSCE TIP: Always check the <b>rectum</b> — a melaena stool on the gloved finger confirms UGIB even if the patient denies symptoms.", bg=AMBER, border=GOLD),
]
story.append(FrameBreak())
# --- RIGHT: Scoring Systems ---
story += [
H1("RISK SCORING SYSTEMS"),
HR(),
H2("1. GLASGOW-BLATCHFORD SCORE (GBS) — USE ON ADMISSION"),
P("<i>Used BEFORE endoscopy. Predicts need for intervention.</i>", SML),
tbl(["Parameter", "Finding", "Score"],
[["Urea (mmol/L)", "6.5–8.0 / 8.0–10 / 10–25 / ≥25", "2 / 3 / 4 / 6"],
["Hb g/dL (Male)", "12–13 / 10–12 / <10", "1 / 3 / 6"],
["Hb g/dL (Female)", "10–12 / <10", "1 / 6"],
["SBP mmHg", "100–109 / 90–99 / <90", "1 / 2 / 3"],
["Pulse ≥100 bpm", "Yes", "1"],
["Melaena", "Yes", "1"],
["Syncope", "Yes", "2"],
["Liver disease", "Yes", "2"],
["Cardiac failure","Yes", "2"]],
[0.33, 0.42, 0.25], even_bg=LBLUE, odd_bg=WHITE),
SP(1),
hbox("<b>GBS = 0 → discharge home safely (outpatient OGD).</b> GBS ≥ 1 → admit. GBS ≥ 6 → HIGH risk, urgent OGD within 12–24 hrs.", bg=AMBER, border=GOLD),
SP(3),
H2("2. ROCKALL SCORE — USE AFTER ENDOSCOPY"),
P("<i>Predicts re-bleed rate and mortality.</i>", SML),
tbl(["Variable", "0 pts", "1 pt", "2 pts", "3 pts"],
[["Age", "<60", "60–79", "≥80", "—"],
["Shock", "None", "Tachy >100 bpm","SBP <100", "—"],
["Co-morbid", "None", "—", "CCF / IHD", "CRF / Liver Ca"],
["Diagnosis", "MW/none","—", "All other diag.", "GI malignancy"],
["SRH*", "None/flat spot","—","Blood/vessel/clot","—"]],
[0.24, 0.19, 0.19, 0.22, 0.16], even_bg=PINK, odd_bg=WHITE),
SP(1),
P("<i>*SRH = Stigmata of Recent Haemorrhage (Forrest classification)</i>", SML),
hbox("Score 0 = 0% mortality. Score 1–2 = low risk. Score ≥ 5 = HIGH risk (mortality ~35%). Score ≥ 8 = >40% mortality.", bg=RED, border=DKRED),
SP(3),
H2("3. FORREST CLASSIFICATION — WHAT YOU SEE ON OGD"),
tbl(["Class", "Endoscopic Finding", "Rebleed Risk"],
[["Ia", "Spurting arterial haemorrhage", "90%"],
["Ib", "Oozing haemorrhage", "55%"],
["IIa", "Non-bleeding visible vessel", "43%"],
["IIb", "Adherent blood clot on ulcer", "22%"],
["IIc", "Flat pigmented haematin spot", "10%"],
["III", "Clean-based ulcer — no stigmata", "5%"]],
[0.12, 0.62, 0.26], even_bg=LBLUE, odd_bg=WHITE),
SP(1),
hbox("OSCE RULE: Forrest Ia, Ib, IIa, IIb → requires endoscopic therapy. IIc and III → PPI + discharge when stable.", bg=GREEN, border=DKGREEN),
]
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════════════════════
# PAGE 3 — INVESTIGATIONS (Left) | MANAGEMENT FLOWCHART (Right)
# ════════════════════════════════════════════════════════════════════════════════
story += [
H1("INVESTIGATIONS — WHAT TO ORDER & WHY"),
HR(),
H2("IMMEDIATE BLOODS"),
tbl(["Test", "What You're Looking For", "Clinical Value"],
[["CBC / FBC", "Hb%, haematocrit, platelets", "Hb <7 → transfuse; Hb may be normal initially (dilution lag)"],
["Urea & Electrolytes", "Urea, creatinine, Na+, K+", "Urea:Cr ratio >100 = UGIB; K+ ↓ with NGT suction"],
["LFTs", "Bilirubin, albumin, AST/ALT", "Abnormal → CLD → think varices"],
["Coagulation (PT/INR/APTT)","Clotting cascade", "Prolonged INR → cirrhosis or anticoagulants"],
["Blood group & crossmatch","Compatibility for transfusion", "Order at FIRST contact — don't wait for Hb to drop"],
["Serum lactate", "Tissue hypoperfusion marker", ">2 mmol/L = inadequate resuscitation"],
["ABG", "pH, base excess, HCO₃⁻", "pH <7.35 + BE <-6 = metabolic acidosis from shock"]],
[0.20, 0.30, 0.50], even_bg=LBLUE, odd_bg=WHITE),
SP(2),
H2("IMAGING"),
tbl(["Investigation", "When to Use", "What It Shows"],
[["CXR (erect)", "All admissions with acute abdomen / suspected perforation", "Free air under diaphragm = perforation → DO NOT SCOPE"],
["USS abdomen", "Suspected CLD / portal HTN", "Liver texture, splenomegaly, portal vein flow, ascites"],
["CT mesenteric angiogram","Massive bleed if OGD negative, or pre-op planning","Active bleeding >0.5 mL/min, vascular anatomy"],
["Technetium scan", "Obscure GI bleed, slow intermittent","Detects bleed >0.1 mL/min — localises before angiography"],
["CT abdo/pelvis + contrast","Suspected mass / Ca / aortic fistula","Staging, defines anatomy, excludes aorto-duodenal fistula"]],
[0.23, 0.37, 0.40], even_bg=AMBER, odd_bg=WHITE),
SP(2),
H2("ENDOSCOPY — OGD (Gold Standard)"),
cbox("OGD — When, Why, and What to Do",
["TIMING: Within 24 hrs of admission for all admitted UGIB (within 12 hrs if high-risk Rockall/GBS)",
"DIAGNOSTIC: Identifies source in 95% of cases",
"THERAPEUTIC: Treat the cause at same sitting",
"PU BLEEDING: Adrenaline injection (1:10,000) + mechanical clip / thermal APC",
"VARICEAL BLEED: Endoscopic Variceal Ligation (EVL/banding) — preferred over sclerotherapy",
"MALLORY-WEISS: Usually self-limiting; injection or clip if still bleeding",
"PRE-SCOPE PREP: Erythromycin 250 mg IV 30 min before OGD → clears stomach for better view"],
hbg=DKTEAL, bbg=TEAL),
SP(2),
hbox("OSCE PEARL: Give <b>erythromycin IV</b> (prokinetic) before emergency OGD in haematemesis — it empties the stomach of blood clots, improving visualisation dramatically.", bg=AMBER, border=GOLD),
]
story.append(FrameBreak())
# --- RIGHT: Management ---
story += [
H1("MANAGEMENT — UGIB"),
HR(),
H2("STEP 1 — RESUSCITATION (ABC)"),
cbox("IMMEDIATE ACTIONS (First 30 minutes)",
["A: Airway — intubate if GCS ≤8 or unable to protect airway (massive haematemesis)",
"B: O₂ via mask, SpO₂ target >95%",
"C: 2× large-bore IV cannulae (14–16G) — both antecubital fossae",
"IV fluid: 0.9% NaCl or Hartmann's — RAPID infusion to restore BP",
"Bloods: FBC, U&E, LFT, coag, G&S — draw at cannulation",
"Urinary catheter — target urine output >0.5 mL/kg/hr",
"NG tube: if altered consciousness; confirms blood in stomach",
"Nil by mouth: prepare for OGD",
"Admit to HDU/ICU if haemodynamically unstable"],
hbg=NAVY, bbg=LBLUE),
SP(2),
H2("STEP 2 — BLOOD PRODUCTS"),
tbl(["Situation", "What to Give", "Target"],
[["Hb <7 g/dL", "Packed red cells", "Hb 7–8 g/dL (restrictive strategy reduces mortality)"],
["Hb <8 + IHD/ACS", "Packed red cells", "Hb >8 g/dL"],
["INR >1.5 (coagulopathy)", "FFP 4 units", "INR <1.5"],
["Platelets <50", "Platelet transfusion","Platelets >50"],
["On warfarin + active bleed","Vit K IV + PCC", "Reverse anticoagulation urgently"],
["Massive transfusion (>10u)","1:1:1 (RBC:FFP:Plt)","Damage control resuscitation"]],
[0.28, 0.28, 0.44], even_bg=AMBER, odd_bg=WHITE),
SP(2),
H2("STEP 3 — PHARMACOLOGICAL"),
tbl(["Drug", "For", "Dose / Route"],
[["IV PPI (Omeprazole)", "PU bleed — pre/post OGD", "80 mg bolus → 8 mg/hr infusion × 72 hr, then oral OD"],
["Terlipressin", "Variceal bleed", "2 mg IV every 6 hrs × 72 hr (vasoconstricts splanchnic bed)"],
["Octreotide", "Variceal bleed (alternative)","50 mcg bolus → 50 mcg/hr infusion × 72 hr"],
["Erythromycin", "Pre-OGD prokinetic", "250 mg IV, 30 min before scope"],
["Norfloxacin/Ceftrx", "SBP prophylaxis in cirrhosis","Norfloxacin 400 mg BD or Ceftriaxone 1g IV × 7 days"],
["Propranolol", "Secondary prophylaxis varices","40–80 mg BD — target HR 55–60 bpm"]],
[0.22, 0.28, 0.50], even_bg=GREEN, odd_bg=WHITE),
SP(2),
H2("STEP 4 — WHEN ENDOSCOPY FAILS"),
B("<b>Repeat OGD:</b> second attempt by senior endoscopist"),
B("<b>Sengstaken-Blakemore tube:</b> temporary bridge for variceal bleed — max 24 hrs (risk of necrosis/aspiration)"),
B("<b>TIPSS:</b> Transjugular Intrahepatic Portosystemic Shunt — reduces portal pressure; bridge to transplant"),
B("<b>CT angiography + IR embolisation:</b> for PU bleed refractory to endoscopy"),
B("<b>Surgery:</b> last resort — oversew vessel (PU), oesophageal transection / devascularisation (varices)"),
SP(2),
hbox("OSCE PEARL: In variceal bleed, <b>terlipressin</b> reduces 7-day mortality by 34% and should be started as soon as diagnosis suspected — even before OGD.", bg=AMBER, border=GOLD),
]
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════════════════════
# PAGE 4 — PUD CLINICAL (Left) | PUD OSCE PEARLS + Treatment (Right)
# ════════════════════════════════════════════════════════════════════════════════
story += [
H1("PEPTIC ULCER DISEASE — CLINICAL APPROACH"),
HR(),
H2("HOW TO DISTINGUISH DU FROM GU IN HISTORY"),
tbl(["Clinical Feature", "Duodenal Ulcer (DU)", "Gastric Ulcer (GU)"],
[["Pain timing", "2–3 hrs after eating + night pain", "During or immediately after food"],
["Pain relief", "Food / antacids → RELIEVES pain", "Food → NO relief or WORSENS pain"],
["Appetite / weight", "Normal or ↑ (patient eats to relieve)", "↓ Appetite, weight loss (fear of eating)"],
["Age", "Younger (35–45 yrs)", "Older (55–65 yrs)"],
["Nausea/vomiting", "Less common", "More common (pyloric oedema)"],
["Location of pain", "Right epigastric / upper abdo", "Central / left epigastric"],
["Malignancy risk", "Very rare", "5–10% → MUST biopsy all GU × 6 sites"]],
[0.28, 0.36, 0.36], even_bg=LBLUE, odd_bg=WHITE),
SP(2),
H2("H. PYLORI — THE KEY PATHOGEN"),
tbl(["Fact", "Detail"],
[["Prevalence", "~43% global; >60% in developing world"],
["Detection", "CLO test (OGD biopsy) — GOLD STANDARD; Urea Breath Test (non-invasive); Stool Ag test; Serology (can't distinguish active vs past)"],
["Mechanism", "Urease → ammonia → alkaline microenv. → survives in acid; CagA protein → cytotoxin → mucosal damage"],
["Association", "90% DU, 70–80% GU, gastric MALT lymphoma, gastric Ca"],
["After treatment", "Confirm eradication: UBT or stool Ag test ≥4 weeks post-antibiotics (not serology!)"]],
[0.22, 0.78], even_bg=AMBER, odd_bg=WHITE),
SP(2),
H2("H. PYLORI ERADICATION THERAPY"),
tbl(["Regimen", "Drugs", "Duration"],
[["Triple (1st line)", "PPI (Omeprazole 20mg BD) + Amoxicillin 1g BD + Clarithromycin 500mg BD", "7 days (14 days if low eradication rates)"],
["Quadruple (if clarithromycin-resistant / 2nd line)", "PPI + Bismuth + Metronidazole 400mg TDS + Tetracycline 500mg QID", "10–14 days"],
["Levofloxacin-based (3rd line)", "PPI + Amoxicillin + Levofloxacin", "10–14 days"],
["Allergy to penicillin", "Replace Amoxicillin with Metronidazole", "7–14 days"]],
[0.25, 0.55, 0.20], even_bg=GREEN, odd_bg=WHITE),
SP(2),
hbox("CONFIRM ERADICATION: Test 4–6 weeks after finishing antibiotics (not before!) using <b>Urea Breath Test</b> or <b>stool antigen test</b>. NEVER use serology to confirm — antibodies persist for years.", bg=AMBER, border=GOLD),
]
story.append(FrameBreak())
story += [
H1("PUD — OSCE PEARLS & COMPLICATIONS"),
HR(),
H2("COMPLICATIONS — 'BPOM'"),
tbl(["Complication", "Clinical Features", "Management"],
[["Bleeding", "Haematemesis / melaena — most common", "OGD + clip/adrenaline + IV PPI"],
["Perforation", "SUDDEN severe epigastric pain → board-like rigidity, rigid abdomen, absent bowel sounds, free air on CXR/AXR", "NBM, NG tube, IV fluids, emergency laparotomy (Graham's omental patch)"],
["Obstruction (GOO)","Projectile vomiting (no bile), succussion splash, dehydration, hypokalaemic hypochloraemic alkalosis", "Nasogastric decompression, replace K+, endoscopic balloon dilation or surgery"],
["Malignancy", "Gastric ulcer only (5–10%); suspect if no healing after 8 weeks PPI", "MUST biopsy all gastric ulcers × 6 biopsies; early OGD review at 6–8 weeks"]],
[0.18, 0.44, 0.38], even_bg=RED, odd_bg=WHITE),
SP(2),
hbox("⚠ PERFORATION OSCE TRAP: Patient suddenly improves after severe pain (as peritoneum accommodates) then gets WORSE. Don't be fooled — erect CXR shows free air under diaphragm.", bg=RED, border=DKRED),
SP(3),
H2("SURGICAL OPTIONS FOR PUD"),
tbl(["Procedure", "When Used", "Key Note"],
[["Graham's omental patch", "Perforated DU/GU", "Emergency; patch with omentum; adds H.pylori eradication"],
["Highly Selective Vagotomy (HSV)", "Elective refractory DU (rare now)", "Denervates parietal cells only; preserves antral motility; lowest complication rate"],
["Truncal vagotomy + pyloroplasty", "DU with GOO", "Older operation; causes diarrhoea in 20%"],
["Billroth I (gastroduodenostomy)", "Distal gastric ulcer", "Restores normal anatomy; preferred for GU"],
["Billroth II (gastrojejunostomy)", "When DU anastomosis impossible", "Risk of bile reflux gastritis, dumping syndrome"]],
[0.28, 0.30, 0.42], even_bg=LBLUE, odd_bg=WHITE),
SP(2),
H2("ZOLLINGER-ELLISON SYNDROME (ZES)"),
B("<b>Gastrinoma</b> (usually pancreatic/duodenal) → hypergastrinaemia → ↑↑ acid → multiple / recurrent ulcers"),
B("Suspect when: multiple ulcers, ulcers beyond duodenal bulb, failure of standard therapy, diarrhoea with ulcers"),
B("<b>Diagnose:</b> fasting serum gastrin >1000 pg/mL; secretin stimulation test; somatostatin receptor scintigraphy (Octreoscan)"),
B("Part of MEN-1 syndrome (check parathyroid, pituitary)"),
B("Treat: high-dose PPI + surgical resection if localised"),
]
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════════════════════
# PAGE 5 — OESOPHAGEAL VARICES CLINICAL (Left) | GORD + OSCE SUMMARY (Right)
# ════════════════════════════════════════════════════════════════════════════════
story += [
H1("OESOPHAGEAL VARICES — CLINICAL APPROACH"),
HR(),
H2("HOW VARICES FORM — UNDERSTAND THE PATHWAY"),
cbox("Portal Hypertension → Varices — Step by Step",
["1. Liver cirrhosis (most common) → ↑ resistance to portal blood flow",
"2. Portal pressure rises >12 mmHg (normal 5–10 mmHg)",
"3. Portal blood seeks collateral routes to reach systemic circulation",
"4. Portosystemic anastomoses enlarge → VARICES form",
"5. Variceal wall tension ↑ → rupture when pressure gradient >12 mmHg",
"6. One-third of cirrhotics with varices will bleed; 30% mortality per episode"],
hbg=NAVY, bbg=LBLUE),
SP(2),
H2("PORTOSYSTEMIC ANASTOMOSIS SITES — CLINICAL CORRELATIONS"),
tbl(["Site", "Portal Tributary", "Systemic Vein", "Result"],
[["Lower oesophagus", "L gastric (coronary) v.", "Azygos / hemiazygos", "Oesophageal varices → BLEEDING"],
["Umbilicus", "Paraumbilical veins", "Epigastric veins", "Caput medusae"],
["Rectum", "Superior rectal v.", "Middle/inf rectal v.","Haemorrhoids"],
["Retroperitoneum", "Retroperitoneal veins", "Renal / lumbar veins","Retroperitoneal varices"],
["Spleen-left kidney","Splenicorenal collaterals","Left renal vein", "Spontaneous splenorenalshunt"]],
[0.22, 0.25, 0.25, 0.28], even_bg=AMBER, odd_bg=WHITE),
SP(2),
H2("CLINICAL ASSESSMENT OF LIVER DISEASE SEVERITY"),
tbl(["Parameter", "Child A (1pt)", "Child B (2pt)", "Child C (3pt)"],
[["Bilirubin (µmol/L)", "<34", "34–51", ">51"],
["Albumin (g/L)", ">35", "28–35", "<28"],
["PT prolongation (s)","<4", "4–6", ">6"],
["Ascites", "None", "Mild", "Moderate/Severe"],
["Encephalopathy", "None", "Grade 1–2","Grade 3–4"]],
[0.30, 0.23, 0.23, 0.24], even_bg=LBLUE, odd_bg=WHITE),
SP(1),
hbox("<b>Child A (5–6)</b> = Well-compensated, low op risk. <b>Child B (7–9)</b> = Moderate. <b>Child C (10–15)</b> = Decompensated, high op mortality (~80%). Child C → consider liver transplantation.", bg=AMBER, border=GOLD),
SP(2),
H2("PRIMARY PROPHYLAXIS OF VARICEAL BLEED"),
B("<b>Screen all cirrhotics</b> for varices with OGD at diagnosis"),
B("<b>Small varices + no red signs:</b> non-selective BB (propranolol/nadolol) — target HR 55 bpm or 25% reduction"),
B("<b>Medium/large varices:</b> non-selective BB OR EVL (endoscopic variceal ligation)"),
B("<b>Carvedilol</b> (non-selective BB + alpha-1 blocker) now preferred — greater portal pressure reduction"),
B("<b>ISMN (isosorbide mononitrate):</b> if BB contraindicated"),
]
story.append(FrameBreak())
story += [
H1("GORD — CLINICAL APPROACH & OSCE"),
HR(),
H2("HISTORY TAKING FOR GORD"),
tbl(["Question to Ask", "Why It Matters"],
[["Burning pain from stomach to chest?", "Classic heartburn — GORD hallmark"],
["Worse lying flat / at night?", "Gravity-dependent — confirms reflux"],
["Worse after large meals / fatty food?","LOS relaxation after fat → reflux"],
["Relieved by antacids?", "Acid-mediated → supports GORD"],
["Regurgitation of acid into throat?", "Pathognomonic of reflux"],
["Cough/hoarseness/asthma worsening?", "Atypical GORD — laryngopharyngeal reflux"],
["Dysphagia (progressive)?", "ALARM — peptic stricture or Barrett's-related Ca"],
["Weight loss / anaemia?", "ALARM — rule out malignancy urgently"]],
[0.46, 0.54], even_bg=LBLUE, odd_bg=WHITE),
SP(2),
H2("BARRETT'S OESOPHAGUS — THE KEY COMPLICATION"),
cbox("Barrett's — Everything You Need to Know",
["Definition: Intestinal metaplasia replacing squamous epithelium in lower oesophagus (>3 cm above GOJ)",
"Caused by: Chronic acid exposure (GORD) → columnar metaplasia",
"Risk of progression: 0.3–0.5% per year → Low-grade dysplasia → High-grade → Adenocarcinoma",
"Diagnose: OGD + biopsy (salmon-coloured mucosa, confirmed histologically)",
"Surveillance: OGD 3-yearly (no dysplasia) → 6-monthly (low-grade) → Urgent resection (high-grade)",
"Treatment: High-grade dysplasia → RFA (radiofrequency ablation) or oesophagectomy"],
hbg=MIDRED, bbg=RED),
SP(2),
H2("GORD TREATMENT LADDER"),
tbl(["Step", "Treatment", "Mechanism"],
[["1 — Lifestyle", "↓ weight, elevate HOB 15 cm, small meals, avoid triggers (coffee/alcohol/fat/chocolate)", "Reduce reflux frequency and volume"],
["2 — Antacids/Alginates", "Gaviscon (alginate forms raft), Rennies", "Neutralise acid, mechanical barrier"],
["3 — H₂ Blockers", "Ranitidine 150mg BD (now limited availability)", "↓ histamine-stimulated acid"],
["4 — PPI (mainstay)", "Omeprazole 20–40mg OD, 30 min before breakfast", "Irreversibly block H⁺/K⁺ ATPase — most effective"],
["5 — Surgery", "Laparoscopic Nissen Fundoplication (360° wrap)", "Mechanically restores LOS competence — definitive cure"]],
[0.12, 0.44, 0.44], even_bg=GREEN, odd_bg=WHITE),
SP(2),
hbox("<b>OSCE PEARL — LA Classification of Oesophagitis:</b> Grade A = <5mm erosions. Grade B = >5mm, non-confluent. Grade C = confluent <75% circumference. Grade D = confluent >75% = severe. Seen on OGD.", bg=AMBER, border=GOLD),
SP(2),
hbox("⚠ ALARM SYMPTOMS IN DYSPEPSIA → URGENT OGD (<2 weeks): <b>A</b>naemia, <b>L</b>oss of weight, <b>A</b>norexia, <b>R</b>ecent dyspepsia onset >55 yrs, <b>M</b>elaena/haematemesis, <b>S</b>wallowing difficulty.", bg=RED, border=DKRED),
]
story.append(PageBreak())
# ════════════════════════════════════════════════════════════════════════════════
# PAGE 6 — QUICK REVISION / OSCE VIVA (Left) | HIGH-YIELD MNEMONICS (Right)
# ════════════════════════════════════════════════════════════════════════════════
story += [
H1("OSCE VIVA — RAPID-FIRE Q&A"),
HR(),
tbl(["Examiner Asks...", "Your Answer"],
[["What is the most common cause of UGIB?",
"Peptic ulcer disease (duodenal ulcer most common)"],
["A patient vomits blood after violent vomiting. Diagnosis?",
"Mallory-Weiss tear — mucosal tear at GOJ; OGD confirms"],
["What is the first investigation in UGIB?",
"Bloods (FBC, U&E, coag, G&S) + erect CXR → then OGD within 24 hrs"],
["Urea 12, creatinine 90. Ratio = 133. What does this mean?",
"Strong evidence of UGIB — blood acts as protein load → urea ↑, creatinine normal"],
["Varices patient: what drug do you start immediately?",
"Terlipressin 2mg IV 6-hourly (+ antibiotics for SBP prophylaxis)"],
["Forrest IIa lesion seen on OGD. What do you do?",
"Endoscopic therapy (clip + adrenaline injection) — 43% rebleed risk without treatment"],
["GBS score = 0. What do you tell the patient?",
"Safe to discharge with outpatient OGD appointment — very low risk of intervention needed"],
["Why give erythromycin before OGD in haematemesis?",
"Prokinetic → clears stomach of blood → better endoscopic visualisation"],
["Patient on warfarin, INR 3.5, active bleed. What do you give?",
"Vit K IV + 4-factor PCC (prothrombin complex concentrate) — urgent reversal"],
["CXR shows free air under diaphragm in epigastric pain patient. Next step?",
"Do NOT scope — surgical emergency: laparotomy + Graham's omental patch"],
["Barrett's oesophagus — how do you confirm?",
"OGD + biopsy — intestinal metaplasia on histology (goblet cells)"],
["What confirms H. pylori eradication?",
"Urea breath test or stool antigen — 4 weeks after antibiotics (NOT serology)"],
["Billroth II complication presenting with bilious vomiting post-meal?",
"Afferent loop syndrome — bile/pancreatic secretions pool in afferent limb"],
["What is TIPSS used for?",
"Refractory variceal bleeding or refractory ascites — reduces portal pressure"],
["Johnson Type III gastric ulcer behaves like what?",
"Like a duodenal ulcer — prepyloric location, associated with ↑ acid"],
["Succussion splash on exam. What has happened?",
"Gastric outlet obstruction (GOO) — complication of chronic PUD/pyloric stenosis"]],
[0.46, 0.54], even_bg=LBLUE, odd_bg=WHITE),
]
story.append(FrameBreak())
story += [
H1("MNEMONICS & HIGH-YIELD SUMMARY"),
HR(),
H2("UGIB CAUSES — 'GAVE ME DIARRHEA'"),
cbox("Causes of Upper GI Bleeding",
["G — Gastric ulcer / Gastritis",
"A — Angiodysplasia (Dieulafoy lesion)",
"V — Varices (oesophageal / gastric)",
"E — Erosions (erosive gastropathy)",
"M — Mallory-Weiss tear",
"E — Esophagitis (reflux oesophagitis)",
"D — Duodenal ulcer (most common PU)",
"I — Iatrogenic (post-biopsy, post-polypectomy)",
"A — Aorto-duodenal fistula (rare, post-AAA repair)"],
hbg=NAVY, bbg=LBLUE),
SP(2),
H2("COMPLICATIONS OF PUD — 'BPOM'"),
tbl(["Letter", "Complication", "Key Sign"],
[["B", "Bleeding — most common", "Haematemesis + melaena"],
["P", "Perforation — most serious", "Board-like rigid abdomen + free air on CXR"],
["O", "Obstruction (GOO)", "Succussion splash + hypokalaemic alkalosis"],
["M", "Malignancy (GU only 5–10%)", "Weight loss + ulcer not healing at 8 weeks"]],
[0.08, 0.46, 0.46], even_bg=PINK, odd_bg=WHITE),
SP(2),
H2("CLD STIGMATA — 'JALAPCC' (on Examination)"),
tbl(["Sign", "What It Means"],
[["J — Jaundice", "Impaired bilirubin conjugation/excretion"],
["A — Asterixis (flap)", "Hepatic encephalopathy — metabolic"],
["L — Leuconychia", "Hypoalbuminaemia"],
["A — Ascites", "Portal HTN + hypoalbuminaemia"],
["P — Palmar erythema", "↑ Oestrogen (impaired hepatic metabolism)"],
["C — Clubbing", "Chronic liver / GI disease"],
["C — Caput medusae + Spider naevi","Portal HTN + ↑ oestrogen"]],
[0.28, 0.72], even_bg=AMBER, odd_bg=WHITE),
SP(2),
H2("DRUGS IN UGIB — QUICK RECALL"),
tbl(["Drug", "Use", "Key Fact"],
[["Omeprazole 80mg IV", "PU bleed", "Bolus + 8mg/hr infusion × 72 hr; stabilises clot"],
["Terlipressin 2mg IV", "Variceal bleed", "Every 6 hrs × 72 hr; only drug proven to ↓ mortality"],
["Erythromycin 250mg", "Pre-OGD prokinetic", "30 min before scope; clears stomach of blood"],
["Norfloxacin 400mg BD","SBP prophylaxis", "Cirrhotics with UGIB → prevent infection"],
["Propranolol", "Variceal prophylaxis","Non-selective BB; target HR 55–60 bpm"],
["Vit K + PCC", "Reverse warfarin", "PCC faster than FFP for emergency reversal"]],
[0.25, 0.28, 0.47], even_bg=GREEN, odd_bg=WHITE),
SP(2),
hbox("FINAL OSCE TIP: If asked about a patient with haematemesis — your first 3 answers should always be: <b>1) Resuscitate (ABC + IV access + fluids)</b>, <b>2) Bloods including G&S</b>, <b>3) GBS score to stratify risk</b>. Then discuss OGD.", bg=AMBER, border=GOLD),
]
story.append(PageBreak())
# ── Build ─────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"Saved: {OUTPUT}")
print("Total pages: 7 (1 cover + 6 content)")
cd /home/daytona/workspace/upper_gi_notes && python3 build_upper_gi_booklet.py 2…
~/upper_gi_notes/build_upper_gi_booklet.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/upper_gi_notes/build_upper_gi_booklet.py'
cd /home/daytona/workspace/upper_gi_notes && python3 build_upper_gi_booklet.py 2…
~/upper_gi_notes/build_upper_gi_booklet.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/upper_gi_notes/build_upper_gi_booklet.py'
cd /home/daytona/workspace/upper_gi_notes && python3 build_upper_gi_booklet.py 2…
pdftoppm -r 100 -f 1 -l 3 /home/daytona/workspace/upper_gi_notes/Upper_GI_Clinic…
pdftoppm -r 80 -f 2 -l 2 /home/daytona/workspace/upper_gi_notes/Upper_GI_Clinica…
python3 -c " from PIL import Image img = Image.open('/home/daytona/workspace/upp…
pdftoppm -r 80 -f 1 -l 1 /home/daytona/workspace/upper_gi_notes/Upper_GI_Clinica…
pdftoppm -r 80 -f 4 -l 4 /home/daytona/workspace/upper_gi_notes/Upper_GI_Clinica…
Upper GI Clinical OSCE Booklet
PDF Document · PDF
| Page | Left Column | Right Column |
|---|---|---|
| Cover | Navy/gold title page | - |
| Pg 2 | History taking script - what to ask + what each answer means | Clinical scenarios (who is the patient → what's the diagnosis), Melaena vs coffee ground vs haematemesis table |
| Pg 3 | Full examination guide - haemodynamic shock grading, CLD stigmata with meaning, abdominal findings | Scoring systems - Glasgow-Blatchford, Rockall, Forrest classification with rebleed % |
| Pg 4 | Investigations - what to order, why, and clinical value of each result + OGD timing/technique | Management flowchart - resuscitation, blood products, drug doses, when to escalate |
| Pg 5 | PUD: DU vs GU history differences, H.pylori detection + eradication regimens | Complications (BPOM) with clinical signs, surgical options, ZES |
| Pg 6 | Varices: how portal HTN develops, portosystemic sites, Child-Pugh scoring | GORD: history taking, Barrett's surveillance, treatment ladder, ALARM symptoms |
| Pg 7 | 16 OSCE viva Q&A pairs - examiner questions with model answers | Mnemonics + drug quick-reference table |
If u had to make this in the most efficient 1 page note ,along the illustration remain
pdftoppm -r 120 -f 1 -l 1 "/home/daytona/workspace/attachments/0099bcdf-8d21-4b6…
pdftoppm -r 120 "/home/daytona/workspace/attachments/0099bcdf-8d21-4b64-ba2c-509…
python3 -c " from PIL import Image for i in range(1,5): img = Image.open(f'/…
python3 -c " from PIL import Image for i in range(4,14): img = Image.open(f'…
An error occurred (ValidationException) when calling the ConverseStream operation: The model returned the following errors: messages.13.content.10.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels
We encountered an error while processing your request. Please try again or reach out to us for help.
Send ,e
An error occurred (ValidationException) when calling the ConverseStream operation: The model returned the following errors: messages.13.content.8.image.source.base64.data: At least one of the image dimensions exceed max allowed size for many-image requests: 2000 pixels
We encountered an error while processing your request. Please try again or reach out to us for help.