U created this kinda nbooklet for me when I provided with with some notes related to dysphagia...can u do that for the following topics

Reading File
Running Command

cd /home/daytona/workspace && python3 -c " import pdfplumber with pdfplumber.open('attachments/93929b43-6afe-45b8-83b8-959a1bf7b45a/Upper_GI_Clinical_OSCE_Booklet (1).pdf') as pdf: print(f'Total pages: {len(pdf.pages)}') for i, page in enumerate(pdf.pages[:5]): print(f'\\n=== PAGE {i+1} ===') print(page.extract_text()) "

Running Command

cd /home/daytona/workspace && python3 -c " import pdfplumber with pdfplumber.open('attachments/93929b43-6afe-45b8-83b8-959a1bf7b45a/Upper_GI_Clinical_OSCE_Booklet (1).pdf') as pdf: for i, page in enumerate(pdf.pages[5:]): print(f'\\n=== PAGE {i+6} ===') print(page.extract_text()) "

Asking for Preferences
It seems the response came back as "No preference" - could you type out the specific topics you want? For example:
  • Lower GI Bleeding
  • Acute Abdomen
  • Pancreatitis
  • Jaundice/Biliary
  • Hernia
  • Colorectal Cancer
  • etc.
Just list the topics you want covered and I'll build the booklet in the exact same format as your Upper GI one!

For all these make the booklets like how I ahace attached...first send me constipation and diarrhoae book

Running Command

cd /home/daytona/workspace && python3 -c " import pdfplumber base = 'attachments/93929b43-6afe-45b8-83b8-959a1bf7b45a/' files = { 'Constipation': 'Constipation (2).pdf', 'Diarrhoea': 'Diarrhoea- SGT.pdf', } for name, fname in files.items(): print(f'\n\n========== {name} ==========') with pdfplumber.open(base+fname) as pdf: print(f'Pages: {len(pdf.pages)}') for i, page in enumerate(pdf.pages): print(f'--- page {i+1} ---') print(page.extract_text()) "

Running Command

cd /home/daytona/workspace && python3 -c " import subprocess base = 'attachments/93929b43-6afe-45b8-83b8-959a1bf7b45a/' files = ['Constipation (2).pdf', 'Diarrhoea- SGT.pdf'] for f in files: out = f.replace('.pdf','').replace(' ','_').replace('(','').replace(')','') result = subprocess.run(['pdftotext', '-layout', base+f, f'/tmp/{out}.txt'], capture_output=True, text=True) print(f'{f}: {result.returncode}') with open(f'/tmp/{out}.txt') as fh: content = fh.read() print(f'Length: {len(content)}') print(content[:3000]) print('---END PREVIEW---') "

Running Command

cd /home/daytona/workspace && python3 -c " import subprocess, os base = 'attachments/93929b43-6afe-45b8-83b8-959a1bf7b45a/' # Convert PDFs to images then OCR files = [ ('Constipation (2).pdf', 'constipation'), ('Diarrhoea- SGT.pdf', 'diarrhoea'), ] for fname, tag in files: fpath = base + fname # Convert to PNG images os.makedirs(f'/tmp/{tag}_imgs', exist_ok=True) result = subprocess.run(['pdftoppm', '-r', '200', fpath, f'/tmp/{tag}_imgs/page'], capture_output=True, text=True) print(f'pdftoppm {tag}: rc={result.returncode}') imgs = sorted(os.listdir(f'/tmp/{tag}_imgs')) print(f' Images: {imgs}') "

Running Command

cd /home/daytona/workspace && python3 -c " import subprocess, os for tag in ['constipation', 'diarrhoea']: imgs = sorted(os.listdir(f'/tmp/{tag}_imgs')) all_text = [] for img in imgs: result = subprocess.run( ['tesseract', f'/tmp/{tag}_imgs/{img}', 'stdout', '--psm', '3'], capture_output=True, text=True ) all_text.append(f'--- {img} ---\n{result.stdout}') combined = '\n'.join(all_text) with open(f'/tmp/{tag}_ocr.txt', 'w') as fh: fh.write(combined) print(f'{tag}: {len(combined)} chars extracted') print(combined[:500]) print('...') " 2>&1 | head -80

Running Command

cat /tmp/constipation_ocr.txt && echo "====DIARRHOEA====" && cat /tmp/diarrhoea_ocr.txt

Running Command

mkdir -p /home/daytona/workspace/osce-booklets && python3 << 'PYEOF' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm, cm from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable, KeepTogether) from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT, TA_JUSTIFY from reportlab.platypus.flowables import BalancedColumns import reportlab print("ReportLab version:", reportlab.Version) print("BalancedColumns available:", True) PYEOF

Writing File

~/osce-booklets/build_booklet.py

"""
Build Constipation & Diarrhoea OSCE Booklet
Matches style of Upper GI Bleeding Clinical OSCE Notes booklet:
  - Dark navy header bar per page
  - Two-column layout
  - Tables with alternating shading
  - Coloured section headers
  - Bullet points, clinical pearls in shaded boxes
  - Footer with sources
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.platypus import (
    BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer,
    Table, TableStyle, HRFlowable, KeepTogether, NextPageTemplate,
    PageBreak
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
from reportlab.pdfgen import canvas as pdfgen_canvas

# ── Colours ──────────────────────────────────────────────────────────────────
NAVY       = colors.HexColor('#1a2a4a')
GOLD       = colors.HexColor('#c8a951')
LIGHT_BLUE = colors.HexColor('#dce8f5')
MID_BLUE   = colors.HexColor('#b0cce8')
DARK_BLUE  = colors.HexColor('#2c5282')
TEAL       = colors.HexColor('#2a7a6b')
RED_BOX    = colors.HexColor('#fdecea')
RED_BORDER = colors.HexColor('#c0392b')
GREEN_BOX  = colors.HexColor('#e8f5e9')
GREEN_BDR  = colors.HexColor('#2e7d32')
AMBER_BOX  = colors.HexColor('#fff8e1')
AMBER_BDR  = colors.HexColor('#f57f17')
WHITE      = colors.white
BLACK      = colors.black
GREY_LIGHT = colors.HexColor('#f2f4f7')
GREY_MID   = colors.HexColor('#d5dce8')

PAGE_W, PAGE_H = A4   # 595.27 x 841.89
MARGIN_T = 22*mm
MARGIN_B = 16*mm
MARGIN_L = 13*mm
MARGIN_R = 13*mm
COL_GAP  = 6*mm
HDR_H    = 18*mm
FTR_H    = 10*mm

USABLE_W = PAGE_W - MARGIN_L - MARGIN_R
COL_W    = (USABLE_W - COL_GAP) / 2

# ── Styles ────────────────────────────────────────────────────────────────────
def make_styles():
    s = {}

    s['title_main'] = ParagraphStyle('title_main',
        fontName='Helvetica-Bold', fontSize=17, textColor=WHITE,
        leading=20, spaceAfter=1, alignment=TA_CENTER)

    s['title_sub'] = ParagraphStyle('title_sub',
        fontName='Helvetica-BoldOblique', fontSize=9.5, textColor=GOLD,
        leading=12, alignment=TA_CENTER)

    s['title_tags'] = ParagraphStyle('title_tags',
        fontName='Helvetica', fontSize=8, textColor=colors.HexColor('#a0bcd8'),
        leading=11, alignment=TA_CENTER)

    s['sec_head'] = ParagraphStyle('sec_head',
        fontName='Helvetica-Bold', fontSize=9, textColor=WHITE,
        backColor=DARK_BLUE, borderPadding=(3,5,3,5),
        leading=12, spaceAfter=4, spaceBefore=6)

    s['sec_head_teal'] = ParagraphStyle('sec_head_teal',
        fontName='Helvetica-Bold', fontSize=9, textColor=WHITE,
        backColor=TEAL, borderPadding=(3,5,3,5),
        leading=12, spaceAfter=4, spaceBefore=6)

    s['subsec'] = ParagraphStyle('subsec',
        fontName='Helvetica-Bold', fontSize=8.5, textColor=DARK_BLUE,
        leading=11, spaceAfter=3, spaceBefore=5)

    s['body'] = ParagraphStyle('body',
        fontName='Helvetica', fontSize=7.8, textColor=BLACK,
        leading=11, spaceAfter=2)

    s['body_bold'] = ParagraphStyle('body_bold',
        fontName='Helvetica-Bold', fontSize=7.8, textColor=BLACK,
        leading=11, spaceAfter=2)

    s['bullet'] = ParagraphStyle('bullet',
        fontName='Helvetica', fontSize=7.8, textColor=BLACK,
        leading=11, leftIndent=10, bulletIndent=2, spaceAfter=1.5)

    s['bullet_bold'] = ParagraphStyle('bullet_bold',
        fontName='Helvetica-Bold', fontSize=7.8, textColor=DARK_BLUE,
        leading=11, leftIndent=10, bulletIndent=2, spaceAfter=1.5)

    s['pearl'] = ParagraphStyle('pearl',
        fontName='Helvetica-BoldOblique', fontSize=7.8, textColor=colors.HexColor('#7b3f00'),
        backColor=AMBER_BOX, borderColor=AMBER_BDR,
        borderWidth=0.8, borderPadding=(4,5,4,5),
        leading=11, spaceAfter=4, spaceBefore=4)

    s['red_flag'] = ParagraphStyle('red_flag',
        fontName='Helvetica-Bold', fontSize=7.8, textColor=RED_BORDER,
        backColor=RED_BOX, borderColor=RED_BORDER,
        borderWidth=0.8, borderPadding=(4,5,4,5),
        leading=11, spaceAfter=4, spaceBefore=4)

    s['green_box'] = ParagraphStyle('green_box',
        fontName='Helvetica', fontSize=7.8, textColor=colors.HexColor('#1b5e20'),
        backColor=GREEN_BOX, borderColor=GREEN_BDR,
        borderWidth=0.8, borderPadding=(4,5,4,5),
        leading=11, spaceAfter=4, spaceBefore=4)

    s['tbl_hdr'] = ParagraphStyle('tbl_hdr',
        fontName='Helvetica-Bold', fontSize=7.5, textColor=WHITE,
        leading=10, alignment=TA_CENTER)

    s['tbl_cell'] = ParagraphStyle('tbl_cell',
        fontName='Helvetica', fontSize=7.5, textColor=BLACK,
        leading=10)

    s['tbl_cell_bold'] = ParagraphStyle('tbl_cell_bold',
        fontName='Helvetica-Bold', fontSize=7.5, textColor=DARK_BLUE,
        leading=10)

    s['footer'] = ParagraphStyle('footer',
        fontName='Helvetica-Oblique', fontSize=6.5, textColor=colors.HexColor('#aaaaaa'),
        leading=9, alignment=TA_CENTER)

    s['page_num'] = ParagraphStyle('page_num',
        fontName='Helvetica-Bold', fontSize=7, textColor=NAVY,
        alignment=TA_LEFT)

    return s

S = make_styles()

FOOTER_TEXT = "Sources: Bailey & Love · Sleisenger & Fordtran · Kumar & Clark · Oxford Handbook of Clinical Medicine"

# ── Page canvas callback ──────────────────────────────────────────────────────
class MyCanvas(pdfgen_canvas.Canvas):
    def __init__(self, filename, **kwargs):
        super().__init__(filename, **kwargs)
        self._saved_page_states = []
        self._page_number = 0

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def save(self):
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self._draw_page_decorations(num_pages)
            super().showPage()
        super().save()

    def _draw_page_decorations(self, total_pages):
        self._page_number += 1
        pg = self._page_number
        c = self

        # ── Header bar ──
        c.setFillColor(NAVY)
        c.rect(0, PAGE_H - HDR_H, PAGE_W, HDR_H, fill=1, stroke=0)

        # Gold accent line under header
        c.setFillColor(GOLD)
        c.rect(0, PAGE_H - HDR_H - 1.5, PAGE_W, 1.5, fill=1, stroke=0)

        # Page number in header (left)
        c.setFillColor(GOLD)
        c.setFont('Helvetica-Bold', 8)
        c.drawString(MARGIN_L, PAGE_H - 10*mm, f"Page {pg}")

        # Topic title in header (centre)
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 11)
        if pg <= 6:
            c.drawCentredString(PAGE_W/2, PAGE_H - 8.5*mm, "CONSTIPATION & DIARRHOEA")
            c.setFont('Helvetica', 7.5)
            c.setFillColor(colors.HexColor('#a0bcd8'))
            c.drawCentredString(PAGE_W/2, PAGE_H - 13.5*mm, "Clinical OSCE Notes  |  Final Year MBBS")
        else:
            c.drawCentredString(PAGE_W/2, PAGE_H - 8.5*mm, "CONSTIPATION & DIARRHOEA")
            c.setFont('Helvetica', 7.5)
            c.setFillColor(colors.HexColor('#a0bcd8'))
            c.drawCentredString(PAGE_W/2, PAGE_H - 13.5*mm, "Clinical OSCE Notes  |  Final Year MBBS")

        # "n LEFT / RIGHT n" markers
        c.setFillColor(GOLD)
        c.setFont('Helvetica-Bold', 8)
        c.drawString(MARGIN_L, PAGE_H - HDR_H - 8, "◀ LEFT")
        c.drawRightString(PAGE_W - MARGIN_R, PAGE_H - HDR_H - 8, "RIGHT ▶")

        # ── Footer bar ──
        c.setFillColor(NAVY)
        c.rect(0, 0, PAGE_W, FTR_H + 3, fill=1, stroke=0)
        c.setFillColor(GOLD)
        c.rect(0, FTR_H + 3, PAGE_W, 1, fill=1, stroke=0)
        c.setFillColor(colors.HexColor('#aaaaaa'))
        c.setFont('Helvetica-Oblique', 6)
        c.drawCentredString(PAGE_W/2, 4*mm, FOOTER_TEXT)

        # ── Column divider ──
        c.setStrokeColor(GREY_MID)
        c.setLineWidth(0.5)
        mid_x = MARGIN_L + COL_W + COL_GAP/2
        top_y = PAGE_H - HDR_H - 12
        bot_y = FTR_H + 6
        c.line(mid_x, top_y, mid_x, bot_y)


# ── Helper builders ───────────────────────────────────────────────────────────
def sec(text, style='sec_head'):
    return Paragraph(f"<b>{text}</b>", S[style])

def subsec(text):
    return Paragraph(text, S['subsec'])

def body(text):
    return Paragraph(text, S['body'])

def bbody(text):
    return Paragraph(text, S['body_bold'])

def bull(text, bold=False):
    key = 'bullet_bold' if bold else 'bullet'
    return Paragraph(f"• {text}", S[key])

def pearl(text):
    return Paragraph(f"★ OSCE PEARL: {text}", S['pearl'])

def red(text):
    return Paragraph(f"⚠ RED FLAG: {text}", S['red_flag'])

def green(text):
    return Paragraph(text, S['green_box'])

def sp(h=3):
    return Spacer(1, h)

def hr():
    return HRFlowable(width="100%", thickness=0.5, color=GREY_MID, spaceAfter=3, spaceBefore=2)

def tbl(data, col_widths, hdr_bg=DARK_BLUE, alt=True):
    """Build a styled table. First row = header."""
    style = [
        ('BACKGROUND', (0,0), (-1,0), hdr_bg),
        ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
        ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
        ('FONTSIZE',   (0,0), (-1,-1), 7.5),
        ('ROWBACKGROUNDS', (0,1), (-1,-1),
            [GREY_LIGHT, WHITE] if alt else [WHITE]),
        ('GRID', (0,0), (-1,-1), 0.4, GREY_MID),
        ('LEFTPADDING',  (0,0), (-1,-1), 4),
        ('RIGHTPADDING', (0,0), (-1,-1), 4),
        ('TOPPADDING',   (0,0), (-1,-1), 3),
        ('BOTTOMPADDING',(0,0), (-1,-1), 3),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
        ('LEADING', (0,0), (-1,-1), 10),
    ]
    # wrap cells as Paragraphs if not already
    wrapped = []
    for r, row in enumerate(data):
        wrow = []
        for c, cell in enumerate(row):
            if isinstance(cell, str):
                st = S['tbl_hdr'] if r==0 else S['tbl_cell']
                wrow.append(Paragraph(cell, st))
            else:
                wrow.append(cell)
        wrapped.append(wrow)
    return Table(wrapped, colWidths=col_widths, style=TableStyle(style), hAlign='LEFT')

def tbl_cell(text, bold=False):
    return Paragraph(text, S['tbl_cell_bold'] if bold else S['tbl_cell'])

# ── Build two-column frame document ──────────────────────────────────────────
def build():
    out = '/home/daytona/workspace/osce-booklets/Constipation_Diarrhoea_OSCE_Booklet.pdf'

    top_y = PAGE_H - HDR_H - 14
    bot_y = FTR_H + 8
    frame_h = top_y - bot_y

    # Cover page frame (single, full width)
    cover_frame = Frame(MARGIN_L, bot_y, USABLE_W, frame_h,
                        leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0)

    # Two-column frames
    left_frame = Frame(MARGIN_L, bot_y, COL_W, frame_h,
                       leftPadding=0, rightPadding=3, topPadding=0, bottomPadding=0)
    right_frame = Frame(MARGIN_L + COL_W + COL_GAP, bot_y, COL_W, frame_h,
                        leftPadding=3, rightPadding=0, topPadding=0, bottomPadding=0)

    cover_tmpl  = PageTemplate(id='cover',  frames=[cover_frame])
    twocol_tmpl = PageTemplate(id='twocol', frames=[left_frame, right_frame])

    doc = BaseDocTemplate(out, pagesize=A4,
                          leftMargin=MARGIN_L, rightMargin=MARGIN_R,
                          topMargin=MARGIN_T, bottomMargin=MARGIN_B)
    doc.addPageTemplates([cover_tmpl, twocol_tmpl])

    story = []

    # ═══════════════════════════════════════════════════════
    # PAGE 1 — COVER
    # ═══════════════════════════════════════════════════════
    story.append(NextPageTemplate('cover'))

    story.append(Spacer(1, 30*mm))
    story.append(Paragraph("CONSTIPATION &amp; DIARRHOEA", S['title_main']))
    story.append(Spacer(1, 3))
    story.append(Paragraph("Clinical OSCE Notes  |  Final Year MBBS", S['title_sub']))
    story.append(Spacer(1, 6))
    story.append(Paragraph(
        "History Taking · Examination · Investigations · Management<br/>"
        "Causes · Clinical Correlations · OSCE Tips · Rapid-Fire Q&amp;A",
        S['title_tags']))
    story.append(Spacer(1, 8))
    story.append(HRFlowable(width="80%", thickness=1.5, color=GOLD,
                             spaceAfter=8, hAlign='CENTER'))
    story.append(Paragraph(
        "Sources: Bailey &amp; Love · Sleisenger &amp; Fordtran · Kumar &amp; Clark · Oxford Handbook of Clinical Medicine",
        S['footer']))

    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # PAGES 2-5 — CONSTIPATION
    # ═══════════════════════════════════════════════════════
    story.append(NextPageTemplate('twocol'))

    # ── LEFT COL PAGE 2 ──────────────────────────────────
    story.append(sec("CONSTIPATION — DEFINITION & CAUSES"))
    story.append(body("<b>Definition:</b> &lt;3 spontaneous bowel movements per week, OR straining, hard/lumpy stools, "
                      "sensation of incomplete evacuation, or manual manoeuvres needed for &gt;25% of defaecations "
                      "(Rome IV criteria)."))
    story.append(sp(4))

    story.append(subsec("CAUSES — 'SPINE' Mnemonic"))
    causes_data = [
        ["Category", "Examples"],
        ["S — Structural", "Colorectal Ca, diverticular disease, anal fissure, stricture, pelvic organ prolapse, rectocoele"],
        ["P — Physiological / Dietary", "Low fibre intake, inadequate fluid, immobility, IBS-C, slow transit constipation"],
        ["I — Iatrogenic (Drugs)", "Opioids, antacids (Al/Ca), iron, anticholinergics, CCBs (verapamil), antidepressants (TCAs)"],
        ["N — Neurological", "Parkinson's disease, multiple sclerosis, spinal cord injury, autonomic neuropathy, Hirschsprung's"],
        ["E — Endocrine / Metabolic", "Hypothyroidism, hypercalcaemia, hypokalaemia, diabetes, pregnancy, uraemia"],
    ]
    story.append(tbl(causes_data, [COL_W*0.28, COL_W*0.72]))
    story.append(sp(4))

    story.append(pearl("Hypothyroidism and hypercalcaemia are the two most exam-tested metabolic causes. "
                       "Always check TFTs and serum Ca2+ in unexplained constipation."))
    story.append(sp(4))

    story.append(subsec("PRIMARY vs SECONDARY"))
    story.append(bull("<b>Primary (functional):</b> No identifiable structural/metabolic cause — slow transit or pelvic floor dysfunction"))
    story.append(bull("<b>Secondary:</b> Underlying cause identified — treat the cause"))
    story.append(bull("<b>IBS-C:</b> Constipation + abdominal pain relieved by defaecation, no alarm features, normal investigations"))
    story.append(sp(3))
    story.append(red("ALARM FEATURES in constipation → urgent colonoscopy: Age >50 with new onset, rectal bleeding, "
                     "unintentional weight loss, iron-deficiency anaemia, family Hx CRC, palpable mass, change in bowel habit "
                     "lasting >6 weeks."))

    # ── RIGHT COL PAGE 2 — History Taking ───────────────
    story.append(sec("HISTORY TAKING — CONSTIPATION"))
    story.append(body("<b>Opening:</b> 'Can you describe your bowel habit? How often do you open your bowels?'"))
    story.append(sp(3))

    story.append(subsec("1. CHARACTER OF CONSTIPATION"))
    hx1_data = [
        ["Ask This", "Clinical Meaning"],
        ["Frequency (how many/week?)", "< 3/week = constipation by Rome IV"],
        ["Stool consistency (Bristol 1-2?)", "Hard lumpy stools — slow transit"],
        ["Straining / incomplete evacuation?", "Pelvic floor dysfunction / dyssynergia"],
        ["Manual evacuation needed?", "Severe pelvic floor dysfunction, rectocoele"],
        ["Duration — acute vs chronic?", "Acute: obstruction/Ca; Chronic: IBS/functional"],
        ["Alternating with diarrhoea?", "IBS, colorectal Ca, overflow diarrhoea (overflow around impaction)"],
        ["Blood / mucus in stool?", "ALARM — Ca, IBD, haemorrhoids"],
    ]
    story.append(tbl(hx1_data, [COL_W*0.47, COL_W*0.53]))
    story.append(sp(4))

    story.append(subsec("2. ASSOCIATED SYMPTOMS"))
    assoc_data = [
        ["Symptom", "Think of..."],
        ["Abdominal pain relieved by defaecation", "IBS-C"],
        ["Weight loss + rectal bleeding", "Colorectal carcinoma"],
        ["Bloating, ribbony stools, tenesmus", "Rectal/sigmoid Ca"],
        ["Fatigue + cold intolerance + weight gain", "Hypothyroidism"],
        ["Polyuria, polydipsia, bone pain", "Hypercalcaemia (Ca²⁺ ↑)"],
        ["Nausea + vomiting + absolute constipation", "Bowel obstruction"],
        ["Perianal pain + bleeding on wiping", "Anal fissure → fear of defaecation"],
    ]
    story.append(tbl(assoc_data, [COL_W*0.52, COL_W*0.48]))
    story.append(sp(4))

    story.append(subsec("3. DRUG HISTORY — HIGH-RISK DRUGS"))
    story.append(bull("<b>Opioids</b> (codeine, morphine) → #1 drug cause — reduce gut peristalsis"))
    story.append(bull("<b>Antacids</b> (Al(OH)₃, CaCO₃) → most common OTC cause"))
    story.append(bull("<b>Iron tablets</b> → very common, also darken stools"))
    story.append(bull("<b>Anticholinergics</b> (TCAs, antihistamines, antipsychotics) → reduce gut motility"))
    story.append(bull("<b>CCBs</b> (verapamil > amlodipine) → inhibit smooth muscle"))
    story.append(bull("<b>Ondansetron / 5-HT₃ antagonists</b> → reduce peristalsis"))
    story.append(sp(3))
    story.append(pearl("In any patient on opioids with constipation — first-line is osmotic laxative (macrogol/lactulose). "
                       "Docusate alone is insufficient. Consider methylnaltrexone for opioid-induced constipation refractory to laxatives."))

    story.append(PageBreak())

    # ── PAGE 3 — LEFT ────────────────────────────────────
    story.append(sec("HISTORY TAKING — CONSTIPATION (CONT.)"))
    story.append(subsec("4. SOCIAL HISTORY"))
    story.append(bull("<b>Diet:</b> Fibre intake (target 25–30 g/day), fluid intake (1.5–2 L/day)"))
    story.append(bull("<b>Mobility:</b> Bed-bound / institutionalised → ↓ peristalsis"))
    story.append(bull("<b>Occupation/stress:</b> IBS association with psychological stress"))
    story.append(bull("<b>Pregnancy:</b> Progesterone reduces gut motility + iron supplements"))
    story.append(bull("<b>Laxative use:</b> Long-term stimulant laxative → atonic colon"))
    story.append(sp(4))

    story.append(subsec("5. PAST MEDICAL HISTORY"))
    story.append(bull("Previous colorectal surgery / hysterectomy → adhesions, nerve damage"))
    story.append(bull("Known IBD / diverticular disease / CRC"))
    story.append(bull("Hypothyroidism / diabetes / renal disease"))
    story.append(bull("Depression / eating disorders"))
    story.append(bull("Childhood constipation → Hirschsprung's (no ganglia)"))
    story.append(sp(4))

    story.append(sec("CLINICAL EXAMINATION — CONSTIPATION", 'sec_head_teal'))
    story.append(subsec("A. GENERAL"))
    story.append(bull("Weight / BMI: weight loss → alarm; weight gain → hypothyroidism"))
    story.append(bull("Pallor → iron-deficiency anaemia from occult blood loss"))
    story.append(bull("Thyroid signs: goitre, bradycardia, dry skin, periorbital puffiness"))
    story.append(bull("Signs of hypercalcaemia: 'Bones, Stones, Groans, Moans'"))
    story.append(sp(4))

    story.append(subsec("B. ABDOMINAL EXAMINATION"))
    exam_data = [
        ["Finding", "Interpretation"],
        ["Distension", "Obstruction, ileus, loaded colon"],
        ["Palpable mass LIF", "Loaded sigmoid / faecal impaction"],
        ["Palpable mass — firm, mobile", "Faecal mass (indents with pressure, unlike Ca)"],
        ["Palpable mass — hard, fixed", "Colorectal carcinoma until proven otherwise"],
        ["Diffuse tenderness", "Constipation, IBD, obstruction"],
        ["Tympany on percussion", "Gas-filled loops — obstruction"],
        ["Absent bowel sounds", "Ileus / complete obstruction"],
    ]
    story.append(tbl(exam_data, [COL_W*0.45, COL_W*0.55]))
    story.append(sp(4))

    story.append(subsec("C. PERIANAL & RECTAL EXAMINATION"))
    story.append(bull("Inspect: haemorrhoids, fissure (posterior midline), skin tags, prolapse"))
    story.append(bull("DRE: hard faecal matter in rectum → impaction; empty rectum + constipation → slow transit"))
    story.append(bull("Anal tone: ↓ tone → prolapse, neurological cause; ↑ tone → anal fissure / dyssynergia"))
    story.append(bull("Balloon expulsion test (specialist): prolonged expulsion → pelvic floor dyssynergia"))
    story.append(sp(3))
    story.append(pearl("OSCE TIP: Always offer to perform PR examination. An empty rectum in constipation points "
                       "to slow transit; a loaded rectum points to outlet dysfunction or impaction."))

    # ── PAGE 3 RIGHT ─────────────────────────────────────
    story.append(sec("INVESTIGATIONS — CONSTIPATION"))
    story.append(subsec("FIRST-LINE BLOODS"))
    inv1_data = [
        ["Test", "What You're Looking For"],
        ["FBC", "Anaemia (IDA) → occult bleed / Ca"],
        ["U&E", "K⁺ ↓ → hypokalaemia; Urea ↑ → dehydration"],
        ["Serum Ca²⁺", "Hypercalcaemia → constipation"],
        ["TFTs (TSH/T4)", "Hypothyroidism"],
        ["Glucose / HbA1c", "Diabetic autonomic neuropathy"],
        ["CRP / ESR", "↑ in IBD, Ca, infection"],
        ["CEA", "Raised in colorectal Ca (not diagnostic alone)"],
    ]
    story.append(tbl(inv1_data, [COL_W*0.42, COL_W*0.58]))
    story.append(sp(4))

    story.append(subsec("IMAGING & ENDOSCOPY"))
    inv2_data = [
        ["Investigation", "When to Use", "What It Shows"],
        ["AXR (plain film)", "Acute obstruction / suspected impaction", "Faecal loading, dilated loops, obstruction pattern"],
        ["Colonoscopy / sigmoidoscopy", "Alarm features, age >50, new onset", "Polyps, Ca, IBD, diverticulosis"],
        ["CT colonography", "Unable to tolerate scope", "Same as colonoscopy — non-invasive"],
        ["Colonic transit study", "Chronic constipation — exclude slow transit", "Radiopaque markers track through colon (normal <5 days)"],
        ["Anorectal manometry", "Suspected pelvic floor dysfunction", "Sphincter tone, rectal sensation, dyssynergia"],
        ["Defaecating proctogram", "Outlet dysfunction (straining/incomplete evacuation)", "Rectocoele, intussusception, paradoxical contraction"],
        ["MRI pelvis", "Complex pelvic floor", "Structural and functional anatomy"],
    ]
    story.append(tbl(inv2_data, [COL_W*0.3, COL_W*0.35, COL_W*0.35]))
    story.append(sp(4))

    story.append(pearl("The most important initial investigation in a patient >50 with new-onset constipation "
                       "and alarm features is colonoscopy — to exclude colorectal carcinoma."))
    story.append(sp(4))

    story.append(subsec("ROME IV CRITERIA FOR FUNCTIONAL CONSTIPATION"))
    story.append(body("≥2 of the following for last 3 months (onset ≥6 months ago):"))
    story.append(bull("Straining in >25% of defaecations"))
    story.append(bull("Lumpy/hard stools (Bristol 1-2) in >25%"))
    story.append(bull("Sensation of incomplete evacuation in >25%"))
    story.append(bull("Sensation of anorectal obstruction in >25%"))
    story.append(bull("Manual manoeuvres needed in >25%"))
    story.append(bull("<3 spontaneous bowel movements per week"))
    story.append(body("<i>PLUS: Loose stools rarely present without laxatives. Insufficient criteria for IBS.</i>"))

    story.append(PageBreak())

    # ── PAGE 4 — MANAGEMENT CONSTIPATION ─────────────────
    story.append(sec("MANAGEMENT — CONSTIPATION"))
    story.append(subsec("STEP 1 — LIFESTYLE MODIFICATIONS (ALWAYS FIRST)"))
    story.append(bull("<b>Dietary fibre:</b> Increase to 25–30 g/day (fruits, veg, wholegrain, bran)"))
    story.append(bull("<b>Fluid intake:</b> 1.5–2 L/day — critical for fibre to work"))
    story.append(bull("<b>Exercise:</b> Regular physical activity stimulates colonic motility"))
    story.append(bull("<b>Toilet routine:</b> Regular time after meals (gastrocolic reflex); don't ignore urge"))
    story.append(bull("<b>Review medications:</b> Stop/switch constipating drugs where possible"))
    story.append(sp(4))

    story.append(subsec("STEP 2 — LAXATIVES"))
    lax_data = [
        ["Laxative Class", "Drug", "Mechanism", "Use / Notes"],
        ["Bulk-forming (1st line)", "Ispaghula husk (Fybogel)", "↑ stool volume → stimulates peristalsis", "Take with ≥200 mL water; takes 2–3 days"],
        ["Osmotic", "Macrogol (Movicol) / Lactulose", "Draw water into bowel by osmosis", "Macrogol preferred; lactulose causes bloating"],
        ["Stimulant", "Senna / Bisacodyl", "Stimulate enteric nerves → peristalsis", "Short-term; avoid long-term (atonic colon)"],
        ["Stool softener", "Docusate sodium", "Detergent — softens stool surface", "Mild; useful in anal fissure / post-op"],
        ["Osmotic (acute)", "Sodium picosulfate / Phosphate enema", "Local stimulant / osmotic", "Pre-procedure or faecal impaction"],
        ["Secretagogue", "Linaclotide / Lubiprostone", "↑ intestinal Cl⁻ secretion → stool fluidity", "IBS-C / chronic idiopathic constipation"],
    ]
    story.append(tbl(lax_data, [COL_W*0.25, COL_W*0.2, COL_W*0.3, COL_W*0.25]))
    story.append(sp(4))
    story.append(pearl("OSCE ORDER: Bulk-forming → Osmotic → Stimulant. Always start with macrogol in "
                       "opioid-induced constipation (osmotic) — NOT bulk-forming alone."))

    # ── PAGE 4 RIGHT ─────────────────────────────────────
    story.append(sec("MANAGEMENT — CONSTIPATION (CONT.)"))
    story.append(subsec("STEP 3 — TREATMENT OF SPECIFIC CAUSES"))
    spec_data = [
        ["Cause", "Specific Treatment"],
        ["Hypothyroidism", "Levothyroxine replacement → constipation resolves with euthyroidism"],
        ["Hypercalcaemia", "IV fluids + bisphosphonate → treat underlying cause"],
        ["Opioid-induced", "Macrogol + stimulant laxative; methylnaltrexone if refractory"],
        ["Anal fissure", "GTN 0.4% cream / diltiazem 2% → relax sphincter; botox if fails"],
        ["Hirschsprung's disease", "Surgical resection of aganglionic segment"],
        ["Slow transit constipation", "Prucalopride (5-HT₄ agonist) — prokinetic for chronic constipation"],
        ["Pelvic floor dyssynergia", "Biofeedback therapy (first-line) — retraining defaecation"],
        ["Rectocoele", "Pelvic floor physiotherapy; surgery if symptomatic and large"],
        ["Faecal impaction", "Manual evacuation + phosphate enemas + macrogol disimpaction regimen"],
    ]
    story.append(tbl(spec_data, [COL_W*0.38, COL_W*0.62]))
    story.append(sp(4))

    story.append(subsec("STEP 4 — SURGICAL OPTIONS"))
    story.append(bull("<b>Colectomy + ileorectal anastomosis:</b> Slow transit constipation refractory to all medical therapy"))
    story.append(bull("<b>Anterior resection / Hartmann's:</b> Colorectal carcinoma"))
    story.append(bull("<b>Sacral nerve stimulation:</b> Pelvic floor dysfunction unresponsive to biofeedback"))
    story.append(sp(4))

    story.append(sec("SPECIAL SITUATIONS", 'sec_head_teal'))
    story.append(subsec("CONSTIPATION IN THE ELDERLY"))
    story.append(bull("Most common cause: polypharmacy + immobility + dehydration + low fibre"))
    story.append(bull("Risk of faecal impaction → overflow diarrhoea (DO NOT treat with antidiarrhoeals)"))
    story.append(bull("PR exam essential — hard stool in rectum = impaction"))
    story.append(sp(3))

    story.append(subsec("CONSTIPATION IN PREGNANCY"))
    story.append(bull("Progesterone slows gut motility; iron supplements compound this"))
    story.append(bull("Safe: Ispaghula, lactulose, macrogol"))
    story.append(bull("Avoid: Stimulant laxatives (senna) in 1st trimester; mineral oils"))
    story.append(sp(3))

    story.append(subsec("HIRSCHSPRUNG'S DISEASE"))
    story.append(bull("Congenital absence of ganglion cells (Meissner's + Auerbach's plexus) in distal colon"))
    story.append(bull("Presents: neonatal failure to pass meconium within 48 hrs; childhood chronic constipation"))
    story.append(bull("Diagnosis: Suction rectal biopsy (no ganglion cells)"))
    story.append(bull("Treatment: Surgical pull-through (Swenson / Soave procedure)"))

    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # PAGE 5 — CONSTIPATION OSCE VIVA + TRANSITION
    # ═══════════════════════════════════════════════════════
    story.append(sec("CONSTIPATION — OSCE VIVA Q&A"))
    viva_data = [
        ["Examiner Asks...", "Your Answer"],
        ["Define constipation (Rome IV)", "<3 BM/week or ≥2 of: straining, hard stools, incomplete evacuation, manual manoeuvres, in >25% of defaecations"],
        ["Most common drug cause?", "Opioids — reduce peristalsis; also iron, antacids (Al/Ca), TCAs, verapamil"],
        ["What metabolic causes must you exclude?", "Hypothyroidism (TSH), hypercalcaemia (Ca²⁺), hypokalaemia, diabetes"],
        ["First investigation in new constipation >50 yrs?", "FBC, TFTs, Ca²⁺, then colonoscopy if alarm features present"],
        ["What does an empty rectum on DRE tell you?", "Slow transit constipation — stool not reaching rectum"],
        ["What does a loaded rectum on DRE tell you?", "Outlet dysfunction / faecal impaction / functional constipation"],
        ["Overflow diarrhoea — what is it?", "Liquid stool leaking around a faecal impaction — do NOT give antidiarrhoeals"],
        ["Best laxative for opioid-induced constipation?", "Osmotic (macrogol) ± stimulant laxative; methylnaltrexone if refractory"],
        ["Prucalopride — what is it?", "5-HT₄ agonist → prokinetic → for chronic idiopathic constipation in women"],
        ["Treatment of pelvic floor dyssynergia?", "Biofeedback therapy — first-line; surgery rarely needed"],
        ["Hirschsprung's diagnosis method?", "Suction rectal biopsy — absence of ganglion cells in Meissner's plexus"],
    ]
    story.append(tbl(viva_data, [COL_W*0.42, COL_W*0.58]))

    # ── PAGE 5 RIGHT ─────────────────────────────────────
    story.append(sec("CONSTIPATION MNEMONICS"))
    story.append(subsec("CAUSES — 'SPINE'"))
    mnem_data = [
        ["Letter", "Cause", "Key Example"],
        ["S", "Structural", "CRC, anal fissure, rectocoele, stricture"],
        ["P", "Physiological/Dietary", "Low fibre, IBS-C, slow transit, immobility"],
        ["I", "Iatrogenic (Drugs)", "Opioids, iron, Al antacids, TCAs, verapamil"],
        ["N", "Neurological", "Parkinson's, MS, spinal injury, Hirschsprung's"],
        ["E", "Endocrine/Metabolic", "Hypothyroid, hypercalcaemia, hypokalaemia"],
    ]
    story.append(tbl(mnem_data, [COL_W*0.1, COL_W*0.3, COL_W*0.6], hdr_bg=TEAL))
    story.append(sp(5))

    story.append(subsec("ALARM FEATURES — 'ABCDE'"))
    alarm_data = [
        ["Letter", "Feature"],
        ["A", "Anaemia (iron-deficiency) — check FBC"],
        ["B", "Bleeding per rectum"],
        ["C", "Change in bowel habit >6 weeks (new)"],
        ["D", "Duration sudden/short + age >50"],
        ["E", "Extra findings: weight loss, palpable mass, family Hx CRC"],
    ]
    story.append(tbl(alarm_data, [COL_W*0.1, COL_W*0.9], hdr_bg=RED_BORDER))
    story.append(sp(5))

    story.append(subsec("BRISTOL STOOL CHART — OSCE ESSENTIALS"))
    bsc_data = [
        ["Type", "Appearance", "Transit / Meaning"],
        ["1", "Separate hard lumps (nuts)", "Very slow transit — constipation"],
        ["2", "Lumpy sausage-shaped", "Slow transit"],
        ["3", "Cracked sausage", "Normal to slow"],
        ["4", "Smooth sausage / snake", "IDEAL — normal"],
        ["5", "Soft blobs, clear edges", "Tending towards loose"],
        ["6", "Fluffy/mushy, ragged edges", "Mild diarrhoea"],
        ["7", "Watery, no solid pieces", "Diarrhoea — rapid transit"],
    ]
    story.append(tbl(bsc_data, [COL_W*0.12, COL_W*0.45, COL_W*0.43], hdr_bg=DARK_BLUE))
    story.append(sp(4))
    story.append(pearl("Bristol 1-2 = constipation. Bristol 6-7 = diarrhoea. Ask the patient to describe using the chart in OSCE."))

    story.append(PageBreak())

    # ═══════════════════════════════════════════════════════
    # PAGES 6–9 — DIARRHOEA
    # ═══════════════════════════════════════════════════════
    story.append(sec("DIARRHOEA — DEFINITION & CLASSIFICATION"))
    story.append(body("<b>Definition:</b> Passage of ≥3 loose/watery stools per day, or stool volume >200 mL/day, "
                      "or stool weight >200 g/day."))
    story.append(sp(3))

    story.append(subsec("CLASSIFICATION BY DURATION"))
    dur_data = [
        ["Type", "Duration", "Most Common Causes"],
        ["Acute", "<2 weeks", "Infective gastroenteritis (viral/bacterial), food poisoning, traveller's diarrhoea"],
        ["Persistent", "2–4 weeks", "Continuing infection (Giardia, C. diff), early IBD"],
        ["Chronic", ">4 weeks", "IBD, IBS-D, malabsorption (coeliac), colorectal Ca, endocrine"],
    ]
    story.append(tbl(dur_data, [COL_W*0.18, COL_W*0.2, COL_W*0.62]))
    story.append(sp(4))

    story.append(subsec("CLASSIFICATION BY MECHANISM — 'SOME'"))
    mech_data = [
        ["Mechanism", "Pathophysiology", "Example Causes"],
        ["S — Secretory", "Active Cl⁻/Na⁺ secretion into lumen (↑ cAMP/cGMP) → large volume watery stools; PERSISTS on fasting", "Cholera, VIPoma, carcinoid, bile salt malabsorption"],
        ["O — Osmotic", "Unabsorbed solutes draw water into lumen; STOPS on fasting", "Lactose intolerance, coeliac, laxative abuse, osmotic laxatives"],
        ["M — Motility", "Altered transit time → inadequate absorption", "IBS-D, hyperthyroidism, diabetic autonomic neuropathy, post-vagotomy"],
        ["E — Exudative/ Inflammatory", "Mucosal damage → blood/pus/mucus in stool (dysentery pattern)", "IBD (Crohn's/UC), Shigella, C. diff, ischaemic colitis, colorectal Ca"],
    ]
    story.append(tbl(mech_data, [COL_W*0.22, COL_W*0.42, COL_W*0.36]))
    story.append(sp(4))
    story.append(pearl("Key OSCE distinction: Secretory diarrhoea continues fasting; osmotic diarrhoea stops. "
                       "Large volume = secretory/proximal; small volume + urgency/blood = inflammatory/distal."))

    # RIGHT COL PAGE 6
    story.append(sec("CAUSES OF DIARRHOEA — COMPREHENSIVE"))
    story.append(subsec("INFECTIVE CAUSES"))
    inf_data = [
        ["Organism", "Type", "Clinical Clue"],
        ["Rotavirus / Norovirus", "Viral", "Winter vomiting — outbreaks, self-limiting, watery"],
        ["Adenovirus", "Viral", "Children, respiratory + GI symptoms"],
        ["E. coli (ETEC)", "Bacterial", "Traveller's diarrhoea — watery, no blood"],
        ["E. coli O157 (EHEC)", "Bacterial", "HUS risk — bloody diarrhoea + renal failure"],
        ["Salmonella", "Bacterial", "Poultry/eggs, fever, watery then bloody"],
        ["Shigella", "Bacterial", "Dysentery — bloody + mucus + fever + tenesmus"],
        ["Campylobacter", "Bacterial", "Most common bacterial cause UK; poultry, self-limiting"],
        ["C. difficile", "Bacterial (toxin)", "Post-antibiotics; profuse watery; hospital-acquired"],
        ["Vibrio cholerae", "Bacterial", "Rice-water stools — massive secretory"],
        ["Giardia lamblia", "Protozoal", "Steatorrhoea — malabsorption; contaminated water"],
        ["Entamoeba histolytica", "Protozoal", "Amoebic dysentery — bloody; liver abscess risk"],
        ["Cryptosporidium", "Protozoal", "Immunocompromised (HIV); prolonged watery"],
    ]
    story.append(tbl(inf_data, [COL_W*0.3, COL_W*0.2, COL_W*0.5]))
    story.append(sp(4))

    story.append(subsec("NON-INFECTIVE CAUSES"))
    noninf_data = [
        ["Category", "Examples"],
        ["Inflammatory (IBD)", "Crohn's disease, Ulcerative colitis"],
        ["Malabsorption", "Coeliac disease, chronic pancreatitis, short bowel syndrome, lactose intolerance"],
        ["Drugs", "Antibiotics, laxative abuse, metformin, SSRIs, PPIs, colchicine, chemotherapy"],
        ["Endocrine", "Hyperthyroidism, carcinoid syndrome (5-HT), VIPoma (watery), Addison's, Zollinger-Ellison"],
        ["Colorectal pathology", "CRC (right-sided → liquid → anaemia), diverticular disease, ischaemic colitis"],
        ["Functional", "IBS-D (Rome IV criteria — no alarm features, bloating, pain relieved by defaecation)"],
        ["Post-surgical", "Post-cholecystectomy (bile salt), dumping syndrome, short gut, vagotomy"],
    ]
    story.append(tbl(noninf_data, [COL_W*0.3, COL_W*0.7]))

    story.append(PageBreak())

    # PAGE 7 — HISTORY + EXAM ─────────────────────────────
    story.append(sec("HISTORY TAKING — DIARRHOEA"))
    story.append(body("<b>Opening:</b> 'Can you tell me about your bowel habit? How many times a day? How long has this been happening?'"))
    story.append(sp(3))

    story.append(subsec("1. CHARACTER OF DIARRHOEA"))
    char_data = [
        ["Ask This", "Clinical Meaning"],
        ["Onset + duration (acute/chronic?)", "Acute <2 wks = infective; Chronic >4 wks = structural/functional"],
        ["Frequency (how many per day?)", ">3/day = diarrhoea; nocturnal = organic (not IBS)"],
        ["Volume — large or small?", "Large volume = secretory/proximal; small = distal/inflammatory"],
        ["Colour?", "Pale/greasy → steatorrhoea (malabsorption); green → bile"],
        ["Blood in stool?", "Bright red = lower GI; dark = higher source; mucus = IBD/Ca"],
        ["Watery vs mucoid?", "Watery = secretory/infective; mucoid + blood = inflammatory (IBD, Ca)"],
        ["Urgency / incontinence?", "Rectal inflammation (UC, Ca) → urgency; sphincter damage → incontinence"],
        ["Nocturnal diarrhoea?", "Organic cause — IBD, secretory; NOT IBS (IBS never nocturnal)"],
    ]
    story.append(tbl(char_data, [COL_W*0.42, COL_W*0.58]))
    story.append(sp(4))

    story.append(subsec("2. ASSOCIATED SYMPTOMS"))
    assoc2_data = [
        ["Symptom", "Diagnosis to Consider"],
        ["Abdominal pain + blood + mucus + urgency", "IBD (Crohn's / UC)"],
        ["Periumbilical pain → RIF + constitutional sx", "Crohn's disease"],
        ["Fever + travel history", "Traveller's diarrhoea / typhoid / amoebiasis"],
        ["Weight loss + night sweats", "IBD, TB, HIV, lymphoma, colorectal Ca"],
        ["Steatorrhoea + weight loss + bloating", "Coeliac, chronic pancreatitis, Giardia"],
        ["Palpitations + sweating + weight loss", "Hyperthyroidism"],
        ["Flushing + wheeze + diarrhoea", "Carcinoid syndrome (5-HIAA ↑)"],
        ["Post-antibiotic course", "C. difficile (check toxin EIA)"],
        ["Arthralgia + skin rash + diarrhoea", "IBD extra-intestinal (pyoderma, erythema nodosum, arthritis)"],
    ]
    story.append(tbl(assoc2_data, [COL_W*0.45, COL_W*0.55]))
    story.append(sp(4))
    story.append(red("ALARM FEATURES in diarrhoea → urgent 2WW referral: Age >50 with new bowel habit change, "
                     "rectal bleeding, anaemia, unintentional weight loss, palpable abdominal or rectal mass, positive faecal occult blood."))

    # PAGE 7 RIGHT ─────────────────────────────────────────
    story.append(subsec("3. DRUG HISTORY"))
    story.append(bull("<b>Antibiotics</b> (clindamycin, ciprofloxacin, ampicillin) → C. diff, dysbiosis"))
    story.append(bull("<b>Laxative abuse</b> → osmotic/secretory (factitious)"))
    story.append(bull("<b>Metformin</b> → GI side-effects in up to 30%"))
    story.append(bull("<b>NSAIDs</b> → colitis, gut inflammation"))
    story.append(bull("<b>Orlistat</b> → steatorrhoea by design"))
    story.append(bull("<b>Chemotherapy / radiotherapy</b> → mucosal damage"))
    story.append(bull("<b>Colchicine / magnesium antacids</b> → osmotic"))
    story.append(sp(4))

    story.append(subsec("4. SOCIAL & TRAVEL HISTORY"))
    story.append(bull("<b>Recent travel:</b> South Asia, Africa, SE Asia → Giardia, amoebiasis, typhoid, ETEC"))
    story.append(bull("<b>Food history:</b> Undercooked chicken → Salmonella/Campylobacter; shellfish → Norovirus"))
    story.append(bull("<b>Water source:</b> Untreated well water → Giardia/Cryptosporidium"))
    story.append(bull("<b>Contacts with same illness:</b> Outbreak → infective"))
    story.append(bull("<b>Sexual history:</b> MSM → Shigella, Herpes, LGV proctitis"))
    story.append(bull("<b>Immunosuppression:</b> HIV/transplant → Cryptosporidium, CMV colitis"))
    story.append(sp(4))

    story.append(sec("CLINICAL EXAMINATION — DIARRHOEA", 'sec_head_teal'))
    story.append(subsec("ASSESSMENT OF DEHYDRATION"))
    deh_data = [
        ["Grade", "Signs", "Management"],
        ["None (0%)", "Normal vitals, normal skin turgor, moist mucosa", "Oral fluids — maintain intake"],
        ["Mild (3–5%)", "Dry mouth, slightly ↓ skin turgor, thirst, mildly ↓ urine output", "Oral rehydration solution (ORS)"],
        ["Moderate (5–8%)", "Sunken eyes, poor skin turgor, tachycardia, ↓BP, dry mucosa, oliguria", "ORS + consider IV fluids"],
        ["Severe (>8%)", "Sunken eyes, mottled skin, HR >120, SBP <90, confusion, anuria", "IV Hartmann's / 0.9% NaCl + urgent fluid resus"],
    ]
    story.append(tbl(deh_data, [COL_W*0.2, COL_W*0.45, COL_W*0.35]))
    story.append(sp(4))
    story.append(pearl("In acute diarrhoea — check capillary refill, skin turgor, pulse, BP, and urine output. "
                       "Children: sunken fontanelle + sunken eyes = significant dehydration."))

    story.append(PageBreak())

    # PAGE 8 ─────────────────────────────────────────────
    story.append(sec("INVESTIGATIONS — DIARRHOEA"))
    story.append(subsec("ACUTE DIARRHOEA"))
    story.append(bull("<b>Stool MC&S</b> (Microscopy, Culture & Sensitivity): for bacterial pathogens"))
    story.append(bull("<b>Stool for C. diff toxin EIA</b>: if post-antibiotic or hospital-acquired"))
    story.append(bull("<b>Stool for ova, cysts & parasites</b>: if travel history or prolonged"))
    story.append(bull("<b>Norovirus/Rotavirus PCR</b>: in outbreaks"))
    story.append(sp(3))

    story.append(subsec("CHRONIC DIARRHOEA WORKUP"))
    chronic_inv = [
        ["Test", "What It Detects"],
        ["FBC", "Anaemia (IDA → CRC; macrocytic → coeliac B12 deficiency)"],
        ["CRP/ESR", "Raised in IBD, infective, Ca"],
        ["U&E", "K⁺ ↓ and Na⁺ ↓ in severe diarrhoea"],
        ["TFTs", "Hyperthyroidism → diarrhoea"],
        ["Coeliac serology (anti-tTG IgA)", "Coeliac disease — must be on gluten at time of test"],
        ["Faecal calprotectin", "↑ in IBD (>50 μg/g) — distinguishes IBD from IBS"],
        ["Faecal elastase", "↓ in exocrine pancreatic insufficiency"],
        ["Serum B12/folate/ferritin/Vit D", "Malabsorption screen"],
        ["Gut hormones (VIP, gastrin, 5-HIAA)", "VIPoma, ZES, carcinoid"],
        ["HIV test", "If risk factors — consider CMV, Crypto, MAI"],
        ["Colonoscopy + biopsy", "IBD, CRC, microscopic colitis"],
        ["OGD + duodenal biopsy", "Coeliac (villous atrophy), Giardia (trophozoites)"],
        ["CT abdomen/pelvis", "Mass lesions, lymphadenopathy, pancreatic Ca"],
        ["Hydrogen breath test", "Lactose intolerance, SIBO"],
    ]
    story.append(tbl(chronic_inv, [COL_W*0.4, COL_W*0.6]))
    story.append(sp(4))
    story.append(pearl("Faecal calprotectin is the key non-invasive test to distinguish IBD from IBS — "
                       "raised in IBD (mucosal inflammation), normal in IBS. Use before referring for colonoscopy."))

    # PAGE 8 RIGHT ────────────────────────────────────────
    story.append(sec("MANAGEMENT — DIARRHOEA"))
    story.append(subsec("STEP 1 — GENERAL SUPPORTIVE MEASURES"))
    story.append(bull("<b>Rehydration:</b> Oral rehydration solution (ORS) — first-line for mild-moderate"))
    story.append(bull("<b>IV fluids:</b> Hartmann's or 0.9% NaCl for severe dehydration"))
    story.append(bull("<b>Electrolyte replacement:</b> K⁺, Na⁺ monitoring + correction"))
    story.append(bull("<b>Diet:</b> Small frequent bland meals; avoid dairy initially in acute GE"))
    story.append(bull("<b>Hygiene:</b> Hand washing, isolation if infective, notify PHE if notifiable disease"))
    story.append(sp(4))

    story.append(subsec("STEP 2 — ANTIDIARRHOEAL AGENTS"))
    anti_data = [
        ["Drug", "Mechanism", "Use / Caution"],
        ["Loperamide", "Opioid receptor agonist → ↓ gut motility", "Acute diarrhoea — NOT in bloody/infective (risk of toxic megacolon in C. diff)"],
        ["Codeine phosphate", "Central + peripheral opioid", "Short-term; caution in chronic — dependency"],
        ["Bismuth subsalicylate", "Antimicrobial + antisecretory", "Traveller's diarrhoea prevention"],
        ["Cholestyramine", "Bile acid sequestrant", "Post-cholecystectomy / bile salt diarrhoea"],
        ["Octreotide", "Somatostatin analogue → ↓ secretion", "VIPoma, carcinoid, high-output fistula"],
    ]
    story.append(tbl(anti_data, [COL_W*0.25, COL_W*0.3, COL_W*0.45]))
    story.append(sp(4))
    story.append(red("NEVER give loperamide or antiperistaltic agents in suspected C. difficile, "
                     "E. coli O157 (HUS risk), or any bloody dysentery — risk of toxic megacolon."))
    story.append(sp(4))

    story.append(subsec("STEP 3 — SPECIFIC TREATMENT"))
    spec2_data = [
        ["Cause", "Treatment"],
        ["Viral gastroenteritis", "Supportive (ORS) — self-limiting; no antivirals"],
        ["Campylobacter", "Usually self-limiting; azithromycin if severe/immunocomp"],
        ["Salmonella (non-typhi)", "Usually supportive; ciprofloxacin if bacteraemic/immunocomp"],
        ["Shigella (dysentery)", "Ciprofloxacin or azithromycin — shorten course"],
        ["E. coli O157 EHEC", "Supportive ONLY — no antibiotics (↑ HUS risk); monitor renal function"],
        ["C. difficile (mild-mod)", "Oral vancomycin 125mg QID × 10 days (1st line)"],
        ["C. difficile (severe)", "Oral vancomycin 500mg QID + IV metronidazole; consider fidaxomicin"],
        ["Giardia", "Metronidazole 400mg TDS × 5–7 days"],
        ["Entamoeba histolytica", "Metronidazole + diloxanide furoate (luminal cyst)"],
        ["Cryptosporidium (HIV)", "Nitazoxanide; ART optimisation"],
        ["Coeliac disease", "Strict gluten-free diet (lifelong)"],
        ["IBD — UC", "Aminosalicylates (5-ASA); steroids for flare; biologics"],
        ["IBD — Crohn's", "Steroids / budesonide for flare; azathioprine; biologics (adalimumab)"],
        ["Carcinoid / VIPoma", "Octreotide + surgical resection"],
        ["Lactose intolerance", "Lactose-free diet; lactase enzyme supplements"],
    ]
    story.append(tbl(spec2_data, [COL_W*0.35, COL_W*0.65]))

    story.append(PageBreak())

    # PAGE 9 — RAPID FIRE + MNEMONICS ────────────────────
    story.append(sec("DIARRHOEA — OSCE VIVA Q&A"))
    dviva_data = [
        ["Examiner Asks...", "Your Answer"],
        ["Define diarrhoea", "≥3 loose stools/day or stool volume >200 mL/day"],
        ["Secretory vs osmotic diarrhoea — key difference?", "Secretory continues on fasting; osmotic stops on fasting"],
        ["What does nocturnal diarrhoea suggest?", "Organic cause (IBD, secretory) — IBS does NOT cause nocturnal symptoms"],
        ["Most common bacterial cause of diarrhoea in UK?", "Campylobacter jejuni"],
        ["Traveller's diarrhoea — commonest organism?", "ETEC (enterotoxigenic E. coli) — watery, no blood"],
        ["C. diff risk factors?", "Recent antibiotics (esp. clindamycin, amoxicillin, cephalosporins), hospitalisation, age >65, PPIs, immunosuppression"],
        ["C. diff 1st line treatment?", "Oral vancomycin 125mg QDS × 10 days (NOT metronidazole alone — 2021 NICE)"],
        ["E. coli O157 — why no antibiotics?", "Antibiotics increase Shiga toxin release → ↑ risk of HUS (haemolytic uraemic syndrome)"],
        ["Best test to distinguish IBD from IBS?", "Faecal calprotectin — raised in IBD (>50 μg/g); normal in IBS"],
        ["Patient with diarrhoea + flushing + wheeze. Diagnosis?", "Carcinoid syndrome — measure 24-hr urinary 5-HIAA; treat with octreotide"],
        ["Coeliac test — what must patient be doing?", "Eating gluten — anti-tTG IgA will be falsely negative on gluten-free diet"],
        ["Child with bloody diarrhoea + sudden oliguria. Diagnosis?", "HUS — Haemolytic Uraemic Syndrome (E. coli O157); triad: haemolytic anaemia + thrombocytopaenia + AKI"],
        ["Post-cholecystectomy diarrhoea — mechanism?", "Bile salt malabsorption → secretory; treat with cholestyramine"],
        ["What is Brainerd diarrhoea?", "Chronic secretory diarrhoea post-contaminated water/milk; self-limiting over 1-2 years; no organism found"],
    ]
    story.append(tbl(dviva_data, [COL_W*0.42, COL_W*0.58]))

    # PAGE 9 RIGHT
    story.append(sec("DIARRHOEA — MNEMONICS & SUMMARY"))
    story.append(subsec("CAUSES MNEMONIC — 'ABCDE'"))
    abcde_data = [
        ["Letter", "Cause", "Example"],
        ["A", "Antibiotics / post-infective", "C. diff, antibiotic-associated diarrhoea"],
        ["B", "Bowel disease (IBD / Ca)", "Crohn's, UC, CRC, ischaemic colitis"],
        ["C", "Coeliac / Chronic pancreatitis", "Steatorrhoea, malabsorption"],
        ["D", "Drugs / Diet", "Laxatives, metformin, orlistat, lactose"],
        ["E", "Endocrine / Exotic (infective)", "Carcinoid, VIPoma, thyroid; Giardia, amoebiasis"],
    ]
    story.append(tbl(abcde_data, [COL_W*0.1, COL_W*0.38, COL_W*0.52], hdr_bg=TEAL))
    story.append(sp(5))

    story.append(subsec("MECHANISM MNEMONIC — 'SOME'"))
    some_data = [
        ["S", "Secretory", "Continues fasting. Cholera, carcinoid, VIPoma, bile salt"],
        ["O", "Osmotic", "Stops fasting. Coeliac, lactose intolerance, laxative"],
        ["M", "Motility", "IBS-D, hyperthyroid, diabetic neuropathy, post-surgical"],
        ["E", "Exudative/Inflammatory", "Blood/pus/mucus. IBD, Shigella, C. diff, Ca"],
    ]
    story.append(tbl(some_data, [COL_W*0.08, COL_W*0.22, COL_W*0.7], hdr_bg=DARK_BLUE))
    story.append(sp(5))

    story.append(subsec("HIGH-RISK ORGANISMS — KNOW THESE"))
    hro_data = [
        ["Organism", "Key Feature", "Treatment"],
        ["C. difficile", "Post-antibiotics; pseudomembranes", "Oral vancomycin"],
        ["E. coli O157", "Bloody diarrhoea + HUS", "Supportive ONLY"],
        ["Vibrio cholerae", "Rice-water stools", "ORS + doxycycline"],
        ["Entamoeba histolytica", "Dysentery + liver abscess", "Metronidazole"],
        ["Giardia lamblia", "Steatorrhoea, contaminated water", "Metronidazole"],
        ["Cryptosporidium", "HIV patient, prolonged", "Nitazoxanide + ART"],
    ]
    story.append(tbl(hro_data, [COL_W*0.33, COL_W*0.4, COL_W*0.27]))
    story.append(sp(5))

    story.append(green("FINAL OSCE TIP: In any acute diarrhoea scenario — your first 3 answers are: "
                       "1) Assess dehydration (signs + obs), 2) Stool MC&S + C. diff toxin, "
                       "3) ORS / IV fluids. Then investigate cause based on history clues."))

    doc.build(story, canvasmaker=MyCanvas)
    print("PDF built successfully:", out)

build()
PYEOF
Running Command

cd /home/daytona/workspace/osce-booklets && python3 build_booklet.py 2>&1

Running Command

ls -lh /home/daytona/workspace/osce-booklets/Constipation_Diarrhoea_OSCE_Booklet.pdf && python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/osce-booklets/Constipation_Diarrhoea_OSCE_Booklet.pdf') print(f'Pages: {len(r.pages)}') for i, p in enumerate(r.pages): t = p.extract_text() print(f'Page {i+1}: {len(t)} chars - {t[:80].strip()}') "

Your booklet is ready! Here's the download link:

Constipation Diarrhoea OSCE Booklet

PDF Document · PDF


What's inside (10 pages, same style as your Upper GI booklet):
Constipation (Pages 1–5)
  • Definition + Rome IV criteria
  • Causes mnemonic — 'SPINE' (Structural, Physiological, Iatrogenic, Neurological, Endocrine)
  • Full history taking with tables (character, associated symptoms, drug history, social Hx)
  • Clinical examination — General, Abdominal, Perianal/DRE findings
  • Investigations (bloods + imaging + anorectal manometry + defaecating proctogram)
  • Management step ladder — Lifestyle → Laxatives table → Specific causes → Surgery
  • Special situations: elderly, pregnancy, Hirschsprung's
  • OSCE Viva Q&A + Alarm features mnemonic 'ABCDE' + Bristol Stool Chart
Diarrhoea (Pages 6–10)
  • Definition + Classification by duration and mechanism — 'SOME' (Secretory, Osmotic, Motility, Exudative)
  • Full infective cause table (viral/bacterial/protozoal with clinical clues)
  • Non-infective causes (IBD, malabsorption, drugs, endocrine, functional)
  • Full history taking + dehydration assessment grading table
  • Chronic diarrhoea investigation panel (faecal calprotectin, coeliac serology, etc.)
  • Management: ORS → antianti-diarrhoeal agents → specific treatments per organism
  • OSCE Viva Q&A + Causes mnemonic 'ABCDE' + high-risk organisms table
Whenever you're ready, just say which topic to do next (Lower GI Bleeding or Dyspepsia) and I'll get that built for you!

No I want seperate booklet entirely on constipation and separate of diarrhoea

Writing File

~/osce-booklets/build_constipation.py

"""
Standalone CONSTIPATION OSCE Booklet
Matches style of Upper GI Bleeding Clinical OSCE Notes booklet
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.platypus import (
    BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer,
    Table, TableStyle, HRFlowable, KeepTogether, NextPageTemplate, PageBreak
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.pdfgen import canvas as pdfgen_canvas

NAVY       = colors.HexColor('#1a2a4a')
GOLD       = colors.HexColor('#c8a951')
LIGHT_BLUE = colors.HexColor('#dce8f5')
DARK_BLUE  = colors.HexColor('#2c5282')
TEAL       = colors.HexColor('#2a7a6b')
RED_BOX    = colors.HexColor('#fdecea')
RED_BORDER = colors.HexColor('#c0392b')
GREEN_BOX  = colors.HexColor('#e8f5e9')
GREEN_BDR  = colors.HexColor('#2e7d32')
AMBER_BOX  = colors.HexColor('#fff8e1')
AMBER_BDR  = colors.HexColor('#f57f17')
WHITE      = colors.white
BLACK      = colors.black
GREY_LIGHT = colors.HexColor('#f2f4f7')
GREY_MID   = colors.HexColor('#d5dce8')

PAGE_W, PAGE_H = A4
MARGIN_L = 13*mm
MARGIN_R = 13*mm
HDR_H    = 18*mm
FTR_H    = 10*mm
USABLE_W = PAGE_W - MARGIN_L - MARGIN_R
COL_GAP  = 6*mm
COL_W    = (USABLE_W - COL_GAP) / 2

TOPIC      = "CONSTIPATION"
SUBTOPICS  = "History Taking · Examination · Investigations · Management · Scoring · OSCE Tips"
FOOTER_TXT = "Sources: Bailey & Love · Sleisenger & Fordtran · Kumar & Clark · Oxford Handbook of Clinical Medicine"

def make_styles():
    s = {}
    s['title_main'] = ParagraphStyle('title_main', fontName='Helvetica-Bold', fontSize=18,
        textColor=WHITE, leading=22, alignment=TA_CENTER)
    s['title_sub'] = ParagraphStyle('title_sub', fontName='Helvetica-BoldOblique', fontSize=10,
        textColor=GOLD, leading=13, alignment=TA_CENTER)
    s['title_tags'] = ParagraphStyle('title_tags', fontName='Helvetica', fontSize=8,
        textColor=colors.HexColor('#a0bcd8'), leading=11, alignment=TA_CENTER)
    s['sec_head'] = ParagraphStyle('sec_head', fontName='Helvetica-Bold', fontSize=9,
        textColor=WHITE, backColor=DARK_BLUE, borderPadding=(3,5,3,5),
        leading=12, spaceAfter=4, spaceBefore=6)
    s['sec_teal'] = ParagraphStyle('sec_teal', fontName='Helvetica-Bold', fontSize=9,
        textColor=WHITE, backColor=TEAL, borderPadding=(3,5,3,5),
        leading=12, spaceAfter=4, spaceBefore=6)
    s['sec_red'] = ParagraphStyle('sec_red', fontName='Helvetica-Bold', fontSize=9,
        textColor=WHITE, backColor=RED_BORDER, borderPadding=(3,5,3,5),
        leading=12, spaceAfter=4, spaceBefore=6)
    s['subsec'] = ParagraphStyle('subsec', fontName='Helvetica-Bold', fontSize=8.5,
        textColor=DARK_BLUE, leading=11, spaceAfter=3, spaceBefore=5)
    s['body'] = ParagraphStyle('body', fontName='Helvetica', fontSize=7.8,
        textColor=BLACK, leading=11, spaceAfter=2)
    s['bullet'] = ParagraphStyle('bullet', fontName='Helvetica', fontSize=7.8,
        textColor=BLACK, leading=11, leftIndent=10, bulletIndent=2, spaceAfter=1.5)
    s['bullet_bold'] = ParagraphStyle('bullet_bold', fontName='Helvetica-Bold', fontSize=7.8,
        textColor=DARK_BLUE, leading=11, leftIndent=10, bulletIndent=2, spaceAfter=1.5)
    s['pearl'] = ParagraphStyle('pearl', fontName='Helvetica-BoldOblique', fontSize=7.8,
        textColor=colors.HexColor('#7b3f00'), backColor=AMBER_BOX, borderColor=AMBER_BDR,
        borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    s['red_flag'] = ParagraphStyle('red_flag', fontName='Helvetica-Bold', fontSize=7.8,
        textColor=RED_BORDER, backColor=RED_BOX, borderColor=RED_BORDER,
        borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    s['green_box'] = ParagraphStyle('green_box', fontName='Helvetica', fontSize=7.8,
        textColor=colors.HexColor('#1b5e20'), backColor=GREEN_BOX, borderColor=GREEN_BDR,
        borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    s['tbl_hdr'] = ParagraphStyle('tbl_hdr', fontName='Helvetica-Bold', fontSize=7.5,
        textColor=WHITE, leading=10, alignment=TA_CENTER)
    s['tbl_cell'] = ParagraphStyle('tbl_cell', fontName='Helvetica', fontSize=7.5,
        textColor=BLACK, leading=10)
    s['tbl_cell_b'] = ParagraphStyle('tbl_cell_b', fontName='Helvetica-Bold', fontSize=7.5,
        textColor=DARK_BLUE, leading=10)
    s['footer'] = ParagraphStyle('footer', fontName='Helvetica-Oblique', fontSize=6.5,
        textColor=colors.HexColor('#aaaaaa'), leading=9, alignment=TA_CENTER)
    return s

S = make_styles()

class BookletCanvas(pdfgen_canvas.Canvas):
    def __init__(self, filename, **kwargs):
        super().__init__(filename, **kwargs)
        self._saved_page_states = []
        self._pg = 0

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def save(self):
        total = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self._decorate(total)
            super().showPage()
        super().save()

    def _decorate(self, total):
        self._pg += 1
        pg = self._pg
        c = self

        # Header
        c.setFillColor(NAVY)
        c.rect(0, PAGE_H - HDR_H, PAGE_W, HDR_H, fill=1, stroke=0)
        c.setFillColor(GOLD)
        c.rect(0, PAGE_H - HDR_H - 1.5, PAGE_W, 1.5, fill=1, stroke=0)

        # Page number
        c.setFillColor(GOLD)
        c.setFont('Helvetica-Bold', 8)
        c.drawString(MARGIN_L, PAGE_H - 10*mm, f"Page {pg}")

        # Topic title
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 12)
        c.drawCentredString(PAGE_W/2, PAGE_H - 8.5*mm, TOPIC)
        c.setFont('Helvetica', 7.5)
        c.setFillColor(colors.HexColor('#a0bcd8'))
        c.drawCentredString(PAGE_W/2, PAGE_H - 13.5*mm, "Clinical OSCE Notes  |  Final Year MBBS")

        # Left/Right markers
        c.setFillColor(GOLD)
        c.setFont('Helvetica-Bold', 7.5)
        c.drawString(MARGIN_L, PAGE_H - HDR_H - 8, "◀ LEFT")
        c.drawRightString(PAGE_W - MARGIN_R, PAGE_H - HDR_H - 8, "RIGHT ▶")

        # Footer
        c.setFillColor(NAVY)
        c.rect(0, 0, PAGE_W, FTR_H + 3, fill=1, stroke=0)
        c.setFillColor(GOLD)
        c.rect(0, FTR_H + 3, PAGE_W, 1, fill=1, stroke=0)
        c.setFillColor(colors.HexColor('#aaaaaa'))
        c.setFont('Helvetica-Oblique', 6)
        c.drawCentredString(PAGE_W/2, 4*mm, FOOTER_TXT)

        # Column divider (not on cover)
        if pg > 1:
            c.setStrokeColor(GREY_MID)
            c.setLineWidth(0.5)
            mid_x = MARGIN_L + COL_W + COL_GAP/2
            c.line(mid_x, PAGE_H - HDR_H - 12, mid_x, FTR_H + 6)

def sec(t, style='sec_head'): return Paragraph(f"<b>{t}</b>", S[style])
def subsec(t): return Paragraph(t, S['subsec'])
def body(t): return Paragraph(t, S['body'])
def bull(t): return Paragraph(f"• {t}", S['bullet'])
def bullb(t): return Paragraph(f"• {t}", S['bullet_bold'])
def pearl(t): return Paragraph(f"★ OSCE PEARL: {t}", S['pearl'])
def red(t): return Paragraph(f"⚠ RED FLAG: {t}", S['red_flag'])
def green(t): return Paragraph(t, S['green_box'])
def sp(h=3): return Spacer(1, h)
def hr(): return HRFlowable(width="100%", thickness=0.5, color=GREY_MID, spaceAfter=3, spaceBefore=2)

def tbl(data, widths, hdr_bg=DARK_BLUE):
    style = [
        ('BACKGROUND', (0,0), (-1,0), hdr_bg),
        ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
        ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
        ('FONTSIZE',   (0,0), (-1,-1), 7.5),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [GREY_LIGHT, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.4, GREY_MID),
        ('LEFTPADDING',  (0,0), (-1,-1), 4),
        ('RIGHTPADDING', (0,0), (-1,-1), 4),
        ('TOPPADDING',   (0,0), (-1,-1), 3),
        ('BOTTOMPADDING',(0,0), (-1,-1), 3),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]
    wrapped = []
    for r, row in enumerate(data):
        wrow = []
        for cell in row:
            if isinstance(cell, str):
                st = S['tbl_hdr'] if r == 0 else S['tbl_cell']
                wrow.append(Paragraph(cell, st))
            else:
                wrow.append(cell)
        wrapped.append(wrow)
    return Table(wrapped, colWidths=widths, style=TableStyle(style), hAlign='LEFT')

def build():
    out = '/home/daytona/workspace/osce-booklets/Constipation_OSCE_Booklet.pdf'
    bot_y = FTR_H + 8
    top_y = PAGE_H - HDR_H - 14
    frame_h = top_y - bot_y

    cover_fr = Frame(MARGIN_L, bot_y, USABLE_W, frame_h, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0)
    left_fr  = Frame(MARGIN_L, bot_y, COL_W, frame_h, leftPadding=0, rightPadding=3, topPadding=0, bottomPadding=0)
    right_fr = Frame(MARGIN_L + COL_W + COL_GAP, bot_y, COL_W, frame_h, leftPadding=3, rightPadding=0, topPadding=0, bottomPadding=0)

    doc = BaseDocTemplate(out, pagesize=A4, leftMargin=MARGIN_L, rightMargin=MARGIN_R,
                          topMargin=22*mm, bottomMargin=16*mm)
    doc.addPageTemplates([
        PageTemplate(id='cover',  frames=[cover_fr]),
        PageTemplate(id='twocol', frames=[left_fr, right_fr]),
    ])

    story = []

    # ── COVER PAGE ──────────────────────────────────────────────────────────
    story.append(NextPageTemplate('cover'))
    story.append(Spacer(1, 28*mm))
    story.append(Paragraph("CONSTIPATION", S['title_main']))
    story.append(Spacer(1, 4))
    story.append(Paragraph("Clinical OSCE Notes  |  Final Year MBBS", S['title_sub']))
    story.append(Spacer(1, 7))
    story.append(Paragraph(SUBTOPICS, S['title_tags']))
    story.append(Spacer(1, 8))
    story.append(HRFlowable(width="80%", thickness=1.5, color=GOLD, spaceAfter=8, hAlign='CENTER'))
    story.append(Paragraph(FOOTER_TXT, S['footer']))
    story.append(PageBreak())

    # ── PAGE 2 — DEFINITION, CAUSES, HISTORY ────────────────────────────────
    story.append(NextPageTemplate('twocol'))

    # LEFT
    story.append(sec("CONSTIPATION — DEFINITION & ROME IV CRITERIA"))
    story.append(body("<b>Definition (Rome IV):</b> ≥2 of the following present for the last 3 months, with symptom onset ≥6 months prior:"))
    story.append(bull("Straining during >25% of defaecations"))
    story.append(bull("Lumpy or hard stools (Bristol type 1–2) in >25%"))
    story.append(bull("Sensation of incomplete evacuation in >25%"))
    story.append(bull("Sensation of anorectal obstruction/blockage in >25%"))
    story.append(bull("Manual manoeuvres to facilitate defaecation in >25%"))
    story.append(bull("Fewer than 3 spontaneous bowel movements per week"))
    story.append(body("<i>PLUS: Loose stools rarely present without laxatives; insufficient criteria for IBS.</i>"))
    story.append(sp(5))

    story.append(sec("CAUSES — 'SPINE' MNEMONIC"))
    causes = [
        ["Letter", "Category", "Key Examples"],
        ["S", "Structural", "Colorectal Ca, anal fissure, stricture, diverticular disease, pelvic organ prolapse, rectocoele, external compression"],
        ["P", "Physiological / Dietary", "Low fibre intake, poor fluid intake, immobility, IBS-C, slow transit constipation, pregnancy"],
        ["I", "Iatrogenic — Drugs", "Opioids (#1), antacids (Al/Ca), iron tablets, TCAs, anticholinergics, CCBs (verapamil), ondansetron, antipsychotics"],
        ["N", "Neurological", "Parkinson's disease, multiple sclerosis, spinal cord injury, autonomic neuropathy, Hirschsprung's disease, cauda equina"],
        ["E", "Endocrine / Metabolic", "Hypothyroidism (#1 metabolic), hypercalcaemia, hypokalaemia, diabetes, uraemia, pregnancy"],
    ]
    story.append(tbl(causes, [COL_W*0.08, COL_W*0.28, COL_W*0.64]))
    story.append(sp(4))
    story.append(pearl("Hypothyroidism and hypercalcaemia are the two most exam-tested metabolic causes. "
                       "Always check TFTs and serum Ca²⁺ in unexplained constipation — both are easily reversible."))
    story.append(sp(4))
    story.append(red("ALARM FEATURES → urgent colonoscopy / 2WW referral: Age >50 with new-onset constipation, "
                     "rectal bleeding, unintentional weight loss, iron-deficiency anaemia, palpable abdominal/rectal mass, "
                     "family Hx of CRC, change in bowel habit lasting >6 weeks."))

    # RIGHT
    story.append(sec("HISTORY TAKING — CONSTIPATION"))
    story.append(body("<b>Opening question:</b> 'Can you tell me about your bowel habit? "
                      "How often are you opening your bowels? How long has this been going on?'"))
    story.append(sp(4))

    story.append(subsec("1. CHARACTER OF CONSTIPATION"))
    hx1 = [
        ["Ask This", "Clinical Meaning"],
        ["How many times per week?", "<3/week = meets Rome IV frequency criterion"],
        ["Stool consistency — hard/lumpy?", "Bristol 1–2 = constipation; describes transit time"],
        ["Straining / feeling of blockage?", "Pelvic floor dyssynergia / outlet dysfunction"],
        ["Need to manually assist?", "Severe outlet dysfunction, large rectocoele"],
        ["Acute onset vs chronic (years)?", "Acute: think obstruction or malignancy; Chronic: functional/IBS"],
        ["Alternating with diarrhoea?", "IBS; OR overflow diarrhoea around faecal impaction"],
        ["Blood or mucus in stool?", "ALARM — CRC, IBD, haemorrhoids, fissure"],
        ["Sensation of incomplete emptying?", "Rome IV criterion; also IBS-C, rectocoele"],
    ]
    story.append(tbl(hx1, [COL_W*0.46, COL_W*0.54]))
    story.append(sp(4))

    story.append(subsec("2. ASSOCIATED SYMPTOMS"))
    assoc = [
        ["Symptom", "Think of..."],
        ["Abdominal pain relieved by defaecation", "IBS-C"],
        ["Weight loss + rectal bleeding + tenesmus", "Colorectal carcinoma — urgent 2WW"],
        ["Bloating + ribbony stools", "Rectal / sigmoid Ca"],
        ["Fatigue + cold intolerance + weight gain", "Hypothyroidism → check TSH"],
        ["Polyuria + polydipsia + bone pain + nausea", "Hypercalcaemia ('Bones, Stones, Groans, Moans')"],
        ["Absolute constipation + vomiting + distension", "Bowel obstruction — emergency"],
        ["Perianal pain + bright red blood on wiping", "Anal fissure → fear of defaecation reinforces constipation"],
        ["Neurological symptoms (weakness/sensory)", "Spinal cord pathology — cauda equina, MS"],
    ]
    story.append(tbl(assoc, [COL_W*0.47, COL_W*0.53]))
    story.append(sp(4))

    story.append(subsec("3. DRUG HISTORY — KEY CONSTIPATING DRUGS"))
    drugs = [
        ["Drug Class", "Example", "Mechanism"],
        ["Opioids (#1 cause)", "Codeine, morphine, tramadol", "μ-receptor agonist → ↓ peristalsis + ↑ sphincter tone"],
        ["Antacids", "Al(OH)₃, CaCO₃ (Rennies)", "Bind water + form insoluble salts → hard stool"],
        ["Iron supplements", "Ferrous sulphate", "Direct mucosa irritation + forms hard stool"],
        ["Anticholinergics", "TCAs, antihistamines, oxybutynin", "↓ acetylcholine → ↓ gut motility"],
        ["Calcium channel blockers", "Verapamil > amlodipine", "↓ smooth muscle contraction in gut"],
        ["5-HT₃ antagonists", "Ondansetron, granisetron", "Block serotonin-mediated peristalsis"],
        ["Antipsychotics", "Clozapine, olanzapine", "Anticholinergic + autonomic effects"],
    ]
    story.append(tbl(drugs, [COL_W*0.27, COL_W*0.27, COL_W*0.46]))
    story.append(sp(3))
    story.append(pearl("In any patient on opioids — use osmotic laxative (macrogol) PLUS stimulant (senna). "
                       "Bulk-forming agents alone are insufficient. Consider methylnaltrexone SC if refractory."))

    story.append(PageBreak())

    # ── PAGE 3 — SOCIAL Hx, PMH, EXAMINATION ──────────────────────────────
    # LEFT
    story.append(sec("HISTORY TAKING — CONSTIPATION (CONT.)"))
    story.append(subsec("4. SOCIAL HISTORY"))
    story.append(bull("<b>Diet:</b> Fibre intake (target 25–30 g/day) — ask about fruit, veg, wholegrains"))
    story.append(bull("<b>Fluid intake:</b> 1.5–2 L/day essential — fibre does not work without water"))
    story.append(bull("<b>Physical activity:</b> Sedentary lifestyle → ↓ colonic motility"))
    story.append(bull("<b>Pregnancy:</b> Progesterone ↓ gut motility; iron supplements compound this"))
    story.append(bull("<b>Occupation/Stress:</b> IBS-C associated with anxiety, stress, psychological factors"))
    story.append(bull("<b>Laxative use:</b> Long-term stimulant laxatives → atonic/cathartic colon"))
    story.append(bull("<b>Mobility:</b> Institutionalised/bed-bound → ↓ peristalsis; constipation universal"))
    story.append(sp(4))

    story.append(subsec("5. PAST MEDICAL HISTORY"))
    story.append(bull("Previous colorectal surgery / hysterectomy → adhesions, pelvic nerve damage"))
    story.append(bull("Known IBD, diverticular disease, CRC"))
    story.append(bull("Hypothyroidism, diabetes mellitus, hypercalcaemia, renal disease"))
    story.append(bull("Depression / eating disorders (anorexia → extreme constipation)"))
    story.append(bull("Childhood onset → consider Hirschsprung's (no ganglion cells in distal bowel)"))
    story.append(bull("Spinal cord injury, Parkinson's, MS → neurogenic bowel"))
    story.append(sp(4))

    story.append(subsec("6. FAMILY HISTORY"))
    story.append(bull("First-degree relative with CRC or polyps → ↑ CRC risk → earlier colonoscopy"))
    story.append(bull("HNPCC / Lynch syndrome family Hx → genetic testing"))
    story.append(sp(4))

    story.append(sec("CLINICAL EXAMINATION — CONSTIPATION", 'sec_teal'))
    story.append(subsec("A. GENERAL INSPECTION"))
    story.append(bull("<b>Weight / BMI:</b> Weight loss → alarm (Ca); weight gain + fatigue → hypothyroidism"))
    story.append(bull("<b>Pallor:</b> Conjunctival pallor → iron-deficiency anaemia from occult blood loss (CRC)"))
    story.append(bull("<b>Thyroid signs:</b> Goitre, bradycardia, dry skin, coarse hair, periorbital oedema → hypothyroidism"))
    story.append(bull("<b>Hypercalcaemia signs:</b> Confusion, weakness, polyuria — 'Bones, Stones, Groans, Moans'"))
    story.append(bull("<b>Parkinson's features:</b> Tremor, rigidity, bradykinesia → neurogenic constipation"))
    story.append(sp(4))

    story.append(subsec("B. ABDOMINAL EXAMINATION"))
    exam = [
        ["Finding", "Diagnosis to Consider"],
        ["Distension", "Obstruction, ileus, loaded colon, megacolon"],
        ["Palpable mass LIF (left iliac fossa)", "Loaded sigmoid colon / faecal impaction"],
        ["Palpable mass — firm, indents on pressure", "Faecal mass (distinguishes from tumour)"],
        ["Palpable mass — hard, fixed, irregular", "Colorectal carcinoma — urgent 2WW referral"],
        ["Diffuse mild tenderness", "Loaded colon / constipation"],
        ["Tympany on percussion", "Gas-filled loops → obstruction pattern"],
        ["Absent/tinkling bowel sounds", "Obstruction / ileus"],
        ["Succussion splash", "Gastric outlet obstruction (if epigastric)"],
    ]
    story.append(tbl(exam, [COL_W*0.45, COL_W*0.55]))

    # RIGHT
    story.append(subsec("C. PERIANAL & DIGITAL RECTAL EXAMINATION"))
    story.append(bull("<b>Inspection:</b> Haemorrhoids (prolapsed), anal fissure (posterior midline), skin tags, rectal prolapse"))
    story.append(bull("<b>DRE — hard stool in rectum:</b> Faecal impaction / outlet dysfunction"))
    story.append(bull("<b>DRE — empty rectum:</b> Slow transit constipation (stool not reaching rectum)"))
    story.append(bull("<b>Anal tone ↓:</b> Rectal prolapse, sphincter damage, neurological cause"))
    story.append(bull("<b>Anal tone ↑ (tight):</b> Anal fissure, dyssynergia"))
    story.append(bull("<b>Anterior rectal wall bulge:</b> Rectocoele (women — bowel herniates into vagina)"))
    story.append(sp(3))
    story.append(pearl("OSCE TIP: Always offer to perform a PR examination. An empty rectum on DRE in a constipated patient "
                       "indicates SLOW TRANSIT (stool not reaching rectum). A loaded rectum indicates OUTLET DYSFUNCTION or impaction."))
    story.append(sp(4))

    story.append(sec("INVESTIGATIONS — CONSTIPATION"))
    story.append(subsec("FIRST-LINE BLOODS"))
    bloods = [
        ["Test", "What You're Looking For", "Clinical Value"],
        ["FBC", "Hb, MCV, platelets", "IDA (↓Hb, ↓MCV) → occult bleed from CRC or IBD"],
        ["U&E", "K⁺, Na⁺, creatinine, urea", "Hypokalaemia → constipation; dehydration → ↑ urea"],
        ["Serum Ca²⁺", "Corrected calcium", "Hypercalcaemia → constipation; check PTH if ↑"],
        ["TFTs (TSH/T4)", "Thyroid stimulating hormone", "Hypothyroidism — very common, easily treated"],
        ["Glucose / HbA1c", "Fasting glucose, HbA1c", "Diabetic autonomic neuropathy"],
        ["CRP / ESR", "Inflammatory markers", "↑ in IBD, CRC, infection"],
        ["CEA", "Carcinoembryonic antigen", "Raised in CRC — NOT diagnostic alone; use for monitoring"],
    ]
    story.append(tbl(bloods, [COL_W*0.22, COL_W*0.28, COL_W*0.5]))
    story.append(sp(4))

    story.append(subsec("IMAGING & SPECIALIST INVESTIGATIONS"))
    imaging = [
        ["Investigation", "When to Order", "What It Shows"],
        ["AXR (plain film)", "Suspected obstruction or faecal impaction", "Faecal loading, dilated loops, obstruction pattern (3-6-9 rule)"],
        ["Colonoscopy / sigmoidoscopy", "Alarm features, age >50, new-onset, rectal bleeding", "Polyps, carcinoma, IBD, diverticulosis — GOLD STANDARD"],
        ["CT colonography", "Unable to tolerate colonoscopy / elderly", "Same as colonoscopy — virtual colonoscopy"],
        ["Colonic transit study (CTT)", "Chronic constipation — slow transit vs outlet", "Radiopaque markers tracked — normal transit <5 days"],
        ["Anorectal manometry", "Suspected pelvic floor dysfunction / dyssynergia", "Sphincter pressures, RAIR, rectal sensation"],
        ["Balloon expulsion test", "Outlet dysfunction screen", "Normal <1 min; prolonged → dyssynergia"],
        ["Defaecating proctogram", "Straining, incomplete evacuation, outlet dysfunction", "Rectocoele, intussusception, paradoxical puborectalis"],
        ["MRI pelvis (dynamic)", "Complex pelvic floor pathology", "Structural + functional anatomy, rectocoele, prolapse"],
    ]
    story.append(tbl(imaging, [COL_W*0.28, COL_W*0.32, COL_W*0.4]))
    story.append(sp(3))
    story.append(pearl("The most important initial investigation in a patient >50 with new-onset constipation "
                       "and alarm features is colonoscopy — to exclude colorectal carcinoma."))

    story.append(PageBreak())

    # ── PAGE 4 — MANAGEMENT ────────────────────────────────────────────────
    # LEFT
    story.append(sec("MANAGEMENT — CONSTIPATION"))
    story.append(subsec("STEP 1 — LIFESTYLE (ALWAYS FIRST)"))
    story.append(bull("<b>Dietary fibre:</b> Increase to 25–30 g/day (fruits, vegetables, wholegrain, bran)"))
    story.append(bull("<b>Fluid intake:</b> 1.5–2 L/day — fibre requires water to bulk stools"))
    story.append(bull("<b>Exercise:</b> Regular aerobic activity directly stimulates colonic motility"))
    story.append(bull("<b>Toilet routine:</b> Regular time after meals (exploit gastrocolic reflex); never ignore urge to defaecate"))
    story.append(bull("<b>Positioning:</b> Feet elevated on footstool (squatting position → straightens anorectal angle)"))
    story.append(bull("<b>Review medications:</b> Stop or switch constipating drugs wherever clinically possible"))
    story.append(sp(5))

    story.append(subsec("STEP 2 — LAXATIVES"))
    lax = [
        ["Class", "Drug", "Mechanism", "Key Notes"],
        ["Bulk-forming\n(1st line mild)", "Ispaghula husk\n(Fybogel)", "↑ stool bulk → stimulates peristalsis", "Must take with ≥200 mL water; takes 2–3 days; avoid in impaction"],
        ["Osmotic\n(1st line opioid-\ninduced)", "Macrogol (Movicol)\nLactulose", "Draw water into bowel\nby osmosis", "Macrogol preferred (less bloating); lactulose causes flatulence"],
        ["Stimulant\n(short-term)", "Senna\nBisacodyl", "Stimulate enteric nerves\n→ ↑ peristalsis", "Avoid long-term — atonic colon risk; works in 6–12 hrs"],
        ["Stool softener", "Docusate sodium", "Detergent action — softens\nstool surface", "Mild; useful post-op / anal fissure; minimal evidence alone"],
        ["Rectal agents\n(acute/impaction)", "Phosphate enema\nGlycerol suppositories", "Local osmotic + stimulant", "Pre-procedure, impaction; glycerol suppositories for outlet dysfunction"],
        ["Secretagogue\n(chronic refractory)", "Linaclotide\nLubiprostone", "↑ intestinal Cl⁻ secretion\n→ stool fluidity", "IBS-C and chronic idiopathic constipation; specialist initiation"],
        ["Prokinetic\n(specialist)", "Prucalopride", "5-HT₄ agonist → ↑\ncolonic motility", "Chronic idiopathic constipation — when 2 laxatives failed"],
    ]
    story.append(tbl(lax, [COL_W*0.22, COL_W*0.22, COL_W*0.3, COL_W*0.26]))
    story.append(sp(4))
    story.append(pearl("LAXATIVE LADDER: Bulk-forming → Osmotic (macrogol) → Stimulant → Combination → "
                       "Secretagogue/Prucalopride. In OPIOID-induced constipation — START with osmotic + stimulant together. "
                       "Bulk-forming alone is NOT effective."))

    # RIGHT
    story.append(sec("MANAGEMENT — SPECIFIC CAUSES"))
    spec = [
        ["Cause", "Specific Treatment"],
        ["Hypothyroidism", "Levothyroxine replacement → constipation resolves as euthyroid state is restored"],
        ["Hypercalcaemia", "IV 0.9% NaCl rehydration + IV bisphosphonate (zoledronate); treat underlying cause (PTH, Ca)"],
        ["Hypokalaemia", "IV / oral K⁺ replacement; address cause"],
        ["Opioid-induced constipation", "Osmotic + stimulant laxative; methylnaltrexone SC (peripherally acting μ-antagonist) if refractory"],
        ["Anal fissure", "Topical GTN 0.4% cream / diltiazem 2% → relax internal sphincter; botulinum toxin if fails; lateral sphincterotomy if chronic"],
        ["Hirschsprung's disease", "Surgical pull-through procedure (Swenson/Soave) — resection of aganglionic segment"],
        ["Slow transit constipation (refractory)", "Prucalopride (5-HT₄ agonist) → prokinetic; subtotal colectomy + ileorectal anastomosis if all else fails"],
        ["Pelvic floor dyssynergia", "Biofeedback therapy — FIRST-LINE; retrains paradoxical puborectalis contraction"],
        ["Rectocoele", "Pelvic floor physiotherapy; surgical repair (transanal/transvaginal) if symptomatic and large"],
        ["Faecal impaction", "Manual evacuation under sedation if needed + phosphate enemas + macrogol disimpaction regimen (8 sachets/day)"],
        ["IBS-C", "Lifestyle + macrogol + linaclotide (NICE approved) + low-FODMAP diet + psychological support"],
        ["CRC causing obstruction", "Emergency: Hartmann's procedure / colonic stenting as bridge to elective surgery"],
    ]
    story.append(tbl(spec, [COL_W*0.35, COL_W*0.65]))
    story.append(sp(4))

    story.append(sec("SPECIAL SITUATIONS", 'sec_teal'))
    story.append(subsec("CONSTIPATION IN ELDERLY"))
    story.append(bull("Most common: polypharmacy + immobility + dehydration + low fibre diet"))
    story.append(bull("Risk of faecal impaction → overflow (spurious) diarrhoea — DO NOT treat with antidiarrhoeals"))
    story.append(bull("PR examination essential — hard stool in rectum confirms impaction"))
    story.append(sp(3))
    story.append(subsec("CONSTIPATION IN PREGNANCY"))
    story.append(bull("Progesterone ↓ gut motility; combined with iron supplements = very common"))
    story.append(bull("<b>Safe:</b> Ispaghula husk, lactulose, macrogol (short-term)"))
    story.append(bull("<b>Avoid:</b> Stimulant laxatives (senna) in 1st trimester; castor oil; mineral oils"))

    story.append(PageBreak())

    # ── PAGE 5 — HIRSCHSPRUNG, OSCE VIVA, MNEMONICS ───────────────────────
    # LEFT
    story.append(sec("HIRSCHSPRUNG'S DISEASE"))
    story.append(subsec("PATHOPHYSIOLOGY"))
    story.append(bull("Congenital absence of ganglion cells (Meissner's submucosal + Auerbach's myenteric plexus) in distal colon"))
    story.append(bull("Failure of neural crest cell migration during embryogenesis (week 5–12)"))
    story.append(bull("Aganglionic segment = tonically contracted → functional obstruction"))
    story.append(bull("Proximal bowel becomes massively dilated (megacolon)"))
    story.append(sp(4))

    story.append(subsec("PRESENTATION"))
    hirsch = [
        ["Age", "Presentation"],
        ["Neonate", "Failure to pass meconium within 48 hrs of birth (normal = within 24 hrs)"],
        ["Infant", "Chronic constipation + abdominal distension + failure to thrive"],
        ["Older child", "Severe constipation since birth; does not self-resolve"],
        ["Adult (rare)", "Lifelong constipation; short segment form"],
    ]
    story.append(tbl(hirsch, [COL_W*0.22, COL_W*0.78]))
    story.append(sp(3))
    story.append(subsec("INVESTIGATIONS"))
    story.append(bull("<b>Suction rectal biopsy:</b> GOLD STANDARD — absence of ganglion cells + ↑ acetylcholinesterase staining"))
    story.append(bull("<b>Contrast enema (barium):</b> Shows transition zone (narrow aganglionic → dilated proximal)"))
    story.append(bull("<b>Anorectal manometry:</b> Absent RAIR (recto-anal inhibitory reflex) — pathognomonic"))
    story.append(sp(3))
    story.append(subsec("TREATMENT"))
    story.append(bull("Definitive: Surgical pull-through (Swenson / Soave / Duhamel procedure)"))
    story.append(bull("Emergency: Colostomy if toxic megacolon / perforation"))
    story.append(sp(5))

    story.append(sec("CONSTIPATION — OSCE VIVA Q&A"))
    viva = [
        ["Examiner Asks...", "Your Answer"],
        ["Define constipation (Rome IV)", "<3 BM/week OR ≥2 of: straining, hard stools, incomplete evacuation, manual manoeuvres, in >25% defaecations"],
        ["Most common drug cause?", "Opioids — reduce peristalsis via μ-receptors; also iron, Al antacids, TCAs, verapamil"],
        ["Which metabolic causes must you exclude?", "Hypothyroidism (TSH), hypercalcaemia (Ca²⁺), hypokalaemia (K⁺), diabetes"],
        ["First investigation in new constipation >50 yrs?", "FBC, TFTs, serum Ca²⁺ → colonoscopy if alarm features present"],
        ["Empty rectum on DRE tells you what?", "Slow transit constipation — stool not reaching rectum; investigate with transit study"],
        ["Loaded rectum on DRE tells you what?", "Outlet dysfunction / faecal impaction — treat impaction before laxatives"],
        ["What is overflow diarrhoea?", "Liquid stool leaks around faecal impaction — DO NOT give antidiarrhoeals; disimpact first"],
        ["Best laxative for opioid-induced constipation?", "Osmotic (macrogol) + stimulant (senna); methylnaltrexone if refractory"],
        ["What is prucalopride?", "5-HT₄ agonist prokinetic — for chronic idiopathic constipation after 2 laxatives failed"],
        ["Treatment for pelvic floor dyssynergia?", "Biofeedback therapy — first-line; surgery rarely needed"],
        ["How do you diagnose Hirschsprung's?", "Suction rectal biopsy — absence of ganglion cells (+ absent RAIR on manometry)"],
        ["Transition zone on barium enema — what does it indicate?", "Hirschsprung's — narrow aganglionic distal segment → dilated proximal colon"],
    ]
    story.append(tbl(viva, [COL_W*0.42, COL_W*0.58]))

    # RIGHT
    story.append(sec("MNEMONICS & HIGH-YIELD SUMMARY"))
    story.append(subsec("CAUSES — 'SPINE'"))
    spine = [
        ["Letter", "Category", "Key Example"],
        ["S", "Structural", "CRC, anal fissure, stricture, rectocoele"],
        ["P", "Physiological / Dietary", "Low fibre, IBS-C, slow transit, immobility"],
        ["I", "Iatrogenic (Drugs)", "Opioids, iron, Al antacids, TCAs, verapamil"],
        ["N", "Neurological", "Parkinson's, MS, spinal injury, Hirschsprung's"],
        ["E", "Endocrine / Metabolic", "Hypothyroid, hypercalcaemia, hypokalaemia"],
    ]
    story.append(tbl(spine, [COL_W*0.08, COL_W*0.32, COL_W*0.6], hdr_bg=TEAL))
    story.append(sp(5))

    story.append(subsec("ALARM FEATURES — 'ABCDE'"))
    alarm = [
        ["Letter", "Feature"],
        ["A", "Anaemia (iron-deficiency) — check FBC"],
        ["B", "Bleeding per rectum — fresh or dark"],
        ["C", "Change in bowel habit >6 weeks (new onset)"],
        ["D", "Duration sudden + age >50 years old"],
        ["E", "Extra: weight loss, palpable mass, family Hx CRC"],
    ]
    story.append(tbl(alarm, [COL_W*0.1, COL_W*0.9], hdr_bg=RED_BORDER))
    story.append(sp(5))

    story.append(subsec("BRISTOL STOOL CHART — ESSENTIALS"))
    bsc = [
        ["Type", "Appearance", "Clinical Meaning"],
        ["1", "Separate hard lumps", "Slow transit — severe constipation"],
        ["2", "Lumpy sausage", "Slow transit — constipation"],
        ["3", "Cracked sausage", "Normal to slow transit"],
        ["4", "Smooth snake/sausage", "IDEAL — normal transit"],
        ["5", "Soft blobs, clear edges", "Lacking fibre / fast transit"],
        ["6", "Fluffy/mushy, ragged", "Mild diarrhoea"],
        ["7", "Watery, no solid pieces", "Diarrhoea — rapid transit"],
    ]
    story.append(tbl(bsc, [COL_W*0.1, COL_W*0.38, COL_W*0.52]))
    story.append(sp(4))
    story.append(pearl("Bristol 1–2 = constipation. Bristol 6–7 = diarrhoea. Bristol 4 = ideal. "
                       "Ask patient to describe their stool using the chart in the OSCE."))
    story.append(sp(5))

    story.append(green("FINAL OSCE TIP: If asked about a constipated patient — your first 3 answers should be: "
                       "1) Full drug history (opioids? iron? CCBs?), 2) Alarm features assessment, "
                       "3) Bloods: FBC, TFTs, Ca²⁺. Then colonoscopy if alarm features present."))

    doc.build(story, canvasmaker=BookletCanvas)
    print("Constipation booklet built:", out)

build()
Writing File

~/osce-booklets/build_diarrhoea.py

"""
Standalone DIARRHOEA OSCE Booklet
Matches style of Upper GI Bleeding Clinical OSCE Notes booklet
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.platypus import (
    BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer,
    Table, TableStyle, HRFlowable, KeepTogether, NextPageTemplate, PageBreak
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.pdfgen import canvas as pdfgen_canvas

NAVY       = colors.HexColor('#1a2a4a')
GOLD       = colors.HexColor('#c8a951')
DARK_BLUE  = colors.HexColor('#2c5282')
TEAL       = colors.HexColor('#2a7a6b')
RED_BOX    = colors.HexColor('#fdecea')
RED_BORDER = colors.HexColor('#c0392b')
GREEN_BOX  = colors.HexColor('#e8f5e9')
GREEN_BDR  = colors.HexColor('#2e7d32')
AMBER_BOX  = colors.HexColor('#fff8e1')
AMBER_BDR  = colors.HexColor('#f57f17')
WHITE      = colors.white
BLACK      = colors.black
GREY_LIGHT = colors.HexColor('#f2f4f7')
GREY_MID   = colors.HexColor('#d5dce8')

PAGE_W, PAGE_H = A4
MARGIN_L = 13*mm
MARGIN_R = 13*mm
HDR_H    = 18*mm
FTR_H    = 10*mm
USABLE_W = PAGE_W - MARGIN_L - MARGIN_R
COL_GAP  = 6*mm
COL_W    = (USABLE_W - COL_GAP) / 2

TOPIC      = "DIARRHOEA"
SUBTOPICS  = "Definition · Causes · History Taking · Examination · Investigations · Management · OSCE Tips"
FOOTER_TXT = "Sources: Bailey & Love · Sleisenger & Fordtran · Kumar & Clark · Oxford Handbook of Clinical Medicine"

def make_styles():
    s = {}
    s['title_main'] = ParagraphStyle('title_main', fontName='Helvetica-Bold', fontSize=18,
        textColor=WHITE, leading=22, alignment=TA_CENTER)
    s['title_sub'] = ParagraphStyle('title_sub', fontName='Helvetica-BoldOblique', fontSize=10,
        textColor=GOLD, leading=13, alignment=TA_CENTER)
    s['title_tags'] = ParagraphStyle('title_tags', fontName='Helvetica', fontSize=8,
        textColor=colors.HexColor('#a0bcd8'), leading=11, alignment=TA_CENTER)
    s['sec_head'] = ParagraphStyle('sec_head', fontName='Helvetica-Bold', fontSize=9,
        textColor=WHITE, backColor=DARK_BLUE, borderPadding=(3,5,3,5),
        leading=12, spaceAfter=4, spaceBefore=6)
    s['sec_teal'] = ParagraphStyle('sec_teal', fontName='Helvetica-Bold', fontSize=9,
        textColor=WHITE, backColor=TEAL, borderPadding=(3,5,3,5),
        leading=12, spaceAfter=4, spaceBefore=6)
    s['sec_red'] = ParagraphStyle('sec_red', fontName='Helvetica-Bold', fontSize=9,
        textColor=WHITE, backColor=RED_BORDER, borderPadding=(3,5,3,5),
        leading=12, spaceAfter=4, spaceBefore=6)
    s['subsec'] = ParagraphStyle('subsec', fontName='Helvetica-Bold', fontSize=8.5,
        textColor=DARK_BLUE, leading=11, spaceAfter=3, spaceBefore=5)
    s['body'] = ParagraphStyle('body', fontName='Helvetica', fontSize=7.8,
        textColor=BLACK, leading=11, spaceAfter=2)
    s['bullet'] = ParagraphStyle('bullet', fontName='Helvetica', fontSize=7.8,
        textColor=BLACK, leading=11, leftIndent=10, bulletIndent=2, spaceAfter=1.5)
    s['bullet_bold'] = ParagraphStyle('bullet_bold', fontName='Helvetica-Bold', fontSize=7.8,
        textColor=DARK_BLUE, leading=11, leftIndent=10, bulletIndent=2, spaceAfter=1.5)
    s['pearl'] = ParagraphStyle('pearl', fontName='Helvetica-BoldOblique', fontSize=7.8,
        textColor=colors.HexColor('#7b3f00'), backColor=AMBER_BOX, borderColor=AMBER_BDR,
        borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    s['red_flag'] = ParagraphStyle('red_flag', fontName='Helvetica-Bold', fontSize=7.8,
        textColor=RED_BORDER, backColor=RED_BOX, borderColor=RED_BORDER,
        borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    s['green_box'] = ParagraphStyle('green_box', fontName='Helvetica', fontSize=7.8,
        textColor=colors.HexColor('#1b5e20'), backColor=GREEN_BOX, borderColor=GREEN_BDR,
        borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    s['tbl_hdr'] = ParagraphStyle('tbl_hdr', fontName='Helvetica-Bold', fontSize=7.5,
        textColor=WHITE, leading=10, alignment=TA_CENTER)
    s['tbl_cell'] = ParagraphStyle('tbl_cell', fontName='Helvetica', fontSize=7.5,
        textColor=BLACK, leading=10)
    s['tbl_cell_b'] = ParagraphStyle('tbl_cell_b', fontName='Helvetica-Bold', fontSize=7.5,
        textColor=DARK_BLUE, leading=10)
    s['footer'] = ParagraphStyle('footer', fontName='Helvetica-Oblique', fontSize=6.5,
        textColor=colors.HexColor('#aaaaaa'), leading=9, alignment=TA_CENTER)
    return s

S = make_styles()

class BookletCanvas(pdfgen_canvas.Canvas):
    def __init__(self, filename, **kwargs):
        super().__init__(filename, **kwargs)
        self._saved_page_states = []
        self._pg = 0

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def save(self):
        total = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self._decorate(total)
            super().showPage()
        super().save()

    def _decorate(self, total):
        self._pg += 1
        pg = self._pg
        c = self
        c.setFillColor(NAVY)
        c.rect(0, PAGE_H - HDR_H, PAGE_W, HDR_H, fill=1, stroke=0)
        c.setFillColor(GOLD)
        c.rect(0, PAGE_H - HDR_H - 1.5, PAGE_W, 1.5, fill=1, stroke=0)
        c.setFillColor(GOLD)
        c.setFont('Helvetica-Bold', 8)
        c.drawString(MARGIN_L, PAGE_H - 10*mm, f"Page {pg}")
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 12)
        c.drawCentredString(PAGE_W/2, PAGE_H - 8.5*mm, TOPIC)
        c.setFont('Helvetica', 7.5)
        c.setFillColor(colors.HexColor('#a0bcd8'))
        c.drawCentredString(PAGE_W/2, PAGE_H - 13.5*mm, "Clinical OSCE Notes  |  Final Year MBBS")
        c.setFillColor(GOLD)
        c.setFont('Helvetica-Bold', 7.5)
        c.drawString(MARGIN_L, PAGE_H - HDR_H - 8, "◀ LEFT")
        c.drawRightString(PAGE_W - MARGIN_R, PAGE_H - HDR_H - 8, "RIGHT ▶")
        c.setFillColor(NAVY)
        c.rect(0, 0, PAGE_W, FTR_H + 3, fill=1, stroke=0)
        c.setFillColor(GOLD)
        c.rect(0, FTR_H + 3, PAGE_W, 1, fill=1, stroke=0)
        c.setFillColor(colors.HexColor('#aaaaaa'))
        c.setFont('Helvetica-Oblique', 6)
        c.drawCentredString(PAGE_W/2, 4*mm, FOOTER_TXT)
        if pg > 1:
            c.setStrokeColor(GREY_MID)
            c.setLineWidth(0.5)
            mid_x = MARGIN_L + COL_W + COL_GAP/2
            c.line(mid_x, PAGE_H - HDR_H - 12, mid_x, FTR_H + 6)

def sec(t, style='sec_head'): return Paragraph(f"<b>{t}</b>", S[style])
def subsec(t): return Paragraph(t, S['subsec'])
def body(t): return Paragraph(t, S['body'])
def bull(t): return Paragraph(f"• {t}", S['bullet'])
def bullb(t): return Paragraph(f"• {t}", S['bullet_bold'])
def pearl(t): return Paragraph(f"★ OSCE PEARL: {t}", S['pearl'])
def red(t): return Paragraph(f"⚠ RED FLAG: {t}", S['red_flag'])
def green(t): return Paragraph(t, S['green_box'])
def sp(h=3): return Spacer(1, h)

def tbl(data, widths, hdr_bg=DARK_BLUE):
    style = [
        ('BACKGROUND', (0,0), (-1,0), hdr_bg),
        ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
        ('FONTNAME',   (0,0), (-1,0), 'Helvetica-Bold'),
        ('FONTSIZE',   (0,0), (-1,-1), 7.5),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [GREY_LIGHT, WHITE]),
        ('GRID', (0,0), (-1,-1), 0.4, GREY_MID),
        ('LEFTPADDING',  (0,0), (-1,-1), 4),
        ('RIGHTPADDING', (0,0), (-1,-1), 4),
        ('TOPPADDING',   (0,0), (-1,-1), 3),
        ('BOTTOMPADDING',(0,0), (-1,-1), 3),
        ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ]
    wrapped = []
    for r, row in enumerate(data):
        wrow = []
        for cell in row:
            if isinstance(cell, str):
                st = S['tbl_hdr'] if r == 0 else S['tbl_cell']
                wrow.append(Paragraph(cell, st))
            else:
                wrow.append(cell)
        wrapped.append(wrow)
    return Table(wrapped, colWidths=widths, style=TableStyle(style), hAlign='LEFT')

def build():
    out = '/home/daytona/workspace/osce-booklets/Diarrhoea_OSCE_Booklet.pdf'
    bot_y = FTR_H + 8
    top_y = PAGE_H - HDR_H - 14
    frame_h = top_y - bot_y

    cover_fr = Frame(MARGIN_L, bot_y, USABLE_W, frame_h, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0)
    left_fr  = Frame(MARGIN_L, bot_y, COL_W, frame_h, leftPadding=0, rightPadding=3, topPadding=0, bottomPadding=0)
    right_fr = Frame(MARGIN_L + COL_W + COL_GAP, bot_y, COL_W, frame_h, leftPadding=3, rightPadding=0, topPadding=0, bottomPadding=0)

    doc = BaseDocTemplate(out, pagesize=A4, leftMargin=MARGIN_L, rightMargin=MARGIN_R,
                          topMargin=22*mm, bottomMargin=16*mm)
    doc.addPageTemplates([
        PageTemplate(id='cover',  frames=[cover_fr]),
        PageTemplate(id='twocol', frames=[left_fr, right_fr]),
    ])

    story = []

    # ── COVER ──────────────────────────────────────────────────────────────
    story.append(NextPageTemplate('cover'))
    story.append(Spacer(1, 28*mm))
    story.append(Paragraph("DIARRHOEA", S['title_main']))
    story.append(Spacer(1, 4))
    story.append(Paragraph("Clinical OSCE Notes  |  Final Year MBBS", S['title_sub']))
    story.append(Spacer(1, 7))
    story.append(Paragraph(SUBTOPICS, S['title_tags']))
    story.append(Spacer(1, 8))
    story.append(HRFlowable(width="80%", thickness=1.5, color=GOLD, spaceAfter=8, hAlign='CENTER'))
    story.append(Paragraph(FOOTER_TXT, S['footer']))
    story.append(PageBreak())

    # ── PAGE 2 — DEFINITION, CLASSIFICATION, CAUSES ────────────────────────
    story.append(NextPageTemplate('twocol'))

    # LEFT
    story.append(sec("DIARRHOEA — DEFINITION & CLASSIFICATION"))
    story.append(body("<b>Definition:</b> Passage of ≥3 loose/watery stools per day, OR stool volume >200 mL/day, "
                      "OR stool weight >200 g/day."))
    story.append(sp(4))

    story.append(subsec("CLASSIFICATION BY DURATION"))
    dur = [
        ["Type", "Duration", "Most Common Causes"],
        ["Acute", "< 2 weeks", "Infective gastroenteritis (viral/bacterial), food poisoning, traveller's diarrhoea, antibiotic-associated"],
        ["Persistent", "2–4 weeks", "Continuing infection (Giardia, C. diff), early IBD presentation, tropical infection"],
        ["Chronic", "> 4 weeks", "IBD, IBS-D, malabsorption (coeliac, chronic pancreatitis), colorectal Ca, endocrine causes"],
    ]
    story.append(tbl(dur, [COL_W*0.18, COL_W*0.18, COL_W*0.64]))
    story.append(sp(4))

    story.append(subsec("CLASSIFICATION BY MECHANISM — 'SOME'"))
    mech = [
        ["Mechanism", "Pathophysiology", "Key Examples"],
        ["S — Secretory", "Active Cl⁻/Na⁺ secretion into lumen (↑ cAMP/cGMP); LARGE volume, watery; CONTINUES on fasting", "Cholera, ETEC, VIPoma, carcinoid, bile salt malabsorption, Addison's"],
        ["O — Osmotic", "Unabsorbed solutes draw water osmotically into lumen; STOPS on fasting; stool osmotic gap >125", "Lactose intolerance, coeliac disease, laxative abuse (MgSO₄), Giardia"],
        ["M — Motility", "Altered transit → inadequate absorption time; may be fast (diarrhoea) or slow (SIBO)", "IBS-D, hyperthyroidism, diabetic autonomic neuropathy, post-vagotomy, dumping syndrome"],
        ["E — Exudative / Inflammatory", "Mucosal damage → blood, pus, mucus in stool (dysentery pattern); SMALL volume, urgency", "UC, Crohn's, Shigella, C. difficile, CMV colitis, ischaemic colitis, CRC"],
    ]
    story.append(tbl(mech, [COL_W*0.2, COL_W*0.42, COL_W*0.38]))
    story.append(sp(4))
    story.append(pearl("Key distinction: Secretory diarrhoea CONTINUES on fasting; osmotic STOPS. "
                       "Large volume = secretory/proximal source. Small volume + urgency + blood = inflammatory/distal source."))

    # RIGHT
    story.append(sec("CAUSES OF DIARRHOEA — INFECTIVE"))
    story.append(subsec("VIRAL"))
    viral = [
        ["Organism", "Clinical Clue", "Management"],
        ["Norovirus", "Winter outbreaks; vomiting + watery diarrhoea; very contagious; 1–3 days", "Supportive — ORS; isolate (hospital outbreaks)"],
        ["Rotavirus", "Children <5 yrs; winter; dehydration major risk", "Supportive + ORS; vaccine prevents severe disease"],
        ["Adenovirus", "Children; respiratory symptoms + diarrhoea", "Supportive; self-limiting"],
        ["CMV", "Immunocompromised (HIV, transplant); bloody diarrhoea; colitis on scope", "IV ganciclovir / oral valganciclovir"],
    ]
    story.append(tbl(viral, [COL_W*0.22, COL_W*0.48, COL_W*0.3]))
    story.append(sp(4))

    story.append(subsec("BACTERIAL"))
    bact = [
        ["Organism", "Clinical Clue", "Treatment"],
        ["Campylobacter jejuni", "Most common bacterial cause UK; undercooked poultry; bloody diarrhoea + fever; self-limiting", "Azithromycin if severe/immunocomp"],
        ["Salmonella (non-typhi)", "Eggs/poultry; watery then bloody; fever; self-limiting usually", "Ciprofloxacin if bacteraemic"],
        ["Shigella (dysentery)", "Dysentery — bloody + mucus + tenesmus + fever; low inoculum; person-to-person", "Ciprofloxacin or azithromycin"],
        ["E. coli ETEC", "Traveller's diarrhoea; watery; no blood; tropical countries", "ORS; azithromycin if severe"],
        ["E. coli O157 (EHEC)", "Bloody diarrhoea; HUS risk (triad: haemolysis + ↓plt + AKI); children", "Supportive ONLY — NO antibiotics (↑ HUS risk)"],
        ["C. difficile", "Post-antibiotics (clindamycin, amoxicillin, cepha); profuse watery; hospital-acquired; elderly", "Oral vancomycin 125 mg QDS × 10d"],
        ["Vibrio cholerae", "Rice-water stools; massive secretory; severe dehydration; developing world", "ORS + doxycycline"],
        ["Salmonella typhi", "Typhoid: sustained fever + relative bradycardia + rose spots + constipation then diarrhoea", "IV ceftriaxone / azithromycin"],
        ["Yersinia enterocolitica", "RIF pain mimicking appendicitis; mesenteric adenitis; cold climates", "Usually self-limiting; ciprofloxacin if severe"],
        ["Staphylococcus aureus", "Food poisoning; preformed toxin; rapid onset (2–6 hrs); vomiting > diarrhoea", "Supportive; self-limiting"],
    ]
    story.append(tbl(bact, [COL_W*0.27, COL_W*0.47, COL_W*0.26]))

    story.append(PageBreak())

    # ── PAGE 3 — PROTOZOAL, NON-INFECTIVE CAUSES ──────────────────────────
    # LEFT
    story.append(sec("CAUSES — PROTOZOAL & NON-INFECTIVE"))
    story.append(subsec("PROTOZOAL"))
    proto = [
        ["Organism", "Clinical Clue", "Treatment"],
        ["Giardia lamblia", "Steatorrhoea + bloating + malabsorption; contaminated water; travellers; prolonged", "Metronidazole 400 mg TDS × 5–7 days"],
        ["Entamoeba histolytica", "Amoebic dysentery — bloody + mucus; liver abscess risk (RUQ pain + fever)", "Metronidazole + diloxanide furoate (luminal cyst)"],
        ["Cryptosporidium parvum", "Immunocompromised (HIV/AIDS); prolonged watery; self-limiting in immunocompetent", "Nitazoxanide + ART optimisation in HIV"],
        ["Cyclospora cayetanensis", "Prolonged watery diarrhoea; travellers; relapsing course", "Co-trimoxazole"],
        ["Microsporidium", "HIV with CD4 <100; chronic malabsorptive diarrhoea", "Albendazole + ART"],
    ]
    story.append(tbl(proto, [COL_W*0.27, COL_W*0.43, COL_W*0.3]))
    story.append(sp(4))

    story.append(subsec("NON-INFECTIVE CAUSES"))
    noninf = [
        ["Category", "Condition", "Key Features"],
        ["Inflammatory (IBD)", "Ulcerative Colitis", "Bloody mucoid diarrhoea + urgency + tenesmus; continuous from rectum proximally"],
        ["Inflammatory (IBD)", "Crohn's Disease", "Non-bloody or bloody; skip lesions; perianal; systemic features"],
        ["Malabsorption", "Coeliac Disease", "Steatorrhoea + weight loss + bloating; anti-tTG IgA + duodenal biopsy"],
        ["Malabsorption", "Chronic Pancreatitis", "Steatorrhoea + ↓ faecal elastase; epigastric pain; alcohol Hx"],
        ["Malabsorption", "Lactose Intolerance", "Watery diarrhoea + bloating after dairy; hydrogen breath test"],
        ["Malabsorption", "Short Bowel Syndrome", "Post-surgical; high-volume watery; electrolyte imbalance"],
        ["Drugs", "Antibiotics, metformin, laxatives, orlistat, PPIs, colchicine, SSRIs, NSAIDs, chemotherapy", "Drug history essential in all chronic diarrhoea"],
        ["Endocrine", "Hyperthyroidism", "Diarrhoea + palpitations + weight loss + sweating; check TFTs"],
        ["Endocrine", "Carcinoid Syndrome", "Diarrhoea + flushing + wheeze + right heart disease; ↑ 24-hr urinary 5-HIAA"],
        ["Endocrine", "VIPoma (Verner-Morrison)", "Massive watery diarrhoea (WDHA) + hypokalaemia + achlorhydria; ↑ serum VIP"],
        ["Endocrine", "Zollinger-Ellison Syndrome", "Diarrhoea + recurrent peptic ulcers; ↑ fasting gastrin; MEN-1"],
        ["Endocrine", "Addison's Disease", "Secretory diarrhoea + hypotension + hyperpigmentation; ↓ Na⁺, ↑ K⁺"],
        ["Colorectal", "CRC (right-sided)", "Altered bowel habit + IDA + occult blood; caecal Ca → watery anaemic diarrhoea"],
        ["Colorectal", "Ischaemic Colitis", "Sudden bloody diarrhoea in elderly/vascular disease; watershed zones (splenic flexure)"],
        ["Functional", "IBS-D", "Rome IV — ≥3 BM/day; pain relieved by defaecation; NO nocturnal symptoms; normal investigations"],
        ["Post-surgical", "Post-cholecystectomy", "Bile salt malabsorption → secretory diarrhoea; treat with cholestyramine"],
    ]
    story.append(tbl(noninf, [COL_W*0.22, COL_W*0.28, COL_W*0.5]))

    # RIGHT
    story.append(sec("HISTORY TAKING — DIARRHOEA"))
    story.append(body("<b>Opening:</b> 'Can you tell me about your bowel habit? "
                      "How many times a day? How long has this been happening?'"))
    story.append(sp(4))

    story.append(subsec("1. CHARACTER OF DIARRHOEA"))
    char = [
        ["Ask This", "Clinical Meaning"],
        ["Onset + duration (acute/chronic?)", "Acute <2 wks = infective; Chronic >4 wks = structural/functional"],
        ["Frequency — how many per day?", ">3/day = diarrhoea; nocturnal = organic (NOT IBS)"],
        ["Volume — large or small?", "Large = secretory/proximal; small + urgency = distal/inflammatory"],
        ["Colour?", "Pale/greasy → steatorrhoea; green → bile; dark/black → upper GI source"],
        ["Blood in stool?", "Bright red = lower GI; mixed with stool = Ca/IBD; mucus = UC/Ca"],
        ["Watery vs mucoid?", "Watery = secretory/infective; mucoid + blood = inflammatory"],
        ["Urgency / incontinence?", "Rectal inflammation (UC, CRC) → urgency; sphincter damage → incontinence"],
        ["Nocturnal diarrhoea?", "ORGANIC — IBD, secretory, CRC; IBS NEVER causes nocturnal diarrhoea"],
        ["Steatorrhoea (oily, floats)?", "Malabsorption — coeliac, chronic pancreatitis, Giardia"],
    ]
    story.append(tbl(char, [COL_W*0.42, COL_W*0.58]))
    story.append(sp(4))

    story.append(subsec("2. ASSOCIATED SYMPTOMS"))
    assoc = [
        ["Symptom Cluster", "Diagnosis"],
        ["Abdominal pain + blood + mucus + urgency + tenesmus", "UC (continuous from rectum)"],
        ["RIF pain + non-bloody or bloody + systemic + perianal", "Crohn's disease"],
        ["Fever + travel history + bloody stool", "Traveller's diarrhoea / dysentery / typhoid"],
        ["Weight loss + night sweats + diarrhoea", "IBD, TB, HIV-related, lymphoma, CRC"],
        ["Steatorrhoea + bloating + weight loss + anaemia", "Coeliac, chronic pancreatitis, Giardia"],
        ["Palpitations + sweating + tremor + weight loss", "Hyperthyroidism → check TFTs"],
        ["Flushing + wheeze + watery diarrhoea", "Carcinoid syndrome → 24-hr urinary 5-HIAA"],
        ["Post-antibiotic course", "C. difficile — check toxin EIA; profuse watery"],
        ["Arthralgia + skin rash (pyoderma/EN)", "IBD extra-intestinal manifestations"],
        ["Massive watery diarrhoea + hypokalaemia", "VIPoma (WDHA syndrome)"],
    ]
    story.append(tbl(assoc, [COL_W*0.5, COL_W*0.5]))
    story.append(sp(4))
    story.append(red("ALARM FEATURES → urgent 2WW referral: Rectal bleeding, unintentional weight loss, "
                     "iron-deficiency anaemia, palpable abdominal/rectal mass, age >50 with new bowel habit change, "
                     "positive faecal occult blood test."))

    story.append(PageBreak())

    # ── PAGE 4 — HISTORY CONT., EXAMINATION ───────────────────────────────
    # LEFT
    story.append(sec("HISTORY TAKING — DIARRHOEA (CONT.)"))
    story.append(subsec("3. DRUG HISTORY"))
    drugs = [
        ["Drug", "Mechanism / Type of Diarrhoea"],
        ["Antibiotics (clindamycin, amoxicillin, cephalosporins)", "Disrupts gut flora → C. diff; most common iatrogenic cause"],
        ["Metformin", "GI irritation → osmotic; affects up to 30% patients; dose-dependent"],
        ["Laxative abuse (osmotic)", "Osmotic diarrhoea — factitious; check stool osmotic gap"],
        ["NSAIDs / aspirin", "Mucosal inflammation → colitis pattern"],
        ["Orlistat", "Inhibits pancreatic lipase → steatorrhoea by design"],
        ["SSRIs (sertraline, fluoxetine)", "↑ gut serotonin → ↑ motility"],
        ["Colchicine", "Osmotic + secretory; dose-dependent"],
        ["Magnesium-containing antacids", "Osmotic diarrhoea"],
        ["Chemotherapy / radiotherapy", "Mucosal damage → inflammatory"],
        ["PPIs (omeprazole)", "↑ risk of C. diff; can cause diarrhoea directly"],
    ]
    story.append(tbl(drugs, [COL_W*0.48, COL_W*0.52]))
    story.append(sp(4))

    story.append(subsec("4. SOCIAL & TRAVEL HISTORY"))
    story.append(bull("<b>Recent travel:</b> South Asia, Africa, SE Asia → Giardia, amoebiasis, typhoid, ETEC (traveller's diarrhoea)"))
    story.append(bull("<b>Food history:</b> Undercooked chicken → Campylobacter/Salmonella; shellfish → Norovirus; buffet food → Staph toxin"))
    story.append(bull("<b>Water source:</b> Untreated well water → Giardia / Cryptosporidium"))
    story.append(bull("<b>Contact with similar illness:</b> Suggests outbreak → infective; notify PHE"))
    story.append(bull("<b>Sexual history:</b> MSM → Shigella, LGV proctitis, Herpes, gonorrhoea rectal"))
    story.append(bull("<b>Immunosuppression:</b> HIV, transplant, steroids → Cryptosporidium, CMV, Microsporidium, MAI"))
    story.append(bull("<b>Occupation:</b> Food handlers, healthcare workers → notify if notifiable pathogen"))
    story.append(sp(4))

    story.append(subsec("5. PAST MEDICAL & FAMILY HISTORY"))
    story.append(bull("Known IBD → relapse? Change in symptoms?"))
    story.append(bull("Previous GI surgery → post-cholecystectomy, short bowel, dumping syndrome"))
    story.append(bull("Diabetes → autonomic neuropathy; coeliac → 1st-degree relatives (10% prevalence)"))
    story.append(bull("Family Hx CRC → ↑ risk; Lynch syndrome (HNPCC)"))
    story.append(bull("Previous pancreatitis → exocrine insufficiency → steatorrhoea"))

    # RIGHT
    story.append(sec("CLINICAL EXAMINATION — DIARRHOEA", 'sec_teal'))
    story.append(subsec("A. DEHYDRATION ASSESSMENT — DO FIRST"))
    deh = [
        ["Grade", "Clinical Signs", "Management"],
        ["None (0%)", "Normal vitals, moist mucosa, normal skin turgor", "Oral fluids — maintain intake"],
        ["Mild (3–5%)", "Thirst, dry mouth, ↓ urine output, slight ↓ skin turgor", "ORS 50 mL/kg over 4 hrs"],
        ["Moderate (5–8%)", "Sunken eyes, poor skin turgor, tachycardia, dry mucosa, oliguria, ↓ BP postural", "ORS or IV Hartmann's"],
        ["Severe (>8%)", "Sunken eyes, mottled skin, HR >120, SBP <90, cold peripheries, confusion, anuria", "IV 0.9% NaCl / Hartmann's urgently"],
    ]
    story.append(tbl(deh, [COL_W*0.17, COL_W*0.48, COL_W*0.35]))
    story.append(sp(3))
    story.append(pearl("Assess dehydration: capillary refill >2s, skin turgor (pinch test — stays tented >2s = moderate), "
                       "sunken eyes, pulse, BP, urine output. In children: sunken fontanelle = significant dehydration."))
    story.append(sp(4))

    story.append(subsec("B. GENERAL SIGNS"))
    gen = [
        ["Finding", "Think of..."],
        ["Weight loss + pallor", "CRC, IBD, malabsorption, chronic infection"],
        ["Goitre + tachycardia + tremor", "Hyperthyroidism"],
        ["Hyperpigmentation + hypotension", "Addison's disease"],
        ["Flushing + facial telangiectasia", "Carcinoid syndrome"],
        ["Lymphadenopathy", "HIV, lymphoma, TB"],
        ["Oral ulcers + perianal disease", "Crohn's disease"],
        ["Pyoderma gangrenosum / Erythema nodosum", "IBD extra-intestinal manifestations"],
        ["Dermatitis herpetiformis", "Coeliac disease — intensely itchy blistering rash"],
    ]
    story.append(tbl(gen, [COL_W*0.4, COL_W*0.6]))
    story.append(sp(4))

    story.append(subsec("C. ABDOMINAL EXAMINATION"))
    abd = [
        ["Finding", "Interpretation"],
        ["RIF mass / tenderness", "Crohn's (terminal ileum), caecal Ca, appendicitis"],
        ["LIF tenderness", "UC, diverticular disease, sigmoid Ca"],
        ["Hepatomegaly", "Liver mets from CRC; amoebic liver abscess"],
        ["Distension + increased bowel sounds", "Infective / inflammatory — ↑ peristalsis"],
        ["Rectal mass on PR", "CRC — urgent 2WW"],
        ["Anal fissure / fistula / skin tags", "Crohn's disease"],
    ]
    story.append(tbl(abd, [COL_W*0.42, COL_W*0.58]))

    story.append(PageBreak())

    # ── PAGE 5 — INVESTIGATIONS ────────────────────────────────────────────
    # LEFT
    story.append(sec("INVESTIGATIONS — ACUTE DIARRHOEA"))
    story.append(subsec("STOOL TESTS (MOST IMPORTANT IN ACUTE)"))
    stool = [
        ["Test", "What It Detects", "When to Order"],
        ["Stool MC&S (culture)", "Bacterial pathogens (Salmonella, Shigella, Campylobacter, E. coli)", "All acute diarrhoea >48 hrs; blood in stool; travel Hx"],
        ["C. diff toxin EIA", "C. difficile toxins A and B", "Post-antibiotic; hospital-acquired; age >65"],
        ["Stool OCP\n(ova, cysts, parasites)", "Giardia, Entamoeba, Cryptosporidium, Cyclospora", "Travel Hx; prolonged diarrhoea; HIV"],
        ["Norovirus/Rotavirus PCR", "Viral gastroenteritis", "Outbreaks; children; winter season"],
        ["Faecal calprotectin", "Intestinal inflammation marker", "Chronic diarrhoea — IBD vs IBS screen"],
        ["Faecal occult blood (FOB)", "Occult GI blood loss", "Chronic diarrhoea; alarm features; CRC screening"],
        ["Faecal elastase", "Exocrine pancreatic function", "Steatorrhoea; suspected chronic pancreatitis"],
    ]
    story.append(tbl(stool, [COL_W*0.28, COL_W*0.38, COL_W*0.34]))
    story.append(sp(4))

    story.append(subsec("BLOOD TESTS"))
    blood = [
        ["Test", "What to Look For"],
        ["FBC", "Anaemia (IDA → CRC; macrocytic → coeliac B12/folate); ↑ WCC → infective/IBD"],
        ["CRP / ESR", "↑ in IBD, infective, CRC; normal in IBS"],
        ["U&E", "K⁺ ↓ + Na⁺ ↓ in severe diarrhoea; assess renal function (dehydration/HUS)"],
        ["TFTs (TSH/T4)", "Hyperthyroidism → diarrhoea"],
        ["LFTs", "Liver disease; amoebic abscess (↑ ALP, bilirubin)"],
        ["Coeliac serology (anti-tTG IgA)", "Coeliac disease — MUST be eating gluten at test time; + serum IgA level"],
        ["Serum B12/Folate/Ferritin/Vit D", "Malabsorption screen — coeliac, Crohn's, short bowel"],
        ["Gut hormones (VIP, gastrin, 5-HIAA)", "VIPoma (↑VIP), ZES (↑gastrin), carcinoid (↑5-HIAA)"],
        ["HIV test", "If risk factors — CMV, Crypto, MAI, microsporidium"],
        ["Blood cultures", "If fever + systemic illness → bacteraemia (Salmonella typhi)"],
    ]
    story.append(tbl(blood, [COL_W*0.32, COL_W*0.68]))
    story.append(sp(3))
    story.append(pearl("Faecal calprotectin is the KEY non-invasive test to distinguish IBD from IBS. "
                       "Raised (>50 μg/g) in IBD (mucosal inflammation), normal in IBS. Use BEFORE colonoscopy referral."))

    # RIGHT
    story.append(sec("INVESTIGATIONS — IMAGING & ENDOSCOPY"))
    imaging = [
        ["Investigation", "When to Use", "What It Shows"],
        ["AXR", "Acute severe colitis; suspected toxic megacolon", "Dilated colon >6 cm = toxic megacolon; thumb printing = ischaemia/IBD"],
        ["USS abdomen", "RIF pain (Crohn's vs appendicitis), liver abscess", "Bowel wall thickening; amoebic abscess; lymphadenopathy"],
        ["CT abdomen/pelvis + contrast", "Severe/complicated IBD; CRC; ischaemic colitis; abscess", "Extent of disease, mural thickening, strictures, lymph nodes, metastases"],
        ["Colonoscopy + biopsy", "Chronic diarrhoea; alarm features; IBD diagnosis/monitoring; CRC exclusion", "Mucosal changes, ulceration, polyps, Ca — GOLD STANDARD for lower GI"],
        ["OGD + duodenal biopsy (4 samples)", "Suspected coeliac; Giardia; Crohn's", "Villous atrophy (coeliac); Giardia trophozoites; Crohn's"],
        ["MR enterography", "Small bowel Crohn's; fistulae; strictures", "Non-radiation; best for Crohn's activity and complications"],
        ["Capsule endoscopy", "Suspected small bowel Crohn's or obscure GI bleed", "Full mucosal imaging; CI if stricture suspected"],
        ["Hydrogen breath test", "Lactose intolerance; SIBO", "↑ H₂ after lactulose/lactose ingestion"],
        ["SeHCAT scan", "Post-cholecystectomy diarrhoea / bile salt malabsorption", "Retained activity <15% at 7 days = BAM"],
    ]
    story.append(tbl(imaging, [COL_W*0.25, COL_W*0.35, COL_W*0.4]))
    story.append(sp(4))

    story.append(subsec("STOOL OSMOTIC GAP — DISTINGUISH SECRETORY vs OSMOTIC"))
    story.append(body("<b>Formula:</b> Osmotic gap = 290 − 2 × (stool Na⁺ + stool K⁺)"))
    gap = [
        ["Gap", "Interpretation"],
        ["< 50 mOsm/kg", "Secretory diarrhoea (electrolytes drive osmolality)"],
        ["> 125 mOsm/kg", "Osmotic diarrhoea (unabsorbed solutes drive osmolality)"],
        ["50–125 mOsm/kg", "Mixed / indeterminate"],
    ]
    story.append(tbl(gap, [COL_W*0.3, COL_W*0.7]))

    story.append(PageBreak())

    # ── PAGE 6 — MANAGEMENT ────────────────────────────────────────────────
    # LEFT
    story.append(sec("MANAGEMENT — DIARRHOEA"))
    story.append(subsec("STEP 1 — RESUSCITATION & SUPPORTIVE CARE"))
    story.append(bull("<b>Rehydration:</b> ORS (oral rehydration solution) — 1st line for mild-moderate dehydration"))
    story.append(bull("<b>IV fluids:</b> Hartmann's or 0.9% NaCl for severe dehydration / unable to tolerate oral"))
    story.append(bull("<b>Electrolytes:</b> K⁺ replacement (hypokalaemia common in severe diarrhoea)"))
    story.append(bull("<b>Diet:</b> Small frequent bland meals; continue feeding — 'starving' worsens mucosal recovery"))
    story.append(bull("<b>Hygiene / isolation:</b> Handwashing, contact precautions for C. diff / Norovirus"))
    story.append(bull("<b>Notify:</b> PHE if notifiable disease (Salmonella, Shigella, VTEC, Cryptosporidium, Cholera, Typhoid)"))
    story.append(sp(4))

    story.append(subsec("ORS — ORAL REHYDRATION SOLUTION"))
    story.append(body("WHO-ORS composition: Na⁺ 75 mmol/L + K⁺ 20 mmol/L + glucose 75 mmol/L + Cl⁻ 65 mmol/L + citrate 10 mmol/L"))
    story.append(body("Mechanism: Na⁺-glucose co-transporter (SGLT1) absorption of Na⁺ + glucose → water follows passively"))
    story.append(body("Dose: 200–400 mL after each loose stool; target 50 mL/kg over 4 hrs for mild dehydration"))
    story.append(sp(5))

    story.append(subsec("STEP 2 — ANTIDIARRHOEAL AGENTS"))
    anti = [
        ["Drug", "Mechanism", "Use / Caution"],
        ["Loperamide\n(Imodium)", "μ-opioid receptor agonist → ↓ peristalsis + ↑ sphincter tone", "Acute non-bloody diarrhoea; traveller's diarrhoea. NEVER in bloody/C. diff/EHEC (toxic megacolon risk)"],
        ["Codeine phosphate", "Central + peripheral opioid receptor agonist", "Short-term; dependency risk in chronic use"],
        ["Bismuth subsalicylate", "Antimicrobial + antisecretory", "Traveller's diarrhoea prevention + treatment"],
        ["Cholestyramine\n(bile acid sequestrant)", "Binds bile acids in gut → ↓ secretory effect", "Post-cholecystectomy bile acid malabsorption"],
        ["Octreotide\n(somatostatin analogue)", "↓ gut secretion + motility; ↓ tumour hormone secretion", "VIPoma, carcinoid diarrhoea, high-output fistula, short bowel"],
    ]
    story.append(tbl(anti, [COL_W*0.22, COL_W*0.3, COL_W*0.48]))
    story.append(sp(3))
    story.append(red("NEVER use loperamide or any antimotility agent in: C. difficile colitis, E. coli O157 EHEC, "
                     "Shigella dysentery, or any bloody diarrhoea — risk of toxic megacolon."))

    # RIGHT
    story.append(sec("MANAGEMENT — SPECIFIC TREATMENTS"))
    spec = [
        ["Cause", "First-Line Treatment", "Notes"],
        ["Viral gastroenteritis (Norovirus/Rotavirus)", "Supportive ORS", "Self-limiting 1–3 days; no antivirals; isolate in hospital"],
        ["Campylobacter", "Usually supportive", "Azithromycin 500 mg OD × 3d if severe/immunocomp"],
        ["Salmonella (non-typhi)", "Usually supportive", "Ciprofloxacin 500 mg BD × 5–7d if bacteraemic"],
        ["Typhoid (S. typhi)", "IV ceftriaxone 2g OD", "Or azithromycin; notify PHE; avoid ciprofloxacin if resistant"],
        ["Shigella (dysentery)", "Ciprofloxacin 500 mg BD × 3d", "Or azithromycin; shorten course significantly"],
        ["E. coli O157 (EHEC)", "SUPPORTIVE ONLY — no antibiotics", "Antibiotics ↑ Shiga toxin release → HUS risk; monitor renal function, FBC"],
        ["C. diff (mild-moderate)", "Oral vancomycin 125 mg QDS × 10d", "First-line per NICE 2021 — not metronidazole alone"],
        ["C. diff (severe)", "Oral vancomycin 500 mg QDS + IV metronidazole", "Severe = WCC >15, creatinine >1.5× baseline, temp >38.5°C"],
        ["C. diff (recurrent ≥2)", "Fidaxomicin 200 mg BD × 10d OR bezlotoxumab prophylaxis", "Fidaxomicin preferred for recurrence (narrower spectrum)"],
        ["C. diff (refractory)", "Faecal microbiota transplantation (FMT)", "90% efficacy for recurrent C. diff"],
        ["Giardia lamblia", "Metronidazole 400 mg TDS × 5–7d", "Or tinidazole 2g single dose"],
        ["Entamoeba histolytica", "Metronidazole + diloxanide furoate", "Metronidazole treats tissue; diloxanide clears luminal cysts"],
        ["Cryptosporidium (immunocomp)", "Nitazoxanide + optimise ART", "Self-limiting in immunocompetent"],
        ["Coeliac disease", "Strict lifelong gluten-free diet", "Monitor with anti-tTG + DXA scan; supplement B12/Vit D"],
        ["UC (acute flare)", "Oral/rectal 5-ASA (mesalazine) + steroids (prednisolone 40 mg)", "IV hydrocortisone if severe; ciclosporin/infliximab if steroid-refractory"],
        ["Crohn's (acute flare)", "Prednisolone 40 mg or budesonide (ileocaecal)", "Azathioprine/6-MP maintenance; anti-TNF (adalimumab) if biologics needed"],
        ["Carcinoid syndrome", "Octreotide LAR SC monthly injection", "+ surgical resection if localised; serotonin measured as 5-HIAA"],
        ["VIPoma", "Octreotide + surgical resection", "Streptozocin if metastatic"],
        ["Lactose intolerance", "Lactose-free diet + lactase enzyme supplements", "Not an allergy — lactase deficiency; calcium supplementation"],
        ["Post-cholecystectomy", "Cholestyramine 4 g BD-TDS", "Or colesevelam; SeHCAT test confirms diagnosis"],
    ]
    story.append(tbl(spec, [COL_W*0.3, COL_W*0.38, COL_W*0.32]))

    story.append(PageBreak())

    # ── PAGE 7 — SPECIFIC TOPICS: C. DIFF, IBD, TRAVELLER'S ──────────────
    # LEFT
    story.append(sec("C. DIFFICILE — DETAILED APPROACH"))
    story.append(subsec("RISK FACTORS"))
    story.append(bull("<b>Recent antibiotics:</b> Clindamycin (highest risk), amoxicillin, cephalosporins, ciprofloxacin"))
    story.append(bull("<b>Hospital/care home:</b> Spores persist in environment; faeco-oral spread"))
    story.append(bull("<b>Age >65:</b> ↓ immune response; ↓ microbiome diversity"))
    story.append(bull("<b>PPIs:</b> ↓ gastric acidity → ↑ spore survival"))
    story.append(bull("<b>Immunosuppression:</b> Steroids, chemotherapy, HIV"))
    story.append(sp(4))

    story.append(subsec("DIAGNOSIS"))
    cdiff_dx = [
        ["Test", "Detail"],
        ["Stool toxin EIA (GDH + toxin A/B)", "2-stage: GDH screen (sensitive) + toxin confirm (specific) — rapid; first-line"],
        ["Stool culture", "Anaerobic culture; slower; used in outbreak investigation"],
        ["PCR (NAAT)", "Most sensitive; detects toxin genes; high NPV"],
        ["Colonoscopy", "Pseudomembranes (raised yellow plaques) = pathognomonic; reserved for equivocal cases"],
        ["AXR / CT", "Toxic megacolon assessment: colon >6 cm, thumbprinting, systemic toxicity"],
    ]
    story.append(tbl(cdiff_dx, [COL_W*0.38, COL_W*0.62]))
    story.append(sp(3))

    story.append(subsec("SEVERITY GRADING"))
    cdiff_sev = [
        ["Grade", "Criteria"],
        ["Mild", "Diarrhoea only; WCC normal; no systemic features"],
        ["Moderate", "WCC 11–15 × 10⁹/L; creatinine <1.5× baseline; temp <38.5°C"],
        ["Severe", "WCC >15 × 10⁹/L; creatinine >1.5× baseline; temp >38.5°C; albumin <25"],
        ["Life-threatening", "ICU admission; hypotension; toxic megacolon (colon >6 cm); ileus; partial/full-thickness colitis"],
    ]
    story.append(tbl(cdiff_sev, [COL_W*0.22, COL_W*0.78]))
    story.append(sp(3))
    story.append(pearl("Stop the implicated antibiotic where possible. Avoid antimotility agents (loperamide). "
                       "Use contact precautions + alcohol gel is INEFFECTIVE against C. diff spores — use soap and water."))
    story.append(sp(4))

    story.append(sec("TRAVELLER'S DIARRHOEA"))
    story.append(body("<b>Definition:</b> ≥3 loose stools/day + ≥1 of: nausea, vomiting, cramps, fever, blood — occurring during or "
                      "within 10 days of return from travel."))
    story.append(sp(3))
    story.append(subsec("CAUSES BY REGION"))
    travel = [
        ["Region", "Commonest Organism"],
        ["South/SE Asia", "ETEC, EAEC, Campylobacter, typhoid, cholera"],
        ["Sub-Saharan Africa", "ETEC, Shigella, typhoid, Giardia, amoebiasis"],
        ["Latin America", "ETEC, EAEC, Shigella"],
        ["Middle East", "ETEC, Cryptosporidium, Giardia"],
    ]
    story.append(tbl(travel, [COL_W*0.35, COL_W*0.65]))
    story.append(sp(3))
    story.append(subsec("MANAGEMENT"))
    story.append(bull("ORS + bland diet; loperamide for non-bloody watery (symptom control for travel)"))
    story.append(bull("Azithromycin 500 mg OD × 3d — antibiotic of choice (safe, covers resistant Campylobacter)"))
    story.append(bull("Ciprofloxacin 500 mg BD × 3d — alternative (↑ resistance in SE Asia)"))
    story.append(bull("Prevention: 'Boil it, cook it, peel it or forget it'; avoid tap water/ice/street food"))

    # RIGHT
    story.append(sec("INFLAMMATORY BOWEL DISEASE — DIARRHOEA COMPARISON"))
    ibd = [
        ["Feature", "Ulcerative Colitis", "Crohn's Disease"],
        ["Diarrhoea type", "Bloody mucoid; urgency; tenesmus", "Variable — watery or bloody; malabsorptive if small bowel"],
        ["Location", "Continuous from rectum (always involves rectum)", "Any part of GI tract (mouth to anus); skip lesions"],
        ["Depth of inflammation", "Mucosal only (superficial)", "Transmural (full thickness)"],
        ["Histology", "Crypt abscesses; goblet cell depletion; no granulomas", "Non-caseating granulomas; cobblestone mucosa"],
        ["Key complication", "Toxic megacolon; colorectal Ca (risk ↑ with extent/duration)", "Strictures; fistulae; perianal disease; short bowel"],
        ["Smoking effect", "Protective (paradoxically)", "Worsens disease (pro-inflammatory)"],
        ["Extra-intestinal", "Pyoderma gangrenosum; episcleritis; PSC (closely linked); erythema nodosum; arthritis", "Same as UC + metabolic bone disease + gallstones + renal stones"],
        ["Colonoscopy", "Continuous mucosal inflammation; pseudopolyps; no skip lesions", "Skip lesions; cobblestone appearance; deep linear ulcers; strictures"],
        ["Surgery", "Proctocolectomy = curative", "Surgery NOT curative — reserved for complications"],
        ["CRC risk", "↑↑ with pancolitis >8 years; annual surveillance", "Mildly ↑ with colonic involvement"],
    ]
    story.append(tbl(ibd, [COL_W*0.25, COL_W*0.375, COL_W*0.375]))
    story.append(sp(4))
    story.append(pearl("UC always involves rectum; Crohn's spares rectum in 50%. Granulomas = Crohn's (NOT UC). "
                       "Smoking protects against UC but worsens Crohn's — classic OSCE trap."))
    story.append(sp(4))

    story.append(sec("HUS — HAEMOLYTIC URAEMIC SYNDROME"))
    story.append(body("<b>Classic Triad:</b> Microangiopathic haemolytic anaemia + Thrombocytopaenia + Acute kidney injury"))
    story.append(bull("Caused by Shiga toxin from E. coli O157:H7 (also Shigella dysenteriae)"))
    story.append(bull("Shiga toxin damages endothelium of glomeruli → thrombotic microangiopathy → AKI"))
    story.append(bull("<b>Presentation:</b> Child with bloody diarrhoea → sudden oliguria + pallor + petechiae 5–10 days later"))
    hus_inv = [
        ["Investigation", "Finding"],
        ["FBC", "↓ Hb (haemolytic: fragments on film) + ↓ platelets"],
        ["Blood film", "Schistocytes (fragmented RBCs) — microangiopathic haemolysis"],
        ["U&E / creatinine", "↑↑ creatinine — AKI"],
        ["LDH", "↑ — haemolysis marker"],
        ["Stool culture + Shiga toxin", "E. coli O157 — confirms diagnosis"],
        ["Urinalysis", "Haematuria + proteinuria"],
    ]
    story.append(tbl(hus_inv, [COL_W*0.35, COL_W*0.65]))
    story.append(sp(3))
    story.append(bull("<b>Management:</b> Supportive — IV fluids, blood transfusion, dialysis if severe AKI"))
    story.append(bull("<b>NO antibiotics, NO antidiarrhoeals</b> — both ↑ toxin release → worsen HUS"))

    story.append(PageBreak())

    # ── PAGE 8 — OSCE VIVA Q&A + MNEMONICS ───────────────────────────────
    # LEFT
    story.append(sec("DIARRHOEA — OSCE VIVA Q&A"))
    viva = [
        ["Examiner Asks...", "Your Answer"],
        ["Define diarrhoea", "≥3 loose stools/day or stool volume >200 mL/day or weight >200 g/day"],
        ["Secretory vs osmotic — key test?", "Fasting: secretory continues; osmotic stops. Stool osmotic gap: >125 = osmotic; <50 = secretory"],
        ["What does nocturnal diarrhoea mean?", "Organic cause (IBD, secretory, CRC) — IBS NEVER causes nocturnal symptoms"],
        ["Most common bacterial cause in UK?", "Campylobacter jejuni — undercooked poultry"],
        ["Traveller's diarrhoea — commonest organism?", "ETEC (enterotoxigenic E. coli) — watery, no blood, tropical travel"],
        ["C. diff risk factors?", "Recent antibiotics (esp. clindamycin), hospitalisation, age >65, PPIs, immunosuppression"],
        ["C. diff 1st line treatment (NICE 2021)?", "Oral vancomycin 125 mg QDS × 10 days (NOT metronidazole monotherapy)"],
        ["Why NO antibiotics in E. coli O157?", "Antibiotics lyse bacteria → release Shiga toxin → ↑ HUS risk; supportive only"],
        ["Best test to distinguish IBD from IBS?", "Faecal calprotectin — raised in IBD (>50 μg/g); normal in IBS"],
        ["Carcinoid syndrome — diagnosis?", "24-hr urinary 5-HIAA (5-hydroxyindoleacetic acid); octreotide scan for tumour localisation"],
        ["Patient: diarrhoea + flushing + wheeze. Diagnosis?", "Carcinoid syndrome — metastatic carcinoid to liver; treat with octreotide"],
        ["Coeliac serology — what must patient be doing?", "Eating gluten — anti-tTG IgA is falsely negative on gluten-free diet"],
        ["Child with bloody diarrhoea → oliguria + pallor. Diagnosis?", "HUS — E. coli O157; triad: haemolysis + ↓plt + AKI; supportive only"],
        ["Post-cholecystectomy diarrhoea — mechanism?", "Bile acid malabsorption → secretory diarrhoea; confirm with SeHCAT scan; treat with cholestyramine"],
        ["What is FMT and when is it used?", "Faecal microbiota transplantation — for recurrent C. diff (≥2 episodes); 90% efficacy; restores gut flora"],
        ["C. diff soap vs alcohol gel?", "Soap and water ONLY — alcohol gel does NOT kill C. diff spores"],
        ["IBS vs IBD — key difference?", "IBD: abnormal investigations (↑ CRP, ↑ calprotectin, colonoscopy abnormal). IBS: all normal; Rome IV diagnosis of exclusion"],
        ["Steatorrhoea investigation sequence?", "Anti-tTG IgA (coeliac) → faecal elastase (pancreatic insufficiency) → OGD + duodenal biopsy → Giardia stool OCP"],
    ]
    story.append(tbl(viva, [COL_W*0.42, COL_W*0.58]))

    # RIGHT
    story.append(sec("MNEMONICS & HIGH-YIELD SUMMARY"))
    story.append(subsec("CAUSES — 'ABCDE'"))
    abcde = [
        ["Letter", "Category", "Example"],
        ["A", "Antibiotics / post-infective", "C. diff, antibiotic-associated diarrhoea"],
        ["B", "Bowel disease (IBD / Ca)", "Crohn's, UC, CRC, ischaemic colitis"],
        ["C", "Coeliac / Chronic pancreatitis", "Steatorrhoea — malabsorption pattern"],
        ["D", "Drugs / Diet", "Laxatives, metformin, orlistat, lactose"],
        ["E", "Endocrine / Exotic infection", "Carcinoid, VIPoma, thyroid; Giardia, amoebiasis"],
    ]
    story.append(tbl(abcde, [COL_W*0.08, COL_W*0.32, COL_W*0.6], hdr_bg=TEAL))
    story.append(sp(4))

    story.append(subsec("MECHANISM — 'SOME'"))
    some = [
        ["Letter", "Mechanism", "Distinguishing Feature"],
        ["S", "Secretory", "CONTINUES on fasting; large volume watery; osmotic gap <50"],
        ["O", "Osmotic", "STOPS on fasting; bloating/gas; osmotic gap >125"],
        ["M", "Motility", "Variable — urgency or slow transit; no structural cause"],
        ["E", "Exudative", "Blood + pus + mucus; small volume; tenesmus"],
    ]
    story.append(tbl(some, [COL_W*0.08, COL_W*0.22, COL_W*0.7], hdr_bg=DARK_BLUE))
    story.append(sp(4))

    story.append(subsec("HIGH-RISK ORGANISMS — QUICK RECALL"))
    hro = [
        ["Organism", "Key Feature", "Treatment"],
        ["C. difficile", "Post-antibiotics; pseudomembranes on scope", "Oral vancomycin 125 mg QDS"],
        ["E. coli O157", "Bloody diarrhoea → HUS (haemolysis + AKI + ↓plt)", "SUPPORTIVE ONLY — no antibiotics"],
        ["Vibrio cholerae", "Rice-water stools; massive volume", "ORS + doxycycline"],
        ["Shigella", "Dysentery + tenesmus; tiny inoculum", "Ciprofloxacin or azithromycin"],
        ["Entamoeba histolytica", "Bloody dysentery + liver abscess", "Metronidazole + diloxanide"],
        ["Giardia lamblia", "Steatorrhoea; contaminated water; prolonged", "Metronidazole 400 mg TDS × 5–7d"],
        ["Cryptosporidium", "HIV/immunocomp; prolonged watery", "Nitazoxanide + ART"],
        ["Typhoid (S. typhi)", "Relative bradycardia + rose spots + constipation then diarrhoea", "IV ceftriaxone"],
    ]
    story.append(tbl(hro, [COL_W*0.3, COL_W*0.38, COL_W*0.32]))
    story.append(sp(4))

    story.append(green("FINAL OSCE TIP: In any acute diarrhoea scenario — your first 3 answers are: "
                       "1) Assess dehydration (vitals, skin turgor, urine output), "
                       "2) Stool MC&S + C. diff toxin EIA, "
                       "3) ORS / IV fluids + electrolyte correction. Then investigate the cause."))

    doc.build(story, canvasmaker=BookletCanvas)
    print("Diarrhoea booklet built:", out)

build()
Running Command

cd /home/daytona/workspace/osce-booklets && python3 build_constipation.py 2>&1 && python3 build_diarrhoea.py 2>&1

Running Command

python3 -c " from pypdf import PdfReader for f, label in [ ('/home/daytona/workspace/osce-booklets/Constipation_OSCE_Booklet.pdf', 'CONSTIPATION'), ('/home/daytona/workspace/osce-booklets/Diarrhoea_OSCE_Booklet.pdf', 'DIARRHOEA'), ]: r = PdfReader(f) import os sz = os.path.getsize(f) print(f'{label}: {len(r.pages)} pages, {sz//1024} KB') for i, p in enumerate(r.pages): t = p.extract_text() print(f' p{i+1}: {t[:60].strip()}') "

Both booklets built perfectly. Here are your two separate downloads:

📗

Constipation OSCE Booklet

PDF Document · PDF

— 7 pages
📘

Diarrhoea OSCE Booklet

PDF Document · PDF

— 11 pages

Constipation booklet (7 pages) covers:
  • Cover page with topic tags
  • Definition + Rome IV criteria
  • Causes mnemonic — 'SPINE'
  • Full history taking (6 sections including drug Hx + alarm features)
  • Clinical examination (General, Abdomen, PR/DRE)
  • Investigations (bloods + imaging + anorectal manometry + defaecating proctogram)
  • Management step ladder — Lifestyle → Laxatives table → Specific causes → Special situations (elderly, pregnancy)
  • Hirschsprung's disease full section
  • OSCE Viva Q&A + Alarm features 'ABCDE' + Bristol Stool Chart
Diarrhoea booklet (11 pages) covers:
  • Definition + classification by duration and mechanism — 'SOME'
  • Full infective causes: Viral, Bacterial (10 organisms), Protozoal tables
  • Non-infective causes (IBD, malabsorption, drugs, endocrine, CRC, functional)
  • Full history taking + drug Hx + travel Hx
  • Dehydration assessment grading table
  • Full investigation panel (stool tests, bloods, imaging, endoscopy, osmotic gap)
  • Management: ORS, antidiarrhoeals, specific treatments for 20 conditions
  • C. difficile detailed section (risk factors, diagnosis, severity grading)
  • Traveller's diarrhoea by region
  • IBD comparison table (UC vs Crohn's)
  • HUS section
  • OSCE Viva Q&A (18 Q&As) + Mnemonics 'ABCDE' + 'SOME' + organism quick-recall table

Not only those heading written in the booklet I attached...whatever clinical things I need to know related to both topics...taken from the top books referred by professors ...like must to face a final year mbbs

Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Reading File
Reading File
Writing File

~/osce-booklets/build_constipation_v2.py

"""
CONSTIPATION — Comprehensive Final Year MBBS OSCE Booklet
Sources: Bailey & Love 28e, Sleisenger & Fordtran, Sabiston, Harrison's 22e,
         Goldman-Cecil, Kumar & Clark, Oxford Handbook
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame,
    Paragraph, Spacer, Table, TableStyle, HRFlowable, NextPageTemplate, PageBreak)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.pdfgen import canvas as pdfgen_canvas

NAVY=colors.HexColor('#1a2a4a'); GOLD=colors.HexColor('#c8a951')
DKBLUE=colors.HexColor('#2c5282'); TEAL=colors.HexColor('#2a7a6b')
REDBG=colors.HexColor('#fdecea'); REDBDR=colors.HexColor('#c0392b')
GRNBG=colors.HexColor('#e8f5e9'); GRNBDR=colors.HexColor('#2e7d32')
AMBG=colors.HexColor('#fff8e1'); AMBDR=colors.HexColor('#f57f17')
PURPBG=colors.HexColor('#f3e5f5'); PURPBDR=colors.HexColor('#7b1fa2')
WHITE=colors.white; BLACK=colors.black
GL=colors.HexColor('#f2f4f7'); GM=colors.HexColor('#d5dce8')
PW,PH=A4; ML=13*mm; MR=13*mm; HH=18*mm; FH=10*mm
UW=PW-ML-MR; CG=6*mm; CW=(UW-CG)/2

def S():
    d={}
    d['TM']=ParagraphStyle('TM',fontName='Helvetica-Bold',fontSize=18,textColor=WHITE,leading=22,alignment=TA_CENTER)
    d['TS']=ParagraphStyle('TS',fontName='Helvetica-BoldOblique',fontSize=10,textColor=GOLD,leading=13,alignment=TA_CENTER)
    d['TT']=ParagraphStyle('TT',fontName='Helvetica',fontSize=8,textColor=colors.HexColor('#a0bcd8'),leading=11,alignment=TA_CENTER)
    d['SH']=ParagraphStyle('SH',fontName='Helvetica-Bold',fontSize=9,textColor=WHITE,backColor=DKBLUE,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6)
    d['ST']=ParagraphStyle('ST',fontName='Helvetica-Bold',fontSize=9,textColor=WHITE,backColor=TEAL,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6)
    d['SR']=ParagraphStyle('SR',fontName='Helvetica-Bold',fontSize=9,textColor=WHITE,backColor=REDBDR,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6)
    d['SP']=ParagraphStyle('SP',fontName='Helvetica-Bold',fontSize=9,textColor=WHITE,backColor=colors.HexColor('#7b1fa2'),borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6)
    d['SS']=ParagraphStyle('SS',fontName='Helvetica-Bold',fontSize=8.5,textColor=DKBLUE,leading=11,spaceAfter=3,spaceBefore=5)
    d['BD']=ParagraphStyle('BD',fontName='Helvetica',fontSize=7.8,textColor=BLACK,leading=11,spaceAfter=2)
    d['BL']=ParagraphStyle('BL',fontName='Helvetica',fontSize=7.8,textColor=BLACK,leading=11,leftIndent=10,bulletIndent=2,spaceAfter=1.5)
    d['BB']=ParagraphStyle('BB',fontName='Helvetica-Bold',fontSize=7.8,textColor=DKBLUE,leading=11,leftIndent=10,bulletIndent=2,spaceAfter=1.5)
    d['PE']=ParagraphStyle('PE',fontName='Helvetica-BoldOblique',fontSize=7.8,textColor=colors.HexColor('#7b3f00'),backColor=AMBG,borderColor=AMBDR,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4)
    d['RF']=ParagraphStyle('RF',fontName='Helvetica-Bold',fontSize=7.8,textColor=REDBDR,backColor=REDBG,borderColor=REDBDR,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4)
    d['GN']=ParagraphStyle('GN',fontName='Helvetica',fontSize=7.8,textColor=colors.HexColor('#1b5e20'),backColor=GRNBG,borderColor=GRNBDR,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4)
    d['PU']=ParagraphStyle('PU',fontName='Helvetica-BoldOblique',fontSize=7.8,textColor=colors.HexColor('#4a148c'),backColor=PURPBG,borderColor=PURPBDR,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4)
    d['TH']=ParagraphStyle('TH',fontName='Helvetica-Bold',fontSize=7.5,textColor=WHITE,leading=10,alignment=TA_CENTER)
    d['TC']=ParagraphStyle('TC',fontName='Helvetica',fontSize=7.5,textColor=BLACK,leading=10)
    d['TB']=ParagraphStyle('TB',fontName='Helvetica-Bold',fontSize=7.5,textColor=DKBLUE,leading=10)
    d['FT']=ParagraphStyle('FT',fontName='Helvetica-Oblique',fontSize=6.5,textColor=colors.HexColor('#aaaaaa'),leading=9,alignment=TA_CENTER)
    return d
ST=S()
FOOTER='Sources: Bailey & Love 28e · Sleisenger & Fordtran · Sabiston Surgery · Harrison\'s 22e · Goldman-Cecil · Oxford Handbook'

class BC(pdfgen_canvas.Canvas):
    def __init__(self,f,**kw): super().__init__(f,**kw); self._sp=[]; self._pg=0
    def showPage(self): self._sp.append(dict(self.__dict__)); self._startPage()
    def save(self):
        for st in self._sp:
            self.__dict__.update(st); self._dec(); super().showPage()
        super().save()
    def _dec(self):
        self._pg+=1; c=self
        c.setFillColor(NAVY); c.rect(0,PH-HH,PW,HH,fill=1,stroke=0)
        c.setFillColor(GOLD); c.rect(0,PH-HH-1.5,PW,1.5,fill=1,stroke=0)
        c.setFillColor(GOLD); c.setFont('Helvetica-Bold',8); c.drawString(ML,PH-10*mm,f"Page {self._pg}")
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',12); c.drawCentredString(PW/2,PH-8.5*mm,"CONSTIPATION")
        c.setFont('Helvetica',7.5); c.setFillColor(colors.HexColor('#a0bcd8'))
        c.drawCentredString(PW/2,PH-13.5*mm,"Clinical OSCE Notes  |  Final Year MBBS")
        c.setFillColor(GOLD); c.setFont('Helvetica-Bold',7.5)
        c.drawString(ML,PH-HH-8,"◀ LEFT"); c.drawRightString(PW-MR,PH-HH-8,"RIGHT ▶")
        c.setFillColor(NAVY); c.rect(0,0,PW,FH+3,fill=1,stroke=0)
        c.setFillColor(GOLD); c.rect(0,FH+3,PW,1,fill=1,stroke=0)
        c.setFillColor(colors.HexColor('#aaaaaa')); c.setFont('Helvetica-Oblique',6)
        c.drawCentredString(PW/2,4*mm,FOOTER)
        if self._pg>1:
            c.setStrokeColor(GM); c.setLineWidth(0.5)
            c.line(ML+CW+CG/2,PH-HH-12,ML+CW+CG/2,FH+6)

def sec(t,s='SH'): return Paragraph(f"<b>{t}</b>",ST[s])
def ss(t): return Paragraph(t,ST['SS'])
def bd(t): return Paragraph(t,ST['BD'])
def bl(t): return Paragraph(f"• {t}",ST['BL'])
def bb(t): return Paragraph(f"• {t}",ST['BB'])
def pe(t): return Paragraph(f"★ OSCE PEARL: {t}",ST['PE'])
def rf(t): return Paragraph(f"⚠ RED FLAG: {t}",ST['RF'])
def gn(t): return Paragraph(t,ST['GN'])
def pu(t): return Paragraph(f"🔬 PATHOPHYSIOLOGY: {t}",ST['PU'])
def sp(h=3): return Spacer(1,h)
def tbl(data,w,bg=None):
    if bg is None: bg=DKBLUE
    ts=[('BACKGROUND',(0,0),(-1,0),bg),('TEXTCOLOR',(0,0),(-1,0),WHITE),
        ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'),('FONTSIZE',(0,0),(-1,-1),7.5),
        ('ROWBACKGROUNDS',(0,1),(-1,-1),[GL,WHITE]),('GRID',(0,0),(-1,-1),0.4,GM),
        ('LEFTPADDING',(0,0),(-1,-1),4),('RIGHTPADDING',(0,0),(-1,-1),4),
        ('TOPPADDING',(0,0),(-1,-1),3),('BOTTOMPADDING',(0,0),(-1,-1),3),('VALIGN',(0,0),(-1,-1),'TOP')]
    wr=[]
    for r,row in enumerate(data):
        wr.append([Paragraph(c,ST['TH'] if r==0 else ST['TC']) if isinstance(c,str) else c for c in row])
    return Table(wr,colWidths=w,style=TableStyle(ts),hAlign='LEFT')

def build():
    out='/home/daytona/workspace/osce-booklets/Constipation_OSCE_Booklet_v2.pdf'
    by=FH+8; ty=PH-HH-14; fh=ty-by
    cf=Frame(ML,by,UW,fh,leftPadding=0,rightPadding=0,topPadding=0,bottomPadding=0)
    lf=Frame(ML,by,CW,fh,leftPadding=0,rightPadding=3,topPadding=0,bottomPadding=0)
    rf=Frame(ML+CW+CG,by,CW,fh,leftPadding=3,rightPadding=0,topPadding=0,bottomPadding=0)
    doc=BaseDocTemplate(out,pagesize=A4,leftMargin=ML,rightMargin=MR,topMargin=22*mm,bottomMargin=16*mm)
    doc.addPageTemplates([PageTemplate(id='cover',frames=[cf]),PageTemplate(id='two',frames=[lf,rf])])
    s=[]

    # COVER
    s+=[NextPageTemplate('cover'),Spacer(1,26*mm),
        Paragraph("CONSTIPATION",ST['TM']),Spacer(1,4),
        Paragraph("Clinical OSCE Notes  |  Final Year MBBS",ST['TS']),Spacer(1,6),
        Paragraph("Epidemiology · Pathophysiology · Rome IV · History Taking · Examination<br/>Investigations · Laxative Ladder · Specific Disorders · Surgical Options · OSCE Q&amp;A",ST['TT']),
        Spacer(1,7),HRFlowable(width="80%",thickness=1.5,color=GOLD,spaceAfter=8,hAlign='CENTER'),
        Paragraph(FOOTER,ST['FT']),PageBreak()]

    # PAGE 2 — DEFINITION, EPIDEMIOLOGY, PATHOPHYSIOLOGY, CAUSES
    s.append(NextPageTemplate('two'))

    s+=[sec("CONSTIPATION — DEFINITION & EPIDEMIOLOGY"),
        bd("<b>Definition (Rome IV):</b> A disorder characterised by unsatisfactory defecation due to infrequent stools, "
           "difficult stool passage, or both for ≥3 months. Stricter 'chronic constipation' definition requires ≥6 months "
           "and failure of ≥2 laxative classes."),sp(3),
        bd("<b>Prevalence:</b> ~10% worldwide self-reported; true chronic constipation ~0.5%. Female:Male = 2:1 (self-reported); "
           "9:1 for chronic constipation. Increases with age — affects >50% of those over 65."),sp(4),
        sec("ROME IV DIAGNOSTIC CRITERIA FOR FUNCTIONAL CONSTIPATION"),
        bd("Must satisfy ≥2 of the following, present during >25% of defaecations, for the last 3 months with symptom onset ≥6 months ago:"),
        sp(2)]
    rome=[["Criterion","Detail"],
          ["1. Straining","Excessive effort to pass stool — suggests outlet dysfunction or slow transit"],
          ["2. Hard/lumpy stools","Bristol stool type 1 or 2 — indicates prolonged colonic transit time"],
          ["3. Incomplete evacuation","Sensation of retained stool — suggests outlet dysfunction or rectocoele"],
          ["4. Anorectal blockage","Feeling of obstruction or blockage at anus/rectum"],
          ["5. Manual manoeuvres","Digital evacuation or support of pelvic floor; indicates pelvic floor failure"],
          ["6. <3 BM/week","Fewer than 3 spontaneous bowel movements per week"],
          ["PLUS","Loose stools rarely present without laxatives; insufficient criteria for IBS"]]
    s+=[tbl(rome,[CW*0.38,CW*0.62]),sp(3),
        pe("Rome IV distinguishes functional constipation from IBS-C: IBS-C requires abdominal pain LINKED to bowel habit change. "
           "In functional constipation, pain may be present but is NOT the dominant symptom."),sp(4),
        sec("PATHOPHYSIOLOGY — MECHANISMS OF CONSTIPATION",'ST'),
        pu("Normal defaecation requires: colonic propulsion (HAPCs — high-amplitude propagating contractions) → rectal filling "
           "→ rectal distension activates RAIR (recto-anal inhibitory reflex, relaxes IAS) → puborectalis relaxes, anorectal "
           "angle opens → straining raises intra-abdominal pressure → stool expulsion. Failure at ANY step = constipation."),sp(3)]
    path=[["Mechanism","Pathophysiology","Result"],
          ["Slow colonic transit","↓ HAPCs + ↑ segmenting (non-propulsive) contractions in sigmoid. "
           "Enteric excitatory motor innervation changes. Also ↑ retrograde pressure waves.","Stool stays in colon; water absorbed → hard stools"],
          ["Pelvic floor dyssynergia\n(outlet dysfunction)","Paradoxical contraction of puborectalis/EAS during straining instead of relaxing. "
           "Failure of RAIR relaxation. Also called anismus or dyssynergic defaecation.","Functional obstruction — stool reaches rectum but cannot exit"],
          ["Megarectum /\nrectal hyposensitivity","Blunted sensation of rectal fullness → reduced call to stool. "
           "Rectum chronically loaded without urge.","Infrequent, difficult defaecation despite loaded rectum"],
          ["Reduced colonic\npropulsion (IBS-C)","Visceral hypersensitivity + altered gut-brain axis signalling. "
           "No structural defect.","Pain + bloating + constipation"],
          ["Hirschsprung's","Absent ganglion cells → tonic contraction of aganglionic segment → functional obstruction","Neonatal / childhood constipation"]]
    s+=[tbl(path,[CW*0.28,CW*0.42,CW*0.3],'ST'),]

    # RIGHT PAGE 2
    s+=[sec("CAUSES OF CONSTIPATION — COMPREHENSIVE"),ss("PRIMARY (FUNCTIONAL) CAUSES")]
    prim=[["Type","Features"],
          ["Slow transit constipation\n(colonic inertia)","Predominantly female (>95%). ↓ HAPCs throughout colon. May start in childhood. "
           "Normal or dilated colon on AXR/contrast. Responds to prucalopride (5-HT₄ agonist)."],
          ["Pelvic floor dyssynergia\n(anismus)","Paradoxical puborectalis contraction on straining. Normal transit but outlet obstruction. "
           "Diagnosed by anorectal manometry + balloon expulsion test + defaecating proctogram. Treat with biofeedback."],
          ["IBS-Constipation (IBS-C)","Rome IV: recurrent abdominal pain ≥1 day/week + change in stool form/frequency. "
           "Visceral hypersensitivity. Manage with low-FODMAP diet, linaclotide, antispasmodics."],
          ["Rectocoele","Anterior rectal wall herniates into posterior vagina. Women only. Stool traps in pouch. "
           "Need to digitate vaginally to empty. Proctogram confirms."],
          ["Intussusception /\nrectal prolapse","Mucosal intussusception can obstruct rectal outlet. Full rectal prolapse = chronic straining, "
           "incomplete evacuation. Confirm on proctogram."]]
    s+=[tbl(prim,[CW*0.32,CW*0.68]),sp(3),ss("SECONDARY CAUSES — 'SPINE'")]
    spine=[["S","Structural","Colorectal Ca (must exclude), benign strictures, anal fissure, haemorrhoids (fear of pain), "
            "external compression (pelvic mass), diverticular stricture, adhesional"],
           ["P","Physiological/Dietary","Low fibre (<25g/day), low fluid intake, immobility, IBS-C, pregnancy (progesterone + iron supplements), obesity"],
           ["I","Iatrogenic — Drugs","Opioids (μ-receptor: ↓ peristalsis + ↑ sphincter tone), Al/Ca antacids, iron, anticholinergics (TCAs, oxybutynin), "
            "verapamil (CCBs), ondansetron, antipsychotics (clozapine), antidiarrhoeals"],
           ["N","Neurological","Parkinson's (↓ dopamine → ↓ gut motility), MS, spinal cord injury, cauda equina syndrome, "
            "autonomic neuropathy (DM), Hirschsprung's (congenital), pseudo-obstruction"],
           ["E","Endocrine/Metabolic","Hypothyroidism (#1 metabolic), hypercalcaemia (Ca²⁺↑), hypokalaemia (K⁺↓), "
            "hypomagnesaemia, diabetes (autonomic), uraemia, pregnancy, panhypopituitarism"]]
    s+=[tbl([["Letter","Category","Detail"]]+spine,[CW*0.07,CW*0.27,CW*0.66]),sp(3),
        rf("New-onset constipation in a patient >50 years is colorectal carcinoma until proven otherwise. "
           "Alarm features (rectal bleeding, weight loss, IDA, palpable mass, family Hx CRC) → urgent colonoscopy/2WW referral.")]

    s.append(PageBreak())

    # PAGE 3 — HISTORY TAKING FULL
    s+=[sec("HISTORY TAKING — CONSTIPATION"),
        bd("<b>Opening:</b> 'Can you describe your bowel habit? How often do you open your bowels? Has this changed recently?'"),sp(3),
        ss("1. NATURE OF CONSTIPATION")]
    hx1=[["Question","What It Tells You","Clinical Implication"],
         ["Frequency (BM/week)?","<3/week = Rome IV criterion","Chronic if ≥6 months"],
         ["Stool consistency?","Bristol 1-2 = slow transit","Ask patient to describe or use chart"],
         ["Straining?","Outlet dysfunction / dyssynergia","Test with balloon expulsion test"],
         ["Incomplete evacuation?","Outlet dysfunction, rectocoele","Proctogram if confirmed"],
         ["Manual assistance?","Severe pelvic floor disorder","Digitation vaginally/anally"],
         ["Duration — acute or chronic?","Acute → obstruction/Ca; Chronic → functional","Key discriminator"],
         ["Alternating diarrhoea?","IBS-C; OR overflow around impaction","Never diagnose overflow with antidiarrhoeals!"],
         ["Rectal bleeding?","CRC, IBD, haemorrhoids, fissure","ALARM — investigate urgently"]]
    s+=[tbl(hx1,[CW*0.32,CW*0.36,CW*0.32]),sp(3),ss("2. ASSOCIATED SYMPTOMS")]
    hx2=[["Symptom","Consider"],
         ["Abdominal pain relieved by defaecation","IBS-C (Rome IV hallmark)"],
         ["Weight loss + change in bowel habit","CRC — urgent 2WW"],
         ["Bloating + ribbony stools + tenesmus","Rectal/sigmoid carcinoma"],
         ["Fatigue + cold intolerance + weight gain + dry skin","Hypothyroidism → TSH"],
         ["Polyuria + polydipsia + nausea + bone pain","Hypercalcaemia (Bones, Stones, Groans, Moans)"],
         ["Absolute constipation + vomiting + distension","Large bowel obstruction — EMERGENCY"],
         ["Perianal pain + blood on paper","Anal fissure — pain → voluntary withholding"],
         ["Neurological symptoms (leg weakness, sensory loss)","Cauda equina / spinal cord pathology — EMERGENCY"],
         ["Urinary symptoms + pelvic prolapse","Pelvic floor disorder (rectocoele, cystocoele)"]]
    s+=[tbl(hx2,[CW*0.48,CW*0.52]),sp(3),
        rf("Cauda equina syndrome: saddle anaesthesia + bilateral leg weakness + urinary/faecal incontinence + constipation → "
           "MRI SPINE EMERGENCY. Do NOT delay.")]

    # RIGHT PAGE 3
    s+=[ss("3. DRUG HISTORY")]
    drugs=[["Drug Class","Example","Mechanism"],
           ["Opioids (#1 cause)","Morphine, codeine, tramadol, fentanyl","μ-receptor: ↓ peristalsis + ↑ sphincter tone + ↓ rectal sensation"],
           ["Antacids","Al(OH)₃, CaCO₃ (Rennies, Gaviscon)","Bind water + insoluble stool formation"],
           ["Iron supplements","Ferrous sulphate","Direct GI mucosal irritation + dark hard stool"],
           ["Anticholinergics","TCAs, oxybutynin, hyoscine, antihistamines","↓ ACh → ↓ smooth muscle activity in gut"],
           ["Calcium channel blockers","Verapamil >> amlodipine","↓ L-type Ca²⁺ channels → ↓ gut smooth muscle contraction"],
           ["5-HT₃ antagonists","Ondansetron, granisetron","Block serotonin-mediated peristalsis"],
           ["Antipsychotics","Clozapine, olanzapine, quetiapine","Anticholinergic + anti-adrenergic effect on gut"],
           ["Antihypertensives","Clonidine (α₂-agonist)","Central + peripheral reduction in gut motility"],
           ["Diuretics","Loop + thiazide diuretics","Dehydration → hard stools"],
           ["Antidiarrhoeals","Loperamide, codeine","Opioid effect on gut"],
           ["Anticonvulsants","Gabapentin, pregabalin, phenytoin","↓ neuronal firing including ENS"]]
    s+=[tbl(drugs,[CW*0.27,CW*0.3,CW*0.43]),sp(3),
        pe("Always ask about over-the-counter preparations — iron, antacids (Al-based), and laxative abuse (paradoxical atonic colon). "
           "Opioid-induced constipation (OIC) is the most common drug cause in hospital settings."),sp(3),
        ss("4. SOCIAL HISTORY")]
    soc=[["Area","Ask","Significance"],
         ["Diet","Fibre intake — fruit, veg, wholegrains?","Target 25-30g/day; <15g/day = significant risk"],
         ["Fluids","Daily fluid intake?","1.5-2 L/day needed for fibre to work; dehydration → hard stool"],
         ["Exercise","Physical activity level?","Aerobic exercise stimulates colonic motility directly"],
         ["Mobility","Bed-bound? Wheelchair-bound?","Institutionalised patients: near-universal constipation"],
         ["Pregnancy","Weeks gestation? Iron supplements?","Progesterone ↓ motility; iron tablets compound"],
         ["Psychosocial","Stress, anxiety, eating disorders?","IBS-C, anorexia → severe constipation; also abuse Hx"],
         ["Laxative use","What, how long, any benefit?","Long-term stimulants → atonic (cathartic) colon"],
         ["Occupation","Shifts? Irregular toileting?","Ignoring urge → blunted rectal sensation over time"]]
    s+=[tbl(soc,[CW*0.17,CW*0.3,CW*0.53]),sp(3),ss("5. PAST MEDICAL & FAMILY HISTORY"),
        bl("Previous colorectal/pelvic surgery: adhesions, nerve damage, rectocoele formation"),
        bl("Previous hysterectomy: pelvic floor denervation → common trigger for slow transit constipation"),
        bl("Known CRC / IBD / diverticular disease / anal fissure"),
        bl("Hypothyroidism, DM, hypercalcaemia, renal disease, Parkinson's, MS"),
        bl("Childhood constipation since birth → consider Hirschsprung's disease"),
        bl("<b>Family Hx CRC or FAP:</b> 1st degree relative → colonoscopy from age 40-45 or 10 yrs before index case")]

    s.append(PageBreak())

    # PAGE 4 — EXAMINATION
    s+=[sec("CLINICAL EXAMINATION — CONSTIPATION",'ST'),
        ss("A. GENERAL INSPECTION")]
    gen=[["Sign","Think of..."],
         ["Weight loss + pallor","Occult CRC bleeding; eating disorder; widespread motility disorder"],
         ["Weight gain + bradycardia + dry skin + periorbital oedema","Hypothyroidism → check TSH immediately"],
         ["Confusion + polyuria + constipation","Hypercalcaemia → corrected Ca²⁺"],
         ["Tremor + rigidity + bradykinesia","Parkinson's disease → neurogenic constipation"],
         ["Abdominal scars","Previous pelvic/abdominal surgery → adhesions"],
         ["Abdominal distension","Loaded colon, obstruction, megacolon"],
         ["Signs of anaemia (conjunctival pallor)","IDA from occult CRC bleeding"]]
    s+=[tbl(gen,[CW*0.38,CW*0.62]),sp(3),ss("B. ABDOMINAL EXAMINATION")]
    abd=[["Finding","Interpretation","Action"],
         ["Distension","Loaded colon; obstruction; megacolon","AXR to assess"],
         ["LIF palpable mass (mobile, soft, indents)","Loaded sigmoid — faeces; indents on pressure unlike tumour","Fleet enema + laxatives"],
         ["Hard fixed irregular mass — any quadrant","CRC until proven otherwise","Urgent colonoscopy + CT staging"],
         ["Diffuse mild tenderness","Loaded colon","Laxatives + reassess"],
         ["Epigastric mass + constipation + vomiting","Gastric outlet obstruction; abdominal Ca","CT abdomen"],
         ["Tympany + absent/tinkling bowel sounds","Obstruction","Erect AXR + surgical review"],
         ["Hepatomegaly","Metastatic CRC; hypothyroidism","CT + LFTs"],
         ["Palpable bladder","Urinary retention — often co-exists with faecal impaction","Catheterise; PR exam"]]
    s+=[tbl(abd,[CW*0.32,CW*0.38,CW*0.3]),sp(3),
        pe("OSCE TRAP: Overflow (spurious) diarrhoea — elderly patient presents with 'diarrhoea'. PR exam reveals packed "
           "rectum. This is liquid stool passing around an impaction. NEVER give antidiarrhoeals → disimpact first!")]

    # RIGHT PAGE 4
    s+=[ss("C. PERIANAL & DIGITAL RECTAL EXAMINATION (DRE)"),
        bd("DRE is mandatory in ALL patients presenting with constipation — Bailey & Love 28e"),sp(2)]
    dre=[["Inspect/Find","Interpretation"],
         ["Anal fissure (posterior midline)","Pain → voluntary withholding → exacerbates constipation; look for sentinel tag"],
         ["External haemorrhoids","Pain/bleeding → patient avoids defaecation"],
         ["Rectal prolapse","Chronic straining; present with mucus discharge + incontinence"],
         ["Perineal descent on straining","Pelvic floor weakness; >3.5 cm descent = abnormal"],
         ["Empty rectum on DRE","SLOW TRANSIT constipation — stool not reaching rectum; investigate with transit study"],
         ["Hard stool in rectum","Faecal impaction / outlet dysfunction / functional constipation"],
         ["Anal tone ↓","Sphincter damage; neurological cause; prolapse"],
         ["Anal tone ↑ (tight sphincter)","Anal fissure; dyssynergia; anxiety"],
         ["Anterior rectal wall bulge (women)","Rectocoele — stool traps in pouch"],
         ["Mass on DRE","CRC until proven otherwise → urgent referral"]]
    s+=[tbl(dre,[CW*0.38,CW*0.62]),sp(3),
        rf("A mass on DRE is CRC until proven otherwise. Do NOT reassure and discharge."),sp(4),
        sec("SPECIAL TESTS — ANORECTAL PHYSIOLOGY",'SP'),
        ss("BALLOON EXPULSION TEST")]
    s+=[bd("Method: 50 mL water-filled balloon placed in rectum; patient asked to expel while seated on commode."),
        bl("Normal: expelled within 1 minute"),
        bl("Prolonged (>1 min): suggests pelvic floor dyssynergia (anismus)"),
        bl("Cannot expel: outlet obstruction — proceed to anorectal manometry + defaecating proctogram"),sp(3),
        ss("ANORECTAL MANOMETRY"),
        bl("Measures: resting + squeeze anal pressures; rectoanal inhibitory reflex (RAIR); rectal sensation thresholds"),
        bl("High resting pressure: anal fissure / dyssynergia"),
        bl("<b>Absent RAIR: PATHOGNOMONIC of Hirschsprung's disease</b>"),
        bl("Dyssynergia: paradoxical ↑ EAS pressure during simulated defaecation"),sp(3),
        ss("DEFAECATING PROCTOGRAM (EVACUATING PROCTOGRAPHY)"),
        bl("Barium paste placed in rectum; lateral fluoroscopy during straining/evacuation"),
        bl("Shows: rectocoele (anterior bulge), intussusception, rectal prolapse, perineal descent, incomplete emptying"),
        bl("MRI proctogram: no radiation; shows soft tissue anatomy — pelvic organ prolapse, levator avulsion"),sp(3),
        ss("COLONIC TRANSIT STUDY (SITZMARKS)"),
        bl("Patient swallows 20 radiopaque markers; AXR at Day 5"),
        bl("Normal: <5 markers remaining (>80% passed)"),
        bl("Slow transit: markers throughout colon — pure slow transit constipation"),
        bl("Outlet dysfunction: markers accumulated in rectosigmoid region despite normal proximal transit")]

    s.append(PageBreak())

    # PAGE 5 — INVESTIGATIONS
    s+=[sec("INVESTIGATIONS — CONSTIPATION"),ss("FIRST-LINE BLOODS")]
    bloods=[["Test","What to Look For","Rationale"],
            ["FBC","Hb ↓ + MCV ↓ (IDA)","Occult bleeding from CRC or IBD"],
            ["U&E","K⁺ ↓ (hypokalaemia)","Hypokalaemia causes constipation; also assess hydration"],
            ["Corrected Ca²⁺","Ca²⁺ >2.6 mmol/L","Hypercalcaemia — check PTH if elevated"],
            ["TFTs (TSH ± free T4)","TSH ↑ = hypothyroidism","Most common reversible endocrine cause"],
            ["Glucose/HbA1c","DM diagnosis/monitoring","Diabetic autonomic neuropathy → constipation"],
            ["Mg²⁺","Hypomagnesaemia","Less common; check if Ca²⁺/K⁺ low"],
            ["CRP/ESR","↑ inflammation","IBD, CRC, abscess, diverticulitis"],
            ["CEA","↑ in CRC","Not diagnostic alone — use for monitoring post-diagnosis"],
            ["B12/folate/ferritin/Vit D","Malnutrition, malabsorption","If concurrent weight loss / malabsorption symptoms"]]
    s+=[tbl(bloods,[CW*0.22,CW*0.28,CW*0.5]),sp(3),ss("IMAGING")]
    img=[["Investigation","Indication","Findings"],
         ["AXR (plain film)","Suspected impaction/obstruction; acute symptoms","Faecal loading; dilated loops (3-6-9 rule); megacolon"],
         ["Contrast enema (Gastrografin)","Suspected Hirschsprung's; stricture; incomplete bowel prep","Transition zone (Hirschsprung's); strictures; mucosal pattern"],
         ["CT abdomen/pelvis + contrast","Mass lesion, CRC staging, complicated diverticular","Extent of Ca, lymph nodes, mets, pelvic anatomy"],
         ["CT colonography","Unable to tolerate colonoscopy (elderly, comorbid)","Same as colonoscopy; excludes polyps/Ca; misses flat lesions"]]
    s+=[tbl(img,[CW*0.28,CW*0.35,CW*0.37]),sp(3),ss("ENDOSCOPY")]
    endo=[["Procedure","Indication","Key Points"],
          ["Flexible sigmoidoscopy","First-line if <50 yrs without alarm features","Views rectum, sigmoid, descending colon"],
          ["Colonoscopy","Alarm features; age >50; new-onset; positive FOB","Full mucosal survey; biopsy; polypectomy — GOLD STANDARD"],
          ["OGD + duodenal biopsy","Concurrent malabsorption symptoms","Exclude coeliac (check anti-tTG first)"]]
    s+=[tbl(endo,[CW*0.28,CW*0.38,CW*0.34]),sp(3),ss("SPECIALIST INVESTIGATIONS")]
    spec=[["Test","When","Diagnoses"],
          ["Colonic transit study (Sitzmarks)","Chronic constipation — slow vs outlet","Slow transit: scattered; Outlet: rectosigmoid"],
          ["Anorectal manometry","Dyssynergia; Hirschsprung's; incontinence","Absent RAIR = Hirschsprung's; ↑ resting pressure = fissure/dyssynergia"],
          ["Balloon expulsion test","Outlet dysfunction screen","Failure to expel in <1 min → dyssynergia"],
          ["Defaecating proctogram (MRI preferred)","Straining; incomplete evacuation; prolapse","Rectocoele; intussusception; perineal descent"],
          ["Hydrogen breath test","SIBO; lactose intolerance co-existing","↑ H₂ after substrate ingestion"],
          ["Electromyography (EMG) — pudendal nerve","Suspected pudendal neuropathy","Prolonged pudendal nerve terminal motor latency"]]
    s+=[tbl(spec,[CW*0.28,CW*0.28,CW*0.44]),sp(3),
        pe("INVESTIGATION SEQUENCE from Bailey & Love: 1) Exclude secondary causes (bloods + colonoscopy if alarm features). "
           "2) Trial of lifestyle + basic laxatives. 3) If chronic + refractory → colonic transit study + anorectal physiology to "
           "subtype and direct targeted treatment.")]

    # RIGHT PAGE 5
    s+=[sec("MANAGEMENT — CONSTIPATION STEP LADDER"),
        ss("STEP 1 — LIFESTYLE MODIFICATION (ALWAYS FIRST)"),
        bl("<b>Dietary fibre:</b> Increase to 25-30 g/day (fruits, vegetables, wholegrains, legumes)"),
        bl("<b>Fluid intake:</b> 1.5-2 L/day — fibre CANNOT work without adequate water"),
        bl("<b>Exercise:</b> Regular aerobic activity directly ↑ colonic motility via ENS stimulation"),
        bl("<b>Toilet routine:</b> Regular attempt after meals (exploit gastrocolic reflex); NEVER ignore urge"),
        bl("<b>Positioning:</b> Squatting / footstool (Squatty Potty) — straightens anorectal angle → easier evacuation"),
        bl("<b>Medication review:</b> Stop/switch constipating drugs wherever clinically safe"),sp(3),
        ss("STEP 2 — LAXATIVES")]
    lax=[["Class","Drug(s)","Dose","Mechanism","Notes"],
         ["Bulk-forming\n(1st line mild)","Ispaghula husk\n(Fybogel / Psyllium)","1 sachet BD in 200 mL water","↑ stool bulk → stimulates peristalsis","Takes 2-3 days; MUST take with water; avoid in impaction; CI in dysphagia"],
         ["Osmotic\n(1st line OIC)","Macrogol (Movicol/MiraLAX)\nLactulose\nMagnesium hydroxide","1-3 sachets/day\n15-30 mL BD\n2-4 tabs BD","Non-absorbable polymer draws water into bowel osmotically\nNon-absorbable disaccharide\nOsmotic","Macrogol preferred (less bloating, better evidence). Lactulose causes gas/bloating. Mg²⁺ CI in renal failure"],
         ["Stimulant\n(add-on or short-term)","Senna\nBisacodyl\nSodium picosulfate","2-4 tabs nocte\n5-10 mg nocte\n5-10 mg/day","Stimulate myenteric plexus → ↑ peristalsis. Also ↑ secretion.","Onset 6-12 hrs. Avoid long-term (theoretical atonic colon). Bisacodyl suppository for quicker effect."],
         ["Stool softener","Docusate sodium","100-200 mg BD","Surfactant — lowers surface tension → softer stool","Mild; little evidence alone; useful post-op / anal fissure"],
         ["Rectal agents","Phosphate enema\nGlycerol suppository\nMicrolax enema","Prn","Local osmotic + stimulant\nRectal lubricant + mild irritant\nOsmotic + local stimulant","For acute impaction; pre-procedure; rapid onset (minutes)"],
         ["Secretagogue\n(chronic, refractory)","Linaclotide\nLubiprostone","290 μg OD (IBS-C: 72 μg)\n8-24 μg BD","↑ intestinal Cl⁻ via guanylate cyclase-C → ↑ fluid secretion\n↑ Cl⁻ channel (ClC-2) activation","NICE approved for chronic idiopathic constipation when ≥2 laxatives failed"],
         ["Prokinetic\n(specialist)","Prucalopride","2 mg OD (1 mg OD if >65 yrs)","Highly selective 5-HT₄ agonist → ↑ colonic motility (HAPCs)","For chronic constipation after ≥2 laxatives failed; good evidence in RCTs; women only (initially)"],
         ["Peripherally acting\nμ-opioid antagonist\n(PAMORA)","Methylnaltrexone SC\nNaloxegol oral\nNaldemedine oral","8-12 mg SC every 2 days\n25 mg OD\n0.2 mg OD","Block peripheral μ-receptors in gut WITHOUT reversing central analgesia","Specifically for opioid-induced constipation (OIC) refractory to standard laxatives"]]
    s+=[tbl(lax,[CW*0.18,CW*0.18,CW*0.14,CW*0.25,CW*0.25]),sp(3),
        pe("LAXATIVE ORDER (Bailey & Love): Bulk-forming → Osmotic (macrogol) → Stimulant → Combine. "
           "For OPIOID-INDUCED CONSTIPATION: start with OSMOTIC + STIMULANT together — bulk-forming alone is inadequate. "
           "NEVER stop osmotic laxative without also stopping the opioid.")]

    s.append(PageBreak())

    # PAGE 6 — SPECIFIC TREATMENTS + SURGERY
    s+=[sec("MANAGEMENT — SPECIFIC CAUSES & CONDITIONS")]
    specTx=[["Cause","Specific Treatment","Key Note"],
            ["Hypothyroidism","Levothyroxine replacement","Constipation resolves as euthyroid state restored; recheck TFTs at 6-8 weeks"],
            ["Hypercalcaemia","IV 0.9% NaCl + IV bisphosphonate (zoledronate 4mg)","Then treat underlying cause (PTH, Ca malignancy)"],
            ["Hypokalaemia","IV/oral K⁺ replacement","Address underlying cause"],
            ["Opioid-induced (OIC)","Macrogol + senna; add methylnaltrexone SC if refractory","Do NOT stop opioid without clinical review"],
            ["Anal fissure","Topical GTN 0.4% cream or diltiazem 2% gel BD × 8 wks","Botulinum toxin if topical fails; lateral internal sphincterotomy if chronic"],
            ["Faecal impaction","Macrogol disimpaction regimen (8 sachets/day × 3 days)","Phosphate enema if rectal impaction; manual evacuation under sedation if severe"],
            ["Pelvic floor dyssynergia","Biofeedback therapy (10-15 sessions)","Gold standard — retrains paradoxical contraction; 70% response rate"],
            ["Slow transit constipation","Prucalopride 2mg OD (after 2 laxatives fail)","Subtotal colectomy + IRA if all medical therapy fails"],
            ["Rectocoele","Pelvic floor physiotherapy first","Surgical repair (posterior colporrhaphy / transanal repair) if symptomatic, large (>3cm)"],
            ["IBS-C","Low-FODMAP diet + linaclotide 72 μg OD + antispasmodics","Psychological therapy (CBT) improves outcomes"],
            ["Hirschsprung's","Surgical pull-through (Swenson/Soave/Duhamel)","Definitive; colostomy if acute toxic megacolon"],
            ["CRC causing obstruction","Emergency: Hartmann's procedure; or colonic stenting as bridge to elective","Elective: anterior resection / right hemicolectomy depending on site"],
            ["Pseudo-obstruction (Ogilvie's)","IV neostigmine 2mg over 3-5 min","Monitor HR; colonoscopic decompression if fails; caecostomy last resort"]]
    s+=[tbl(specTx,[CW*0.27,CW*0.4,CW*0.33]),sp(3),
        sec("SURGICAL OPTIONS IN CHRONIC CONSTIPATION",'SP'),ss("WHEN IS SURGERY CONSIDERED?"),
        bl("After ALL medical options exhausted (minimum 2 laxative classes + prucalopride + biofeedback if dyssynergia)"),
        bl("Pathophysiology confirmed with specialist tests (transit study + anorectal manometry + proctogram)"),
        bl("MDT review recommended; patient fully counselled on outcomes and risks"),sp(3)]
    surg=[["Procedure","Indication","Key Points / Complications"],
          ["Subtotal colectomy + ileorectal\nanastomosis (IRA)","Refractory slow transit constipation; all medical therapy failed","Most effective for pure slow transit; complications: diarrhoea (5-15%), SBO (10-20%), fecal incontinence (10-20%)"],
          ["Stapled transanal rectal resection\n(STARR)","Rectal intussusception + rectocoele causing outlet dysfunction","Removes redundant anterior rectal wall; risk: urgency, fistula, haemorrhage"],
          ["Posterior colporrhaphy /\ntransanal rectocoele repair","Symptomatic rectocoele failing physiotherapy","Restores posterior vaginal wall; good results for isolated rectocoele"],
          ["Ventral rectopexy","Rectal prolapse / intussusception","Laparoscopic mesh fixation to sacral promontory; low recurrence"],
          ["Sacral nerve stimulation (SNS)","Pelvic floor dysfunction; refractory outlet obstruction","Modulates sacral nerves S2-S4; neuromodulation"],
          ["ACE procedure\n(antegrade colonic enema)","Severe intractable constipation; neurogenic bowel","Appendicocoecostomy or Malone conduit; allows antegrade irrigation"],
          ["Stoma (ileostomy / colostomy)","Last resort; severe quality of life impairment","May be the only effective solution in refractory cases"]]
    s+=[tbl(surg,[CW*0.28,CW*0.28,CW*0.44])]

    # RIGHT PAGE 6
    s+=[sec("SPECIAL POPULATIONS & SPECIFIC DISORDERS",'ST'),ss("CONSTIPATION IN THE ELDERLY"),
        bl("Most common: polypharmacy (opioids, Ca-channel blockers, anticholinergics) + immobility + dehydration"),
        bl("<b>Faecal impaction:</b> liquid stool bypasses impaction → overflow (spurious) diarrhoea — DO NOT give antidiarrhoeals"),
        bl("PR examination essential — confirm impaction and exclude rectal cancer"),
        bl("Macrogol disimpaction + phosphate enema if rectal; manual evacuation under sedation if severe"),
        bl("Review all medications; encourage mobilisation; ensure adequate fluid intake"),sp(3),
        ss("CONSTIPATION IN PREGNANCY"),
        bl("Mechanism: progesterone ↓ gut smooth muscle + iron supplements + mechanical compression by gravid uterus"),
        bl("<b>Safe laxatives:</b> Ispaghula husk (1st line), lactulose, macrogol (limited data but used in practice)"),
        bl("<b>Avoid:</b> Stimulant laxatives (senna, bisacodyl) in 1st trimester; castor oil (induces labour); mineral oils"),sp(3),
        ss("HIRSCHSPRUNG'S DISEASE — COMPLETE"),]
    hirsch=[["Feature","Details"],
            ["Pathology","Absence of ganglion cells (Meissner's submucosal + Auerbach's myenteric plexus) in distal colorectum"],
            ["Cause","Failure of neural crest cell migration (weeks 5-12 of embryogenesis). RET proto-oncogene mutation (50%)"],
            ["Length","Short segment (75% — rectosigmoid); Long segment (20%); Total colonic (5%)"],
            ["Presentation (neonate)","Failure to pass meconium within 48 hrs; abdominal distension; bilious vomiting"],
            ["Presentation (child)","Chronic constipation since birth; ribbon stools; never had normal bowel habit; growth retardation"],
            ["Complication","Hirschsprung's-associated enterocolitis (HAEC) — life-threatening; fever + explosive diarrhoea + sepsis"],
            ["Investigation","Suction rectal biopsy (GOLD STANDARD) — absent ganglion cells + ↑ AChE staining"],
            ["Contrast enema","Transition zone: narrow aganglionic segment → dilated proximal (cone-shaped)"],
            ["Manometry","Absent RAIR (recto-anal inhibitory reflex) — pathognomonic"],
            ["Surgery","Pull-through procedure (Swenson/Soave/Duhamel) — resect aganglionic segment"]]
    s+=[tbl(hirsch,[CW*0.28,CW*0.72]),sp(3),
        ss("SLOW TRANSIT CONSTIPATION — COLONIC INERTIA"),
        bl("Definition: ↓ frequency of HAPCs throughout colon; normal anorectal function"),
        bl("Demographics: overwhelmingly female (>95%); often starts in teens/early adulthood"),
        bl("Sitzmarks: markers distributed throughout colon at Day 5 (vs outlet: markers in rectosigmoid)"),
        bl("Medical: prucalopride 2mg OD (5-HT₄ agonist — ↑ HAPCs); linaclotide"),
        bl("Surgical: subtotal colectomy + IRA — effective in selected patients with PURE slow transit (no outlet dysfunction)")]

    s.append(PageBreak())

    # PAGE 7 — IBS-C, COMPLICATIONS, OSCE VIVA
    s+=[sec("IBS — CONSTIPATION PREDOMINANT (IBS-C)"),ss("ROME IV DIAGNOSTIC CRITERIA FOR IBS"),
        bd("Recurrent abdominal pain ≥1 day/week in last 3 months associated with ≥2 of:"),
        bl("Related to defaecation (pain relieved OR worsened by defaecation)"),
        bl("Change in stool frequency"),
        bl("Change in stool form (consistency)"),
        bd("<i>+ Symptom onset ≥6 months prior</i>"),sp(3),
        ss("IBS-C vs FUNCTIONAL CONSTIPATION — KEY DIFFERENCE"),
        bd("IBS-C: abdominal PAIN is the dominant symptom + linked to bowel habit"),
        bd("Functional constipation: defaecatory symptoms dominant; pain may exist but not primary"),sp(3)]
    ibs=[["Feature","IBS-C","Functional Constipation"],
         ["Abdominal pain","DOMINANT symptom — linked to defaecation","Secondary; bloating more prominent"],
         ["Stool pattern","Constipation alternating with normal or diarrhoea","Predominantly constipated"],
         ["Response to defaecation","Pain changes after BM","No consistent change"],
         ["Investigations","All normal (Rome IV = diagnosis of exclusion)","All normal (after secondary causes excluded)"],
         ["Treatment","Low-FODMAP; linaclotide 72 μg; antispasmodics; CBT","Laxative ladder; prucalopride; biofeedback"],
         ["Pathophysiology","Visceral hypersensitivity + altered gut-brain axis","↓ colonic motility / outlet dysfunction"]]
    s+=[tbl(ibs,[CW*0.25,CW*0.375,CW*0.375]),sp(3),
        pe("IBS is a DIAGNOSIS OF EXCLUSION. NEVER diagnose IBS if: age >50 with new symptoms, alarm features, "
           "nocturnal symptoms, anaemia, weight loss, positive FOB, family Hx CRC. Always investigate these first."),sp(4),
        sec("COMPLICATIONS OF CHRONIC CONSTIPATION",'SR'),]
    comp=[["Complication","Mechanism","Features"],
          ["Haemorrhoids","↑ venous pressure from straining → engorgement of haemorrhoidal plexus","Bright red rectal bleeding; prolapse; mucus discharge; pruritus"],
          ["Anal fissure","Hard stools tear posterior midline anoderm","Severe pain on defaecation + bright red blood; sentinel skin tag"],
          ["Rectal prolapse","Chronic straining → stretch ligaments + pudendal neuropathy","Full thickness rectal mucosa protrudes on straining/standing"],
          ["Rectocoele","Anterior rectal wall herniates into vaginal wall","Need to digitate vagina to defaecate; 'pocket' of stool"],
          ["Faecal impaction","Chronic constipation + dehydration + immobility","Hard stool mass — may cause overflow diarrhoea; urinary retention"],
          ["Stercoral ulcer / perforation","Pressure necrosis of colonic wall by impacted faecal mass","Antemesenteric wall; can perforate → peritonitis"],
          ["Megacolon / volvulus","Chronic colonic dilatation from constipation; also Hirschsprung's","Sigmoid volvulus: 'coffee bean' sign on AXR; massive distension"],
          ["UTI / urinary retention","Faecal impaction compresses bladder/urethra","Common in elderly; disimpaction resolves urinary symptoms"]]
    s+=[tbl(comp,[CW*0.22,CW*0.38,CW*0.4])]

    # RIGHT PAGE 7
    s+=[sec("OSCE VIVA — RAPID FIRE Q&A")]
    viva=[["Examiner Asks...","Your Answer"],
          ["Define constipation (Rome IV)","<3 BM/week OR ≥2 of: straining, hard stools (BSC 1-2), incomplete evacuation, anorectal blockage, manual manoeuvres in >25% defaecations for ≥3 months"],
          ["Most common drug cause?","Opioids — μ-receptors in gut → ↓ peristalsis + ↑ sphincter tone. Also iron, Al antacids, TCAs, verapamil, ondansetron"],
          ["Metabolic causes to exclude?","Hypothyroidism (TSH), hypercalcaemia (Ca²⁺), hypokalaemia (K⁺), diabetes (autonomic neuropathy)"],
          ["Empty rectum on DRE means?","Slow transit constipation — stool not reaching rectum. Investigate with Sitzmarks transit study"],
          ["Full rectum on DRE means?","Outlet dysfunction OR faecal impaction — treat outlet/impaction; DO NOT give stimulant laxatives alone"],
          ["What is overflow diarrhoea?","Liquid stool bypasses faecal impaction in rectum — presents as diarrhoea. DO NOT give antidiarrhoeals"],
          ["Balloon expulsion test — normal?","Expelled within 1 minute. Prolonged = pelvic floor dyssynergia (anismus)"],
          ["What does absent RAIR mean?","Pathognomonic of Hirschsprung's disease"],
          ["How to diagnose Hirschsprung's?","Suction rectal biopsy — absent ganglion cells + ↑ AChE staining. Also absent RAIR on manometry"],
          ["Best treatment for dyssynergia?","Biofeedback therapy — 70% response; retrains paradoxical puborectalis contraction"],
          ["Prucalopride — mechanism?","Highly selective 5-HT₄ agonist → ↑ colonic motility (stimulates HAPCs)"],
          ["What is OIC? Treatment?","Opioid-induced constipation. Treat with macrogol + stimulant; add methylnaltrexone SC (PAMORA) if refractory"],
          ["Laxative for pregnancy?","Ispaghula husk (1st line) or lactulose. Avoid stimulants (senna) in 1st trimester"],
          ["Slow transit surgery?","Subtotal colectomy + ileorectal anastomosis — only after ALL medical therapy failed + MDT review"],
          ["Stercoral perforation — what is it?","Pressure necrosis of colon wall by impacted faeces → perforation → peritonitis"],
          ["What is Ogilvie's syndrome?","Acute colonic pseudo-obstruction — treat with IV neostigmine; colonoscopic decompression; caecostomy"]]
    s+=[tbl(viva,[CW*0.42,CW*0.58]),sp(4),
        gn("FINAL OSCE TIP: For constipation ALWAYS: 1) Drug history (opioids? iron? verapamil?), "
           "2) Alarm features (age, bleeding, weight loss, family Hx CRC), 3) Bloods: TFTs + Ca²⁺ + FBC. "
           "4) PR exam — NEVER omit. 5) Colonoscopy if any alarm features present.")]

    doc.build(s,canvasmaker=BC)
    print("Built:",out)

build()

Try again

Running Command

cd /home/daytona/workspace/osce-booklets && python3 build_constipation_v2.py 2>&1 | tail -5

Writing File

~/osce-booklets/build_constipation_v2.py

"""
CONSTIPATION — Comprehensive Final Year MBBS OSCE Booklet
Sources: Bailey & Love 28e, Sleisenger & Fordtran, Sabiston, Harrison's 22e, Goldman-Cecil, Oxford Handbook
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame,
    Paragraph, Spacer, Table, TableStyle, HRFlowable, NextPageTemplate, PageBreak)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER
from reportlab.pdfgen import canvas as pdfgen_canvas

# ── Palette ─────────────────────────────────────────────────────────────────
NAVY    = colors.HexColor('#1a2a4a')
GOLD    = colors.HexColor('#c8a951')
DKBLUE  = colors.HexColor('#2c5282')
TEAL    = colors.HexColor('#2a7a6b')
REDBDR  = colors.HexColor('#c0392b')
REDBG   = colors.HexColor('#fdecea')
GRNBDR  = colors.HexColor('#2e7d32')
GRNBG   = colors.HexColor('#e8f5e9')
AMBDR   = colors.HexColor('#f57f17')
AMBG    = colors.HexColor('#fff8e1')
PURP    = colors.HexColor('#6a1b9a')
PURPBG  = colors.HexColor('#f3e5f5')
WHITE   = colors.white
BLACK   = colors.black
GREY_L  = colors.HexColor('#f2f4f7')
GREY_M  = colors.HexColor('#d5dce8')

PW, PH  = A4
ML, MR  = 13*mm, 13*mm
HDR_H   = 18*mm
FTR_H   = 10*mm
UW      = PW - ML - MR
COL_GAP = 6*mm
CW      = (UW - COL_GAP) / 2

FOOTER  = ("Sources: Bailey & Love 28e  ·  Sleisenger & Fordtran  ·  Sabiston Surgery  ·  "
           "Harrison's 22e  ·  Goldman-Cecil Medicine")

# ── Styles ──────────────────────────────────────────────────────────────────
def make_styles():
    d = {}
    kw = dict(fontName='Helvetica', fontSize=7.8, textColor=BLACK, leading=11, spaceAfter=2)
    d['TM'] = ParagraphStyle('TM', fontName='Helvetica-Bold', fontSize=18, textColor=WHITE, leading=22, alignment=TA_CENTER)
    d['TS'] = ParagraphStyle('TS', fontName='Helvetica-BoldOblique', fontSize=10, textColor=GOLD, leading=13, alignment=TA_CENTER)
    d['TT'] = ParagraphStyle('TT', fontName='Helvetica', fontSize=8, textColor=colors.HexColor('#a0bcd8'), leading=11, alignment=TA_CENTER)
    d['SH'] = ParagraphStyle('SH', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, backColor=DKBLUE, borderPadding=(3,5,3,5), leading=12, spaceAfter=4, spaceBefore=6)
    d['ST'] = ParagraphStyle('ST', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, backColor=TEAL,   borderPadding=(3,5,3,5), leading=12, spaceAfter=4, spaceBefore=6)
    d['SR'] = ParagraphStyle('SR', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, backColor=REDBDR, borderPadding=(3,5,3,5), leading=12, spaceAfter=4, spaceBefore=6)
    d['SP'] = ParagraphStyle('SP', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, backColor=PURP,   borderPadding=(3,5,3,5), leading=12, spaceAfter=4, spaceBefore=6)
    d['SS'] = ParagraphStyle('SS', fontName='Helvetica-Bold', fontSize=8.5, textColor=DKBLUE, leading=11, spaceAfter=3, spaceBefore=5)
    d['BD'] = ParagraphStyle('BD', **kw)
    d['BL'] = ParagraphStyle('BL', fontName='Helvetica',      fontSize=7.8, textColor=BLACK,  leading=11, leftIndent=10, bulletIndent=2, spaceAfter=1.5)
    d['BB'] = ParagraphStyle('BB', fontName='Helvetica-Bold', fontSize=7.8, textColor=DKBLUE, leading=11, leftIndent=10, bulletIndent=2, spaceAfter=1.5)
    d['PE'] = ParagraphStyle('PE', fontName='Helvetica-BoldOblique', fontSize=7.8, textColor=colors.HexColor('#7b3f00'), backColor=AMBG,   borderColor=AMBDR, borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    d['RF'] = ParagraphStyle('RF', fontName='Helvetica-Bold',        fontSize=7.8, textColor=REDBDR,                     backColor=REDBG,  borderColor=REDBDR,borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    d['GN'] = ParagraphStyle('GN', fontName='Helvetica',             fontSize=7.8, textColor=colors.HexColor('#1b5e20'), backColor=GRNBG,  borderColor=GRNBDR,borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    d['PU'] = ParagraphStyle('PU', fontName='Helvetica-BoldOblique', fontSize=7.8, textColor=PURP,                      backColor=PURPBG, borderColor=PURP,  borderWidth=0.8, borderPadding=(4,5,4,5), leading=11, spaceAfter=4, spaceBefore=4)
    d['TH'] = ParagraphStyle('TH', fontName='Helvetica-Bold', fontSize=7.5, textColor=WHITE, leading=10, alignment=TA_CENTER)
    d['TC'] = ParagraphStyle('TC', fontName='Helvetica',      fontSize=7.5, textColor=BLACK, leading=10)
    d['FT'] = ParagraphStyle('FT', fontName='Helvetica-Oblique', fontSize=6.5, textColor=colors.HexColor('#aaaaaa'), leading=9, alignment=TA_CENTER)
    return d

ST = make_styles()

# ── Canvas ───────────────────────────────────────────────────────────────────
class BC(pdfgen_canvas.Canvas):
    def __init__(self, f, **kw):
        super().__init__(f, **kw)
        self._sp2 = []
        self._pg  = 0
    def showPage(self):
        self._sp2.append(dict(self.__dict__))
        self._startPage()
    def save(self):
        for state in self._sp2:
            self.__dict__.update(state)
            self._decorate()
            super().showPage()
        super().save()
    def _decorate(self):
        self._pg += 1
        c = self
        c.setFillColor(NAVY);  c.rect(0, PH-HDR_H, PW, HDR_H, fill=1, stroke=0)
        c.setFillColor(GOLD);  c.rect(0, PH-HDR_H-1.5, PW, 1.5, fill=1, stroke=0)
        c.setFillColor(GOLD);  c.setFont('Helvetica-Bold', 8)
        c.drawString(ML, PH-10*mm, f"Page {self._pg}")
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold', 12)
        c.drawCentredString(PW/2, PH-8.5*mm, "CONSTIPATION")
        c.setFont('Helvetica', 7.5); c.setFillColor(colors.HexColor('#a0bcd8'))
        c.drawCentredString(PW/2, PH-13.5*mm, "Clinical OSCE Notes  |  Final Year MBBS")
        c.setFillColor(GOLD); c.setFont('Helvetica-Bold', 7.5)
        c.drawString(ML, PH-HDR_H-8, "◀ LEFT")
        c.drawRightString(PW-MR, PH-HDR_H-8, "RIGHT ▶")
        c.setFillColor(NAVY); c.rect(0, 0, PW, FTR_H+3, fill=1, stroke=0)
        c.setFillColor(GOLD); c.rect(0, FTR_H+3, PW, 1, fill=1, stroke=0)
        c.setFillColor(colors.HexColor('#aaaaaa')); c.setFont('Helvetica-Oblique', 6)
        c.drawCentredString(PW/2, 4*mm, FOOTER)
        if self._pg > 1:
            c.setStrokeColor(GREY_M); c.setLineWidth(0.5)
            c.line(ML+CW+COL_GAP/2, PH-HDR_H-12, ML+CW+COL_GAP/2, FTR_H+6)

# ── Helpers ───────────────────────────────────────────────────────────────────
def sec(t, s='SH'):  return Paragraph(f"<b>{t}</b>", ST[s])
def ssec(t):         return Paragraph(t, ST['SS'])
def body(t):         return Paragraph(t, ST['BD'])
def bul(t):          return Paragraph(f"• {t}", ST['BL'])
def bulb(t):         return Paragraph(f"• {t}", ST['BB'])
def pearl(t):        return Paragraph(f"★  OSCE PEARL: {t}", ST['PE'])
def alarm(t):        return Paragraph(f"⚠  RED FLAG: {t}", ST['RF'])
def tip(t):          return Paragraph(t, ST['GN'])
def path_box(t):     return Paragraph(f"🔬  PATHOPHYSIOLOGY: {t}", ST['PU'])
def sp(h=3):         return Spacer(1, h)

def mktbl(rows, widths, bg=DKBLUE):
    style = TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), bg),
        ('TEXTCOLOR',     (0,0), (-1,0), WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'Helvetica-Bold'),
        ('FONTSIZE',      (0,0), (-1,-1), 7.5),
        ('ROWBACKGROUNDS',(0,1), (-1,-1), [GREY_L, WHITE]),
        ('GRID',          (0,0), (-1,-1), 0.4, GREY_M),
        ('LEFTPADDING',   (0,0), (-1,-1), 4),
        ('RIGHTPADDING',  (0,0), (-1,-1), 4),
        ('TOPPADDING',    (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ])
    wrapped = []
    for ridx, row in enumerate(rows):
        wr = []
        for cell in row:
            if isinstance(cell, str):
                wr.append(Paragraph(cell, ST['TH'] if ridx == 0 else ST['TC']))
            else:
                wr.append(cell)
        wrapped.append(wr)
    return Table(wrapped, colWidths=widths, style=style, hAlign='LEFT')

# ── Build ─────────────────────────────────────────────────────────────────────
def build():
    out = '/home/daytona/workspace/osce-booklets/Constipation_OSCE_Booklet_v2.pdf'
    bot = FTR_H + 8
    top = PH - HDR_H - 14
    fh  = top - bot

    cover_fr  = Frame(ML, bot, UW, fh, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0)
    left_fr   = Frame(ML,          bot, CW, fh, leftPadding=0, rightPadding=3, topPadding=0, bottomPadding=0)
    right_fr  = Frame(ML+CW+COL_GAP, bot, CW, fh, leftPadding=3, rightPadding=0, topPadding=0, bottomPadding=0)

    doc = BaseDocTemplate(out, pagesize=A4, leftMargin=ML, rightMargin=MR,
                          topMargin=22*mm, bottomMargin=16*mm)
    doc.addPageTemplates([
        PageTemplate(id='cover', frames=[cover_fr]),
        PageTemplate(id='two',   frames=[left_fr, right_fr]),
    ])
    s = []

    # ══ COVER ════════════════════════════════════════════════════════════════
    s += [NextPageTemplate('cover'), Spacer(1, 26*mm),
          Paragraph("CONSTIPATION", ST['TM']), Spacer(1, 4),
          Paragraph("Clinical OSCE Notes  |  Final Year MBBS", ST['TS']), Spacer(1, 6),
          Paragraph("Epidemiology · Pathophysiology · Rome IV · History Taking · Examination<br/>"
                    "Investigations · Laxative Ladder · Specific Disorders · Surgical Options · OSCE Q&amp;A", ST['TT']),
          Spacer(1, 7),
          HRFlowable(width="80%", thickness=1.5, color=GOLD, spaceAfter=8, hAlign='CENTER'),
          Paragraph(FOOTER, ST['FT']),
          PageBreak()]

    # ══ PAGE 2 — DEFINITION · EPIDEMIOLOGY · PATHOPHYSIOLOGY · CAUSES ════════
    s.append(NextPageTemplate('two'))

    # LEFT
    s += [sec("DEFINITION & EPIDEMIOLOGY"),
          body("<b>Definition (Rome IV):</b> Unsatisfactory defaecation due to infrequent stools, difficult stool passage, "
               "or both for ≥3 months. Stricter chronic constipation definition adds ≥6 months AND failure of ≥2 laxative classes."),
          sp(3),
          body("<b>Prevalence:</b> ~10% worldwide (self-reported). True chronic constipation ~0.5%. "
               "Female : Male = 2:1 (self-reported); 9:1 for chronic constipation. "
               "Prevalence exceeds 50% in institutionalised patients over age 65."),
          sp(4),
          sec("ROME IV CRITERIA — FUNCTIONAL CONSTIPATION"),
          body("≥2 of the following present during >25% of defaecations, for the last 3 months "
               "(symptom onset ≥6 months prior):"),
          sp(2),
          mktbl([
              ["Criterion","Detail"],
              ["1. Straining",               "Excessive effort — suggests outlet dysfunction or slow transit"],
              ["2. Hard/lumpy stools",        "Bristol type 1–2 — prolonged colonic transit time"],
              ["3. Incomplete evacuation",    "Retained stool sensation — outlet dysfunction or rectocoele"],
              ["4. Anorectal blockage",       "Feeling of obstruction at anus/rectum"],
              ["5. Manual manoeuvres",        "Digital evacuation or pelvic floor support — indicates pelvic floor failure"],
              ["6. <3 BM per week",           "Fewer than 3 spontaneous bowel movements per week"],
              ["PLUS",                        "Loose stools rarely present without laxatives; insufficient criteria for IBS"],
          ], [CW*0.36, CW*0.64]),
          sp(3),
          pearl("Rome IV distinguishes functional constipation from IBS-C: IBS-C requires abdominal PAIN linked "
                "to bowel habit. In functional constipation, defaecatory symptoms dominate."),
          sp(4),
          sec("PATHOPHYSIOLOGY", 'ST'),
          path_box("Normal defaecation: HAPCs (high-amplitude propagating contractions) propel stool to rectum → "
                   "rectal distension → RAIR (recto-anal inhibitory reflex relaxes IAS) → puborectalis relaxes, "
                   "anorectal angle opens → straining raises IAP → stool expelled. Failure at any step = constipation."),
          sp(3),
          mktbl([
              ["Mechanism","Pathophysiology","Clinical Result"],
              ["Slow colonic transit",
               "↓ HAPCs + ↑ segmenting (non-propulsive) contractions in sigmoid. Enteric excitatory motor innervation changes. ↑ retrograde pressure waves.",
               "Water over-absorbed → hard stools; <3 BM/week"],
              ["Pelvic floor dyssynergia",
               "Paradoxical contraction of puborectalis/EAS during straining instead of relaxing (anismus). Failure of RAIR.",
               "Functional outlet obstruction — stool reaches rectum but cannot exit. Prolonged straining."],
              ["Rectal hyposensitivity",
               "Blunted rectal sensation → reduced urge. Rectum chronically loaded without awareness.",
               "Infrequent defaecation; large hard stools; impaction risk"],
              ["Hirschsprung's",
               "Absent ganglion cells → tonic contraction of aganglionic segment → functional obstruction",
               "Neonatal / childhood — failure to pass meconium; chronic constipation"],
          ], [CW*0.27, CW*0.43, CW*0.3], TEAL)]

    # RIGHT
    s += [sec("CAUSES — 'SPINE' MNEMONIC"),
          mktbl([
              ["Letter","Category","Key Examples"],
              ["S","Structural",
               "Colorectal Ca (must exclude), benign strictures, anal fissure, haemorrhoids, "
               "rectocoele, pelvic organ prolapse, diverticular stricture, external pelvic mass"],
              ["P","Physiological / Dietary",
               "Low fibre (<25g/day), poor fluid intake, immobility, pregnancy (progesterone + iron), "
               "IBS-C, obesity, ignoring defaecatory urge → rectal hyposensitivity"],
              ["I","Iatrogenic — Drugs",
               "Opioids #1 (μ-receptor → ↓ peristalsis), Al/Ca antacids, iron tablets, TCAs, "
               "anticholinergics, verapamil (CCBs), ondansetron, antipsychotics (clozapine), antidiarrhoeals"],
              ["N","Neurological",
               "Parkinson's disease, MS, spinal cord injury, cauda equina syndrome, "
               "autonomic neuropathy (DM), Hirschsprung's, pseudo-obstruction"],
              ["E","Endocrine / Metabolic",
               "Hypothyroidism #1 metabolic, hypercalcaemia, hypokalaemia, hypomagnesaemia, "
               "diabetes (autonomic neuropathy), uraemia, pregnancy, panhypopituitarism"],
          ], [CW*0.07, CW*0.25, CW*0.68]),
          sp(3),
          alarm("New-onset constipation in a patient >50 years = colorectal carcinoma until proven otherwise. "
                "Alarm features (rectal bleeding, weight loss, IDA, palpable mass, family Hx CRC, "
                "change in bowel habit >6 weeks) → urgent colonoscopy / 2WW referral."),
          sp(4),
          sec("PRIMARY (FUNCTIONAL) CAUSES"),
          mktbl([
              ["Type","Features & Management"],
              ["Slow transit\n(colonic inertia)",
               "Predominantly female (>95%). ↓ HAPCs throughout colon. May start in teens. "
               "Sitzmarks: markers scattered throughout colon. "
               "Rx: prucalopride 2mg OD (5-HT₄); subtotal colectomy + IRA if all else fails."],
              ["Pelvic floor dyssynergia\n(anismus)",
               "Paradoxical puborectalis contraction on straining. Normal transit. "
               "Dx: balloon expulsion test (prolonged) + anorectal manometry (↑ EAS on defaecation) + proctogram. "
               "Rx: biofeedback therapy — 70% response rate."],
              ["IBS-C",
               "Abdominal pain dominant + linked to bowel habit (Rome IV). "
               "All investigations normal. Visceral hypersensitivity. "
               "Rx: low-FODMAP diet, linaclotide 72 μg OD, antispasmodics, CBT."],
              ["Rectocoele",
               "Anterior rectal wall herniates into posterior vagina. Women only. "
               "Stool traps in pouch → need vaginal digitation to defaecate. "
               "Confirmed on defaecating proctogram. Rx: physiotherapy; surgical repair if >3 cm."],
          ], [CW*0.27, CW*0.73])]

    s.append(PageBreak())

    # ══ PAGE 3 — HISTORY TAKING ═══════════════════════════════════════════════
    # LEFT
    s += [sec("HISTORY TAKING — CONSTIPATION"),
          body("<b>Opening:</b> 'Can you describe your bowel habit? How often do you open your bowels? "
               "Has anything changed recently?'"),
          sp(3),
          ssec("1. CHARACTER OF CONSTIPATION"),
          mktbl([
              ["Question","Clinical Meaning","Implication"],
              ["Frequency — BM per week?","<3/week = Rome IV criterion","Chronic if ≥6 months"],
              ["Stool consistency?","Bristol 1–2 = slow transit","Can show patient Bristol chart"],
              ["Straining?","Outlet dysfunction / dyssynergia","→ Balloon expulsion test"],
              ["Incomplete evacuation?","Outlet dysfunction; rectocoele","→ Defaecating proctogram"],
              ["Manual assistance?","Severe pelvic floor disorder","Digitation vaginally or anally"],
              ["Acute vs chronic?","Acute → obstruction / Ca; Chronic → functional","Key discriminator for urgency"],
              ["Alternating diarrhoea?","IBS-C  OR  overflow around impaction","NEVER treat overflow with antidiarrhoeals!"],
              ["Rectal bleeding / mucus?","CRC, IBD, haemorrhoids, fissure","ALARM FEATURE — investigate urgently"],
          ], [CW*0.32, CW*0.36, CW*0.32]),
          sp(3),
          ssec("2. ASSOCIATED SYMPTOMS"),
          mktbl([
              ["Symptom","Think of..."],
              ["Abdominal pain relieved by defaecation",      "IBS-C (Rome IV hallmark)"],
              ["Weight loss + change in bowel habit",         "CRC — urgent 2WW referral"],
              ["Bloating + ribbony stools + tenesmus",        "Rectal / sigmoid carcinoma"],
              ["Fatigue + cold intolerance + weight gain + dry skin","Hypothyroidism → check TSH"],
              ["Polyuria + polydipsia + nausea + bone pain",  "Hypercalcaemia — 'Bones, Stones, Groans, Moans'"],
              ["Absolute constipation + vomiting + distension","Large bowel obstruction — EMERGENCY"],
              ["Perianal pain + blood on paper",              "Anal fissure → pain → voluntary withholding"],
              ["Saddle anaesthesia + bilateral leg weakness + urinary symptoms","Cauda equina syndrome — MRI SPINE EMERGENCY"],
              ["Urinary symptoms + pelvic prolapse",          "Pelvic floor disorder (rectocoele, cystocoele)"],
          ], [CW*0.48, CW*0.52]),
          sp(3),
          alarm("Cauda equina syndrome: saddle anaesthesia + bilateral leg weakness + urinary/bowel dysfunction "
                "→ MRI spine EMERGENCY. Do NOT delay for bowel workup.")]

    # RIGHT
    s += [ssec("3. DRUG HISTORY — CONSTIPATING DRUGS"),
          mktbl([
              ["Drug Class","Example(s)","Mechanism"],
              ["Opioids  (#1 cause)","Morphine, codeine, tramadol, fentanyl",
               "μ-receptor: ↓ peristalsis + ↑ IAS tone + ↓ rectal sensation"],
              ["Antacids","Al(OH)₃, CaCO₃ (Rennies, Gaviscon)",
               "Bind water → insoluble pellets; astringent effect on mucosa"],
              ["Iron supplements","Ferrous sulphate, ferrous gluconate",
               "Direct GI mucosal irritation; hard dark stool"],
              ["Anticholinergics","TCAs, oxybutynin, hyoscine, antihistamines, antipsychotics",
               "↓ ACh → ↓ smooth muscle contraction throughout GI tract"],
              ["Calcium channel blockers","Verapamil >> diltiazem > amlodipine",
               "↓ L-type Ca²⁺ channels → ↓ gut smooth muscle tone"],
              ["5-HT₃ antagonists","Ondansetron, granisetron",
               "Block serotonin-mediated peristalsis in ENS"],
              ["Diuretics","Loop + thiazides",
               "Dehydration → concentrated faeces → hard stools"],
              ["Anticonvulsants","Gabapentin, pregabalin, phenytoin",
               "↓ neuronal firing including enteric nervous system"],
          ], [CW*0.27, CW*0.3, CW*0.43]),
          sp(3),
          pearl("OIC (opioid-induced constipation): most common in-hospital drug cause. "
                "Treat with osmotic (macrogol) + stimulant (senna) combined. "
                "Add methylnaltrexone SC (peripherally acting μ-antagonist) if refractory — "
                "reverses OIC without antagonising central analgesia."),
          sp(3),
          ssec("4. SOCIAL HISTORY"),
          mktbl([
              ["Area","Key Questions","Significance"],
              ["Diet","Fibre intake — fruit, veg, wholegrains?","Target 25–30g/day; <15g/day = significant risk factor"],
              ["Fluids","Daily fluid intake?","1.5–2 L/day needed; fibre is ineffective without water"],
              ["Exercise","Physical activity level?","Aerobic exercise directly ↑ colonic motility via ENS"],
              ["Mobility","Bed-bound? Wheelchair?","Institutionalised patients: near-universal constipation"],
              ["Pregnancy","Gestation? Iron supplements?","Progesterone ↓ motility + iron compounds effect"],
              ["Stress/Psych","Anxiety, depression, eating disorders?","IBS-C + anorexia → severe constipation; also abuse Hx"],
              ["Laxative Hx","What laxatives? How long? Any benefit?","Long-term stimulant laxatives → atonic colon"],
          ], [CW*0.18, CW*0.3, CW*0.52]),
          sp(3),
          ssec("5. PAST MEDICAL & FAMILY HISTORY"),
          bul("Previous colorectal / pelvic surgery / hysterectomy → adhesions; pelvic nerve damage"),
          bul("Known CRC / IBD / diverticular disease / anal fissure / endometriosis"),
          bul("Hypothyroidism, DM, hypercalcaemia, renal disease, Parkinson's, MS"),
          bul("Childhood constipation since birth → Hirschsprung's disease until excluded"),
          bul("<b>Family Hx CRC or FAP:</b> 1st-degree relative → colonoscopy from age 40–45")]

    s.append(PageBreak())

    # ══ PAGE 4 — EXAMINATION ═══════════════════════════════════════════════════
    # LEFT
    s += [sec("CLINICAL EXAMINATION — CONSTIPATION", 'ST'),
          ssec("A. GENERAL INSPECTION"),
          mktbl([
              ["Sign","Think of..."],
              ["Weight loss + pallor","Occult CRC; eating disorder; widespread motility disorder"],
              ["Weight gain + bradycardia + dry skin + periorbital oedema","Hypothyroidism → check TSH immediately"],
              ["Confusion + polyuria + weakness","Hypercalcaemia → corrected Ca²⁺"],
              ["Tremor + rigidity + bradykinesia","Parkinson's disease → neurogenic constipation"],
              ["Abdominal scars","Previous pelvic/abdominal surgery → adhesions"],
              ["Abdominal distension","Loaded colon; obstruction; megacolon"],
              ["Pallor (conjunctival)","IDA from occult CRC bleeding → FBC + colonoscopy"],
          ], [CW*0.4, CW*0.6]),
          sp(3),
          ssec("B. ABDOMINAL EXAMINATION"),
          mktbl([
              ["Finding","Interpretation","Action"],
              ["Distension","Loaded colon / obstruction / megacolon","AXR; surgical review if acute"],
              ["LIF mass — mobile, soft, indents on pressure","Faecal loading of sigmoid","Laxatives; reassess"],
              ["Hard, fixed, irregular mass — any quadrant","CRC until proven otherwise","Urgent colonoscopy + CT staging"],
              ["Diffuse mild tenderness","Loaded colon / constipation","Laxatives + reassess"],
              ["Tympany + absent bowel sounds","Obstruction / ileus","Erect AXR + surgical review urgently"],
              ["Hepatomegaly","Metastatic CRC; hypothyroidism; right heart failure","CT abdomen + LFTs"],
              ["Palpable bladder","Urinary retention — co-exists with faecal impaction","Catheterise + PR exam"],
          ], [CW*0.32, CW*0.38, CW*0.3]),
          sp(3),
          pearl("OSCE TRAP — Overflow diarrhoea: elderly patient with 'diarrhoea'. PR exam reveals packed rectum. "
                "Liquid stool passes around an impaction. NEVER give antidiarrhoeals — disimpact first!")]

    # RIGHT
    s += [ssec("C. PERIANAL & DIGITAL RECTAL EXAMINATION (DRE)"),
          body("<b>DRE is mandatory in ALL patients with constipation</b> — Bailey & Love 28e"),
          sp(2),
          mktbl([
              ["Finding","Interpretation"],
              ["Anal fissure — posterior midline","Pain → voluntary withholding → exacerbates constipation; sentinel tag"],
              ["External haemorrhoids","Pain / bleeding → patient avoids defaecation"],
              ["Rectal prolapse","Chronic straining; presents with mucus discharge + incontinence"],
              ["Perineal descent on straining","Pelvic floor weakness; >3.5 cm = abnormal"],
              ["Empty rectum on DRE","SLOW TRANSIT — stool not reaching rectum; investigate with transit study"],
              ["Hard stool in rectum","Faecal impaction / outlet dysfunction"],
              ["Anal tone ↓","Sphincter damage; neurological cause; rectal prolapse"],
              ["Anal tone ↑ (tight sphincter)","Anal fissure; dyssynergia; anxiety"],
              ["Anterior rectal wall bulge (F)","Rectocoele — stool traps in vaginal pouch"],
              ["Mass on DRE","CRC until proven otherwise → urgent 2WW referral"],
          ], [CW*0.42, CW*0.58]),
          sp(3),
          alarm("A mass felt on DRE is CRC until proven otherwise. Never reassure and discharge."),
          sp(4),
          sec("SPECIALIST ANORECTAL TESTS", 'SP'),
          mktbl([
              ["Test","Method","What It Tells You"],
              ["Balloon expulsion test",
               "50 mL water balloon in rectum; patient expels on commode",
               "Normal: <1 min. Prolonged = pelvic floor dyssynergia (anismus)"],
              ["Anorectal manometry",
               "Catheter records pressures + RAIR",
               "↑ resting = fissure/dyssynergia. ABSENT RAIR = Hirschsprung's (pathognomonic)"],
              ["Defaecating proctogram\n(MRI preferred)",
               "Barium/MRI during straining & evacuation",
               "Rectocoele, intussusception, perineal descent, prolapse, incomplete emptying"],
              ["Colonic transit study\n(Sitzmarks)",
               "20 radiopaque markers; AXR at Day 5",
               "Normal: <5 markers. Scattered = slow transit. Rectosigmoid = outlet dysfunction"],
              ["EMG — pudendal nerve",
               "Needle EMG of puborectalis",
               "Prolonged pudendal terminal motor latency → pudendal neuropathy"],
          ], [CW*0.25, CW*0.35, CW*0.4]),
          sp(3),
          pearl("Investigation sequence (Bailey & Love): Exclude secondary causes (bloods + colonoscopy if alarm features) → "
                "Trial of lifestyle + 2 laxatives → If chronic refractory: colonic transit study + anorectal manometry + "
                "proctogram → targeted treatment based on subtype.")]

    s.append(PageBreak())

    # ══ PAGE 5 — INVESTIGATIONS ════════════════════════════════════════════════
    # LEFT
    s += [sec("INVESTIGATIONS — CONSTIPATION"),
          ssec("FIRST-LINE BLOODS"),
          mktbl([
              ["Test","What to Find","Why It Matters"],
              ["FBC","Hb ↓ + MCV ↓ = IDA","Occult CRC / IBD bleeding → colonoscopy"],
              ["U&E","K⁺ ↓ (hypokalaemia)","Hypokalaemia causes constipation; assess hydration"],
              ["Corrected Ca²⁺","Ca²⁺ >2.6 mmol/L","Hypercalcaemia → check PTH if elevated"],
              ["TFTs (TSH ± free T4)","TSH ↑ = hypothyroidism","Most common reversible endocrine cause"],
              ["Glucose / HbA1c","DM diagnosis","Diabetic autonomic neuropathy"],
              ["Mg²⁺","Hypomagnesaemia","Check if Ca²⁺ / K⁺ also low"],
              ["CRP / ESR","↑ inflammation","IBD, CRC, diverticulitis, abscess"],
              ["CEA","↑ in CRC","Not diagnostic alone; used for monitoring after diagnosis"],
          ], [CW*0.22, CW*0.3, CW*0.48]),
          sp(3),
          ssec("IMAGING"),
          mktbl([
              ["Investigation","Indication","Findings"],
              ["AXR (plain film)","Suspected impaction; obstruction; acute symptoms",
               "Faecal loading; dilated loops (3–6–9 cm rule); megacolon; 'coffee bean' volvulus"],
              ["Contrast enema\n(Gastrografin / barium)","Suspected Hirschsprung's; stricture",
               "Transition zone (Hirschsprung's); strictures; mucosal pattern"],
              ["CT abdomen/pelvis +contrast","Mass / CRC staging; complicated disease",
               "Extent of Ca, LN, mets, pelvic anatomy"],
              ["CT colonography\n(virtual colonoscopy)","Elderly / unable to tolerate scope",
               "Same as colonoscopy; misses flat lesions; no biopsy"],
          ], [CW*0.28, CW*0.32, CW*0.4]),
          sp(3),
          ssec("ENDOSCOPY"),
          mktbl([
              ["Procedure","Indication","Key Points"],
              ["Flexible sigmoidoscopy","<50 yrs, no alarm features","Views rectum to descending colon"],
              ["Colonoscopy\n(GOLD STANDARD)","Alarm features; age >50; new-onset; positive FOB",
               "Full mucosal survey; biopsy; polypectomy — mandatory with alarm features"],
              ["OGD + duodenal biopsy","Concurrent malabsorption symptoms",
               "Exclude coeliac (check anti-tTG IgA first)"],
          ], [CW*0.28, CW*0.36, CW*0.36])]

    # RIGHT
    s += [sec("MANAGEMENT — LAXATIVE STEP LADDER"),
          ssec("STEP 1 — LIFESTYLE (ALWAYS FIRST)"),
          bul("<b>Dietary fibre:</b> Increase to 25–30g/day — fruits, vegetables, wholegrains, legumes"),
          bul("<b>Fluid intake:</b> 1.5–2 L/day — fibre CANNOT work without adequate water"),
          bul("<b>Exercise:</b> Regular aerobic activity directly ↑ colonic motility via ENS stimulation"),
          bul("<b>Toilet routine:</b> Regular attempt after meals (exploit gastrocolic reflex); never ignore the urge"),
          bul("<b>Positioning:</b> Footstool (squatting position) straightens anorectal angle → easier evacuation"),
          bul("<b>Medication review:</b> Stop / switch constipating drugs wherever clinically safe"),
          sp(3),
          ssec("STEP 2 — LAXATIVES"),
          mktbl([
              ["Class","Drug","Mechanism","Notes"],
              ["Bulk-forming\n(1st line mild)","Ispaghula husk\n(Fybogel)","↑ stool bulk → stimulates peristalsis",
               "Must take with ≥200 mL water. 2–3 days onset. Avoid in impaction. CI in dysphagia."],
              ["Osmotic\n(1st line OIC)","Macrogol (Movicol)\nLactulose\nMg hydroxide",
               "Non-absorbable polymer draws water into bowel osmotically",
               "Macrogol preferred — better evidence, less bloating. Lactulose causes gas. Mg²⁺ CI in renal failure."],
              ["Stimulant\n(add-on or short-term)","Senna\nBisacodyl","Stimulate myenteric plexus → ↑ peristalsis + ↑ secretion",
               "Onset 6–12 hrs. Theoretical atonic colon risk with long-term use. Bisacodyl suppository for faster effect."],
              ["Stool softener","Docusate sodium","Surfactant — ↓ surface tension → softer stool",
               "Mild; minimal evidence alone. Useful post-op or with anal fissure."],
              ["Rectal agents","Phosphate enema\nGlycerol suppository","Local osmotic + stimulant\nRectal lubricant + irritant",
               "For acute impaction / pre-procedure. Rapid onset (minutes)."],
              ["Secretagogue","Linaclotide 290 μg OD\nLubiprostone 8–24 μg BD",
               "↑ intestinal Cl⁻ secretion via GC-C → ↑ luminal fluid",
               "NICE approved when ≥2 laxatives failed. Linaclotide 72 μg for IBS-C."],
              ["Prokinetic","Prucalopride 2 mg OD\n(1 mg if >65 yrs)","Selective 5-HT₄ agonist → ↑ colonic HAPCs",
               "For chronic constipation after ≥2 laxatives failed. Strong RCT evidence."],
              ["PAMORA","Methylnaltrexone SC\nNaloxegol 25 mg OD","Peripheral μ-opioid antagonist in gut",
               "Specifically for OIC (opioid-induced constipation). Does NOT reverse central analgesia."],
          ], [CW*0.18, CW*0.2, CW*0.3, CW*0.32]),
          sp(3),
          pearl("LAXATIVE ORDER: Bulk-forming → Osmotic (macrogol) → Stimulant → Combine → Secretagogue/Prucalopride. "
                "For OPIOID-INDUCED: start osmotic + stimulant TOGETHER from day 1 of opioid. "
                "Bulk-forming alone is inadequate for OIC.")]

    s.append(PageBreak())

    # ══ PAGE 6 — SPECIFIC TREATMENTS · SURGERY ════════════════════════════════
    # LEFT
    s += [sec("MANAGEMENT — SPECIFIC CAUSES"),
          mktbl([
              ["Cause","Treatment","Key Note"],
              ["Hypothyroidism","Levothyroxine replacement",
               "Constipation resolves as euthyroid state restored. Recheck TFTs at 6–8 weeks."],
              ["Hypercalcaemia","IV 0.9% NaCl + IV bisphosphonate (zoledronate 4mg)",
               "Then treat underlying cause (PTHrP, malignancy, primary hyperPTH)"],
              ["Hypokalaemia","IV / oral K⁺ replacement","Address underlying cause (diuretics, vomiting)"],
              ["Opioid-induced (OIC)","Macrogol + senna; add methylnaltrexone SC if refractory",
               "Do NOT stop opioid without clinical review"],
              ["Anal fissure","Topical GTN 0.4% or diltiazem 2% gel BD × 8 wks",
               "Botulinum toxin if topical fails; lateral internal sphincterotomy if chronic (>3 months)"],
              ["Faecal impaction","Macrogol disimpaction (8 sachets/day × 3 days)",
               "Phosphate enema if rectal. Manual evacuation under sedation if severe."],
              ["Pelvic floor dyssynergia","Biofeedback therapy (10–15 sessions)",
               "Gold standard — 70% response rate. Retrains paradoxical contraction."],
              ["Slow transit constipation","Prucalopride 2mg OD (after 2 laxatives fail)",
               "Subtotal colectomy + IRA if ALL medical therapy exhausted"],
              ["Rectocoele","Pelvic floor physiotherapy first",
               "Surgical repair (posterior colporrhaphy / transanal) if symptomatic + >3 cm"],
              ["IBS-C","Low-FODMAP diet + linaclotide 72 μg OD + antispasmodics",
               "CBT + gut-directed hypnotherapy improves outcomes significantly"],
              ["CRC causing obstruction","Emergency: Hartmann's / colonic stent as bridge",
               "Elective resection: anterior resection (rectum/sigmoid) or right hemicolectomy"],
              ["Ogilvie's syndrome\n(acute pseudo-obstruction)","IV neostigmine 2mg over 3–5 min",
               "Monitor HR (bradycardia risk); colonoscopic decompression if neostigmine fails"],
          ], [CW*0.27, CW*0.38, CW*0.35]),
          sp(4),
          sec("SURGICAL OPTIONS IN REFRACTORY CONSTIPATION", 'SP'),
          body("<b>Prerequisites before surgery (Bailey & Love):</b>"),
          bul("Pathophysiology confirmed with specialist tests (transit study + manometry + proctogram)"),
          bul("All conservative options exhausted (minimum 2 laxatives + prucalopride + biofeedback)"),
          bul("MDT review; patient fully counselled on realistic outcomes")]

    # RIGHT
    s += [ssec("SURGICAL PROCEDURES"),
          mktbl([
              ["Procedure","Indication","Complications"],
              ["Subtotal colectomy +\nileorectal anastomosis (IRA)",
               "Refractory slow transit constipation; all medical therapy failed",
               "Diarrhoea 5–15%; SBO 10–20%; faecal incontinence 10–20%"],
              ["STARR (Stapled transanal\nrectal resection)",
               "Rectal intussusception + rectocoele causing outlet dysfunction",
               "Urgency; fistula; haemorrhage"],
              ["Posterior colporrhaphy /\ntransanal rectocoele repair",
               "Symptomatic rectocoele failing physiotherapy",
               "Good results for isolated rectocoele"],
              ["Ventral rectopexy (laparoscopic)","Rectal prolapse / intussusception",
               "Mesh fixation to sacral promontory; low recurrence rate"],
              ["Sacral nerve stimulation (SNS)","Pelvic floor dysfunction; refractory outlet",
               "Neuromodulation S2–S4; reversible"],
              ["ACE procedure\n(antegrade colonic enema)","Severe intractable constipation; neurogenic bowel",
               "Appendicocoecostomy; allows antegrade irrigation"],
              ["Stoma (ileostomy / colostomy)","Last resort; refractory all therapies",
               "May be only effective solution in severe cases"],
          ], [CW*0.3, CW*0.35, CW*0.35]),
          sp(4),
          sec("HIRSCHSPRUNG'S DISEASE — COMPLETE", 'SP'),
          mktbl([
              ["Feature","Details"],
              ["Pathology","Absent ganglion cells (Meissner's + Auerbach's plexus) in distal colorectum → tonic contraction → functional obstruction"],
              ["Cause","Failed neural crest cell migration (weeks 5–12). RET proto-oncogene mutation (50% of cases)."],
              ["Length","Short segment 75% (rectosigmoid); Long segment 20%; Total colonic 5%"],
              ["Neonatal presentation","Failure to pass meconium within 48 hrs; abdominal distension; bilious vomiting"],
              ["Childhood presentation","Chronic constipation since birth; ribbon stools; growth retardation; never had normal BH"],
              ["Complication","HAEC (Hirschsprung-associated enterocolitis): fever + explosive diarrhoea + sepsis — life-threatening"],
              ["Gold standard Dx","Suction rectal biopsy — absent ganglion cells + ↑ AChE staining"],
              ["Barium enema","Transition zone: narrow aganglionic distal → dilated proximal (cone-shaped)"],
              ["Manometry","Absent RAIR — pathognomonic for Hirschsprung's"],
              ["Treatment","Surgical pull-through (Swenson / Soave / Duhamel); colostomy if acute toxic megacolon"],
          ], [CW*0.25, CW*0.75])]

    s.append(PageBreak())

    # ══ PAGE 7 — IBS-C · COMPLICATIONS · OSCE Q&A ═════════════════════════════
    # LEFT
    s += [sec("IBS-C vs FUNCTIONAL CONSTIPATION"),
          mktbl([
              ["Feature","IBS-C","Functional Constipation"],
              ["Dominant symptom","Abdominal PAIN — linked to defaecation","Defaecatory difficulty; bloating"],
              ["Pain pattern","Changes with defaecation (Rome IV core criterion)","Not consistently linked to defaecation"],
              ["Investigations","All NORMAL — diagnosis of exclusion","Normal (after secondary causes excluded)"],
              ["Treatment","Low-FODMAP, linaclotide 72 μg, antispasmodics, CBT","Laxative ladder; prucalopride; biofeedback"],
              ["Pathophysiology","Visceral hypersensitivity + altered gut-brain axis","↓ colonic motility / outlet dysfunction"],
          ], [CW*0.25, CW*0.38, CW*0.37]),
          sp(3),
          pearl("IBS is a DIAGNOSIS OF EXCLUSION. Never diagnose IBS if: age >50 with new symptoms, alarm features, "
                "nocturnal symptoms, anaemia, weight loss, positive FOB, family Hx CRC. Investigate first."),
          sp(4),
          sec("COMPLICATIONS OF CHRONIC CONSTIPATION", 'SR'),
          mktbl([
              ["Complication","Mechanism","Features / Significance"],
              ["Haemorrhoids","↑ venous pressure from straining",
               "Bright red bleeding; prolapse; mucus; pruritus"],
              ["Anal fissure","Hard stool tears posterior midline anoderm",
               "Severe pain on defaecation; bright red blood; sentinel tag"],
              ["Rectal prolapse","Chronic straining → stretched ligaments; pudendal neuropathy",
               "Full-thickness mucosa protrudes; mucus discharge; incontinence"],
              ["Rectocoele","Anterior rectal wall herniates into vaginal wall",
               "Need vaginal digitation to defaecate; confirmed on proctogram"],
              ["Faecal impaction","Chronic constipation + dehydration + immobility",
               "Overflow diarrhoea; urinary retention in elderly"],
              ["Stercoral ulcer / perforation","Pressure necrosis of colon wall by impacted faeces",
               "Antemesenteric wall; can perforate → peritonitis (high mortality)"],
              ["Megacolon / volvulus","Chronic dilatation; Hirschsprung's; pseudo-obstruction",
               "Sigmoid volvulus: 'coffee bean' on AXR; caecal volvulus"],
              ["UTI / urinary retention","Faecal impaction compresses bladder / urethra",
               "Common elderly; disimpaction resolves urinary symptoms"],
          ], [CW*0.25, CW*0.35, CW*0.4])]

    # RIGHT
    s += [sec("OSCE VIVA — RAPID FIRE Q&A"),
          mktbl([
              ["Examiner Asks...","Your Answer"],
              ["Define constipation (Rome IV)",
               "≥2 of: straining, Bristol 1–2 stools, incomplete evacuation, anorectal blockage, manual manoeuvres — "
               "in >25% defaecations; <3 BM/week; ≥3 months; onset ≥6 months"],
              ["Most common drug cause?",
               "Opioids (μ-receptor → ↓ peristalsis + ↑ sphincter tone). Also iron, Al antacids, TCAs, verapamil, ondansetron"],
              ["Metabolic causes to ALWAYS check?",
               "Hypothyroidism (TSH), hypercalcaemia (corrected Ca²⁺), hypokalaemia (K⁺), diabetes (autonomic neuropathy)"],
              ["Empty rectum on DRE?",
               "Slow transit constipation — stool not reaching rectum. Investigate with Sitzmarks colonic transit study"],
              ["Full rectum on DRE?",
               "Outlet dysfunction OR faecal impaction. Disimpact FIRST before laxatives"],
              ["What is overflow diarrhoea?",
               "Liquid stool bypasses faecal impaction — presents as diarrhoea. NEVER give antidiarrhoeals"],
              ["Balloon expulsion test — normal?",
               "Expelled in <1 minute. Prolonged = pelvic floor dyssynergia (anismus)"],
              ["Absent RAIR means?",
               "Pathognomonic of Hirschsprung's disease"],
              ["How to diagnose Hirschsprung's?",
               "Suction rectal biopsy — absent ganglion cells + ↑ AChE staining. Also absent RAIR on manometry"],
              ["Best treatment for dyssynergia?",
               "Biofeedback therapy — 70% response rate; retrains paradoxical puborectalis contraction"],
              ["Prucalopride — mechanism?",
               "Highly selective 5-HT₄ agonist → ↑ colonic motility (stimulates HAPCs)"],
              ["OIC treatment?",
               "Macrogol + stimulant; add methylnaltrexone SC (PAMORA) if refractory"],
              ["Safe laxative in pregnancy?",
               "Ispaghula husk (1st line) or lactulose. Avoid stimulants (senna) in 1st trimester"],
              ["Surgery for slow transit?",
               "Subtotal colectomy + ileorectal anastomosis — ONLY after all medical therapy failed + MDT review"],
              ["Stercoral perforation?",
               "Pressure necrosis of colon wall by impacted faeces → perforation → peritonitis → high mortality"],
              ["Ogilvie's syndrome — treatment?",
               "IV neostigmine 2mg (monitor HR); colonoscopic decompression; caecostomy as last resort"],
          ], [CW*0.42, CW*0.58]),
          sp(4),
          tip("FINAL OSCE TIP — Always: 1) Drug history (opioids? iron? verapamil?), "
              "2) Alarm features screen, 3) Bloods: TFTs + Ca²⁺ + FBC + K⁺, "
              "4) PR examination — NEVER omit, 5) Colonoscopy if ANY alarm feature present.")]

    doc.build(s, canvasmaker=BC)
    print("Constipation booklet built:", out)

build()
Editing File

~/osce-booklets/build_constipation_v2.py

Error: String not found in file: '"""
CONSTIPATION — Comprehensive Final Year MBBS OSCE Booklet
Sources: Bailey & Love 28e, Sleisenger & Fordtran, Sabiston, Harrison's 22e, Goldman-Cecil, Oxford Handbook
"""'
Running Command

head -3 /home/daytona/workspace/osce-booklets/build_constipation_v2.py

Running Command

rm /home/daytona/workspace/osce-booklets/build_constipation_v2.py

Running Command

cat > /home/daytona/workspace/osce-booklets/build_const2.py << 'ENDSCRIPT' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, HRFlowable, NextPageTemplate, PageBreak) from reportlab.lib.styles import ParagraphStyle from reportlab.lib.enums import TA_CENTER from reportlab.pdfgen import canvas as pdfgen_canvas NAVY=colors.HexColor('#1a2a4a'); GOLD=colors.HexColor('#c8a951') DB=colors.HexColor('#2c5282'); TL=colors.HexColor('#2a7a6b') RD=colors.HexColor('#c0392b'); RDBG=colors.HexColor('#fdecea') GN=colors.HexColor('#2e7d32'); GNBG=colors.HexColor('#e8f5e9') AM=colors.HexColor('#f57f17'); AMBG=colors.HexColor('#fff8e1') PR=colors.HexColor('#6a1b9a'); PRBG=colors.HexColor('#f3e5f5') W=colors.white; BK=colors.black GL=colors.HexColor('#f2f4f7'); GM=colors.HexColor('#d5dce8') PW,PH=A4; ML=13*mm; MR=13*mm; HH=18*mm; FH=10*mm UW=PW-ML-MR; CG=6*mm; CW=(UW-CG)/2 FTR='Sources: Bailey & Love 28e · Sleisenger & Fordtran · Harrison\'s 22e · Sabiston Surgery · Goldman-Cecil' def mk(): d={} d['TM']=ParagraphStyle('TM',fontName='Helvetica-Bold',fontSize=18,textColor=W,leading=22,alignment=TA_CENTER) d['TS']=ParagraphStyle('TS',fontName='Helvetica-BoldOblique',fontSize=10,textColor=GOLD,leading=13,alignment=TA_CENTER) d['TT']=ParagraphStyle('TT',fontName='Helvetica',fontSize=8,textColor=colors.HexColor('#a0bcd8'),leading=11,alignment=TA_CENTER) d['H1']=ParagraphStyle('H1',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=DB,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H2']=ParagraphStyle('H2',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=TL,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H3']=ParagraphStyle('H3',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=RD,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H4']=ParagraphStyle('H4',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=PR,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['SS']=ParagraphStyle('SS',fontName='Helvetica-Bold',fontSize=8.5,textColor=DB,leading=11,spaceAfter=3,spaceBefore=5) d['BD']=ParagraphStyle('BD',fontName='Helvetica',fontSize=7.8,textColor=BK,leading=11,spaceAfter=2) d['BL']=ParagraphStyle('BL',fontName='Helvetica',fontSize=7.8,textColor=BK,leading=11,leftIndent=10,bulletIndent=2,spaceAfter=1.5) d['BB']=ParagraphStyle('BB',fontName='Helvetica-Bold',fontSize=7.8,textColor=DB,leading=11,leftIndent=10,bulletIndent=2,spaceAfter=1.5) d['PE']=ParagraphStyle('PE',fontName='Helvetica-BoldOblique',fontSize=7.8,textColor=colors.HexColor('#7b3f00'),backColor=AMBG,borderColor=AM,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['AL']=ParagraphStyle('AL',fontName='Helvetica-Bold',fontSize=7.8,textColor=RD,backColor=RDBG,borderColor=RD,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['GT']=ParagraphStyle('GT',fontName='Helvetica',fontSize=7.8,textColor=colors.HexColor('#1b5e20'),backColor=GNBG,borderColor=GN,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['PB']=ParagraphStyle('PB',fontName='Helvetica-BoldOblique',fontSize=7.8,textColor=PR,backColor=PRBG,borderColor=PR,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['TH']=ParagraphStyle('TH',fontName='Helvetica-Bold',fontSize=7.5,textColor=W,leading=10,alignment=TA_CENTER) d['TC']=ParagraphStyle('TC',fontName='Helvetica',fontSize=7.5,textColor=BK,leading=10) d['FO']=ParagraphStyle('FO',fontName='Helvetica-Oblique',fontSize=6.5,textColor=colors.HexColor('#aaaaaa'),leading=9,alignment=TA_CENTER) return d ST=mk() class Cv(pdfgen_canvas.Canvas): def __init__(self,f,**kw): super().__init__(f,**kw); self._st=[]; self._p=0 def showPage(self): self._st.append(dict(self.__dict__)); self._startPage() def save(self): for x in self._st: self.__dict__.update(x); self._d(); super().showPage() super().save() def _d(self): self._p+=1; c=self c.setFillColor(NAVY); c.rect(0,PH-HH,PW,HH,fill=1,stroke=0) c.setFillColor(GOLD); c.rect(0,PH-HH-1.5,PW,1.5,fill=1,stroke=0) c.setFillColor(GOLD); c.setFont('Helvetica-Bold',8); c.drawString(ML,PH-10*mm,f"Page {self._p}") c.setFillColor(W); c.setFont('Helvetica-Bold',12); c.drawCentredString(PW/2,PH-8.5*mm,"CONSTIPATION") c.setFont('Helvetica',7.5); c.setFillColor(colors.HexColor('#a0bcd8')) c.drawCentredString(PW/2,PH-13.5*mm,"Clinical OSCE Notes | Final Year MBBS") c.setFillColor(GOLD); c.setFont('Helvetica-Bold',7.5) c.drawString(ML,PH-HH-8,"◀ LEFT"); c.drawRightString(PW-MR,PH-HH-8,"RIGHT ▶") c.setFillColor(NAVY); c.rect(0,0,PW,FH+3,fill=1,stroke=0) c.setFillColor(GOLD); c.rect(0,FH+3,PW,1,fill=1,stroke=0) c.setFillColor(colors.HexColor('#aaaaaa')); c.setFont('Helvetica-Oblique',6) c.drawCentredString(PW/2,4*mm,FTR) if self._p>1: c.setStrokeColor(GM); c.setLineWidth(0.5) c.line(ML+CW+CG/2,PH-HH-12,ML+CW+CG/2,FH+6) def h1(t): return Paragraph(f"<b>{t}</b>",ST['H1']) def h2(t): return Paragraph(f"<b>{t}</b>",ST['H2']) def h3(t): return Paragraph(f"<b>{t}</b>",ST['H3']) def h4(t): return Paragraph(f"<b>{t}</b>",ST['H4']) def ss(t): return Paragraph(t,ST['SS']) def bd(t): return Paragraph(t,ST['BD']) def bl(t): return Paragraph(f"• {t}",ST['BL']) def bb(t): return Paragraph(f"• {t}",ST['BB']) def pe(t): return Paragraph(f"★ OSCE PEARL: {t}",ST['PE']) def al(t): return Paragraph(f"⚠ RED FLAG: {t}",ST['AL']) def gt(t): return Paragraph(t,ST['GT']) def pb(t): return Paragraph(f"🔬 PATHOPHYSIOLOGY: {t}",ST['PB']) def sp(h=3): return Spacer(1,h) def tb(rows,ws,bg=None): if bg is None: bg=DB ts=TableStyle([('BACKGROUND',(0,0),(-1,0),bg),('TEXTCOLOR',(0,0),(-1,0),W), ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'),('FONTSIZE',(0,0),(-1,-1),7.5), ('ROWBACKGROUNDS',(0,1),(-1,-1),[GL,W]),('GRID',(0,0),(-1,-1),0.4,GM), ('LEFTPADDING',(0,0),(-1,-1),4),('RIGHTPADDING',(0,0),(-1,-1),4), ('TOPPADDING',(0,0),(-1,-1),3),('BOTTOMPADDING',(0,0),(-1,-1),3), ('VALIGN',(0,0),(-1,-1),'TOP')]) wr=[] for ri,row in enumerate(rows): wr.append([Paragraph(c,ST['TH'] if ri==0 else ST['TC']) if isinstance(c,str) else c for c in row]) return Table(wr,colWidths=ws,style=ts,hAlign='LEFT') def build(): out='/home/daytona/workspace/osce-booklets/Constipation_OSCE_Booklet_v2.pdf' by=FH+8; ty=PH-HH-14; fh=ty-by cf=Frame(ML,by,UW,fh,leftPadding=0,rightPadding=0,topPadding=0,bottomPadding=0) lf=Frame(ML,by,CW,fh,leftPadding=0,rightPadding=3,topPadding=0,bottomPadding=0) rff=Frame(ML+CW+CG,by,CW,fh,leftPadding=3,rightPadding=0,topPadding=0,bottomPadding=0) doc=BaseDocTemplate(out,pagesize=A4,leftMargin=ML,rightMargin=MR,topMargin=22*mm,bottomMargin=16*mm) doc.addPageTemplates([PageTemplate(id='cover',frames=[cf]),PageTemplate(id='two',frames=[lf,rff])]) s=[] # COVER s+=[NextPageTemplate('cover'),sp(26), Paragraph("CONSTIPATION",ST['TM']),sp(4), Paragraph("Clinical OSCE Notes | Final Year MBBS",ST['TS']),sp(6), Paragraph("Epidemiology · Pathophysiology · Rome IV · History Taking · Examination<br/>Investigations · Laxative Ladder · Specific Disorders · Surgical Options · OSCE Q&amp;A",ST['TT']), sp(7),HRFlowable(width="80%",thickness=1.5,color=GOLD,spaceAfter=8,hAlign='CENTER'), Paragraph(FTR,ST['FO']),PageBreak()] # PAGE 2 — DEFINITION / PATHOPHYSIOLOGY / CAUSES s.append(NextPageTemplate('two')) # LEFT s+=[h1("DEFINITION & EPIDEMIOLOGY"), bd("<b>Definition (Rome IV):</b> Unsatisfactory defaecation characterised by infrequent stools, difficult stool passage, or both for ≥3 months. Chronic constipation requires ≥6 months AND failure of ≥2 laxative classes. — Sleisenger & Fordtran"),sp(3), bd("<b>Prevalence:</b> ~10% worldwide (self-reported). True chronic constipation ~0.5%. Female:Male = 2:1 (self-reported); 9:1 for chronic constipation. Prevalence >50% in institutionalised patients over 65. — Bailey & Love 28e"),sp(4), h1("ROME IV CRITERIA — FUNCTIONAL CONSTIPATION"), bd("≥2 of the following during >25% of defaecations; last 3 months; onset ≥6 months ago:"),sp(2), tb([["Criterion","Clinical Significance"], ["1. Straining","Suggests outlet dysfunction or slow transit"], ["2. Hard/lumpy stools (Bristol 1–2)","Prolonged colonic transit time"], ["3. Incomplete evacuation","Outlet dysfunction or rectocoele"], ["4. Anorectal blockage sensation","Functional obstruction — dyssynergia"], ["5. Manual manoeuvres needed","Severe pelvic floor disorder"], ["6. <3 spontaneous BM/week","Frequency criterion"], ["PLUS (exclusion)","Loose stools rarely without laxatives; insufficient criteria for IBS"]], [CW*0.38,CW*0.62]),sp(3), pe("Rome IV separates functional constipation from IBS-C: IBS-C requires abdominal PAIN as the dominant symptom, linked to bowel habit change. Functional constipation = defaecatory symptoms without dominant pain."),sp(4), h2("PATHOPHYSIOLOGY"), pb("Normal defaecation: HAPCs (high-amplitude propagating contractions) propel stool to rectum → rectal distension → RAIR (relaxes IAS) → puborectalis relaxes, anorectal angle opens → ↑ IAP → stool expelled. Failure at ANY step = constipation."),sp(3), tb([["Mechanism","Pathophysiology","Clinical Result"], ["Slow colonic transit","↓ HAPCs + ↑ non-propulsive segmenting contractions in sigmoid. Changes in enteric excitatory motor innervation. ↑ retrograde waves.","Water over-absorbed → hard stools; <3 BM/week; Sitzmarks scattered throughout colon"], ["Pelvic floor dyssynergia\n(anismus)","Paradoxical contraction of puborectalis/EAS during straining (instead of relaxing). Absent RAIR-equivalent.","Functional outlet obstruction. Stool reaches rectum but cannot exit. Prolonged straining."], ["Rectal hyposensitivity","Blunted sensation of rectal fullness → reduced urge. Chronically loaded rectum without awareness.","Infrequent defaecation; impaction risk; DRE shows full rectum"], ["Hirschsprung's disease","Absent ganglion cells (Meissner's + Auerbach's plexus) → tonic contraction of aganglionic segment → functional obstruction","Neonatal: failed meconium passage. Child: lifelong constipation."]], [CW*0.27,CW*0.43,CW*0.30],TL)] # RIGHT s+=[h1("CAUSES — 'SPINE' MNEMONIC"), tb([["Letter","Category","Key Examples"], ["S","Structural","Colorectal Ca (MUST exclude), benign strictures, anal fissure, haemorrhoids (pain-avoidance), rectocoele, pelvic organ prolapse, diverticular stricture, external pelvic mass"], ["P","Physiological/Dietary","Low fibre (<25g/day), poor fluid intake, immobility, pregnancy (progesterone + iron supplements), IBS-C, obesity"], ["I","Iatrogenic — Drugs","Opioids #1 (μ-receptor → ↓ peristalsis + ↑ IAS tone), Al/Ca antacids, iron, TCAs, anticholinergics, verapamil (CCBs), ondansetron, antipsychotics (clozapine), antidiarrhoeals"], ["N","Neurological","Parkinson's disease (↓ dopamine → ↓ gut motility), MS, spinal cord injury, cauda equina, autonomic neuropathy (DM), Hirschsprung's, Ogilvie's (pseudo-obstruction)"], ["E","Endocrine/Metabolic","Hypothyroidism #1 metabolic cause, hypercalcaemia, hypokalaemia, hypomagnesaemia, DM (autonomic neuropathy), uraemia, pregnancy, panhypopituitarism"]], [CW*0.07,CW*0.25,CW*0.68]),sp(3), al("New-onset constipation in patient >50 = colorectal carcinoma until proven otherwise. Alarm features (rectal bleeding, weight loss, IDA, palpable mass, family Hx CRC, change in bowel habit >6 weeks) → urgent colonoscopy / 2WW referral."),sp(4), h1("PRIMARY (FUNCTIONAL) CAUSES"), tb([["Type","Features & Management"], ["Slow transit\n(colonic inertia)","Overwhelmingly female (>95%). ↓ HAPCs. Onset teens/early adulthood. Sitzmarks: markers scattered throughout colon (vs outlet dysfunction = rectosigmoid accumulation). Rx: prucalopride 2mg OD (5-HT₄); subtotal colectomy + IRA if all else fails."], ["Pelvic floor dyssynergia\n(anismus)","Paradoxical puborectalis contraction on straining. Normal colonic transit. Dx: balloon expulsion test (prolonged >1 min) + anorectal manometry (↑ EAS pressure during defaecation) + defaecating proctogram. Rx: biofeedback therapy — 70% response rate."], ["IBS-C","Abdominal pain DOMINANT + linked to bowel habit (Rome IV). All investigations normal. Visceral hypersensitivity + altered gut-brain axis. Rx: low-FODMAP diet, linaclotide 72 μg OD, antispasmodics, CBT/gut-directed hypnotherapy."], ["Rectocoele","Anterior rectal wall herniates into posterior vagina. Women only. Stool traps in pouch → need vaginal digitation to defaecate. Confirmed on defaecating proctogram. Rx: pelvic floor physio; surgical repair if >3 cm + symptomatic."]], [CW*0.27,CW*0.73])] s.append(PageBreak()) # PAGE 3 — HISTORY TAKING # LEFT s+=[h1("HISTORY TAKING — CONSTIPATION"), bd("<b>Opening:</b> 'Can you describe your bowel habit? How often are you opening your bowels? Has this changed recently?'"),sp(3), ss("1. CHARACTER OF CONSTIPATION"), tb([["Question","Clinical Meaning","Implication"], ["Frequency — BM per week?","<3/week = Rome IV criterion","Chronic if ≥6 months"], ["Stool consistency?","Bristol 1–2 = slow transit","Show patient Bristol stool chart"], ["Straining?","Outlet dysfunction / dyssynergia","→ Balloon expulsion test"], ["Incomplete evacuation?","Outlet dysfunction; rectocoele","→ Defaecating proctogram"], ["Manual assistance?","Severe pelvic floor disorder","Digitation vaginally or anally"], ["Acute vs chronic?","Acute → obstruction / Ca; Chronic → functional","Key discriminator for urgency"], ["Alternating diarrhoea?","IBS-C OR overflow around impaction","NEVER treat overflow with antidiarrhoeals!"], ["Rectal bleeding / mucus?","CRC, IBD, haemorrhoids, fissure","ALARM — investigate urgently"]], [CW*0.32,CW*0.36,CW*0.32]),sp(3), ss("2. ASSOCIATED SYMPTOMS"), tb([["Symptom","Consider"], ["Abdominal pain relieved by defaecation","IBS-C (Rome IV hallmark)"], ["Weight loss + change in bowel habit","CRC — urgent 2WW referral"], ["Bloating + ribbony stools + tenesmus","Rectal / sigmoid carcinoma"], ["Fatigue + cold intolerance + weight gain + dry skin","Hypothyroidism → check TSH"], ["Polyuria + polydipsia + nausea + bone pain","Hypercalcaemia — 'Bones, Stones, Groans, Moans'"], ["Absolute constipation + vomiting + distension","Large bowel obstruction — EMERGENCY"], ["Perianal pain + bright red blood on wiping","Anal fissure → pain → voluntary withholding"], ["Saddle anaesthesia + bilateral leg weakness + urinary incontinence","Cauda equina — MRI spine EMERGENCY"], ["Urinary symptoms + pelvic prolapse","Pelvic floor disorder (rectocoele, cystocoele)"]], [CW*0.48,CW*0.52]),sp(3), al("Cauda equina syndrome: saddle anaesthesia + bilateral leg weakness + urinary/bowel dysfunction → MRI spine EMERGENCY. Do NOT delay.")] # RIGHT s+=[ss("3. DRUG HISTORY — CONSTIPATING DRUGS"), tb([["Drug Class","Example(s)","Mechanism"], ["Opioids #1 cause","Morphine, codeine, tramadol, fentanyl","μ-receptor: ↓ peristalsis + ↑ IAS tone + ↓ rectal sensation"], ["Antacids","Al(OH)₃, CaCO₃ (Rennies, Gaviscon)","Bind water → insoluble pellets; astringent mucosal effect"], ["Iron supplements","Ferrous sulphate / gluconate","Direct GI mucosal irritation; hard dark stool"], ["Anticholinergics","TCAs, oxybutynin, hyoscine, antihistamines","↓ ACh → ↓ smooth muscle contraction in GI tract"], ["Ca²⁺ channel blockers","Verapamil >> diltiazem > amlodipine","↓ L-type Ca²⁺ channels → ↓ gut smooth muscle tone"], ["5-HT₃ antagonists","Ondansetron, granisetron","Block serotonin-mediated peristalsis in ENS"], ["Antipsychotics","Clozapine, olanzapine, quetiapine","Anticholinergic + anti-adrenergic effects"], ["Diuretics","Loop + thiazide diuretics","Dehydration → hard concentrated faeces"], ["Anticonvulsants","Gabapentin, pregabalin, phenytoin","↓ neuronal firing including ENS"], ["Antidiarrhoeals (iatrogenic)","Loperamide, codeine","Opioid mechanism on gut"]], [CW*0.27,CW*0.3,CW*0.43]),sp(3), pe("OIC (Opioid-Induced Constipation): most common in-hospital cause. Treat with osmotic (macrogol) + stimulant (senna) TOGETHER from day 1 of opioid. Add methylnaltrexone SC (PAMORA) if refractory — reverses OIC without reversing central analgesia."),sp(3), ss("4. SOCIAL HISTORY"), tb([["Area","Ask","Significance"], ["Diet","Fibre intake — fruit, veg, wholegrains?","Target 25–30g/day; <15g = significant risk"], ["Fluids","Daily fluid intake?","1.5–2 L/day needed; fibre ineffective without water"], ["Exercise","Physical activity level?","Aerobic exercise directly ↑ colonic motility via ENS"], ["Mobility","Bed-bound? Wheelchair?","Institutionalised: near-universal constipation"], ["Pregnancy","Gestation? Iron tablets?","Progesterone ↓ motility; iron compounds effect"], ["Psychosocial","Anxiety, depression, eating disorders?","IBS-C, anorexia → severe constipation"], ["Laxative history","What? How long? Any benefit?","Long-term stimulants → atonic colon risk"]], [CW*0.18,CW*0.3,CW*0.52]),sp(3), ss("5. PAST MEDICAL & FAMILY HISTORY"), bl("Previous colorectal/pelvic surgery / hysterectomy → adhesions; pelvic nerve damage"), bl("Known CRC / IBD / diverticular disease / anal fissure / endometriosis"), bl("Hypothyroidism, DM, hypercalcaemia, renal disease, Parkinson's, MS"), bl("Childhood constipation since birth → Hirschsprung's disease until excluded"), bl("<b>Family Hx CRC or FAP:</b> 1st-degree relative → colonoscopy from age 40–45")] s.append(PageBreak()) # PAGE 4 — EXAMINATION # LEFT s+=[h2("CLINICAL EXAMINATION — CONSTIPATION"),ss("A. GENERAL INSPECTION"), tb([["Sign","Think of..."], ["Weight loss + pallor","Occult CRC; eating disorder; widespread motility disorder"], ["Weight gain + bradycardia + dry skin + periorbital oedema","Hypothyroidism → check TSH immediately"], ["Confusion + polyuria + weakness + constipation","Hypercalcaemia → corrected Ca²⁺"], ["Tremor + rigidity + bradykinesia","Parkinson's disease → neurogenic constipation"], ["Abdominal scars","Previous pelvic/abdominal surgery → adhesions"], ["Abdominal distension","Loaded colon; obstruction; megacolon"], ["Conjunctival pallor","IDA from occult CRC bleeding → FBC + colonoscopy"]], [CW*0.4,CW*0.6]),sp(3), ss("B. ABDOMINAL EXAMINATION"), tb([["Finding","Interpretation","Action"], ["Distension","Loaded colon / obstruction / megacolon","AXR; surgical review if acute"], ["LIF mass — mobile, soft, indents on pressure","Faecal loading of sigmoid","Laxatives; reassess"], ["Hard, fixed, irregular mass — any quadrant","CRC until proven otherwise","Urgent colonoscopy + CT staging"], ["Diffuse mild tenderness","Loaded colon / constipation","Laxatives + reassess"], ["Tympany + absent bowel sounds","Obstruction / ileus","Erect AXR + urgent surgical review"], ["Hepatomegaly","Metastatic CRC; hypothyroidism","CT abdomen + LFTs"], ["Palpable bladder","Urinary retention co-existing with faecal impaction","Catheterise + PR exam"]], [CW*0.32,CW*0.38,CW*0.3]),sp(3), pe("OSCE TRAP — Overflow diarrhoea: elderly patient with 'diarrhoea'. PR exam reveals packed rectum. Liquid stool passes AROUND impaction. NEVER give antidiarrhoeals — disimpact first!")] # RIGHT s+=[ss("C. PERIANAL & DIGITAL RECTAL EXAMINATION (DRE)"), bd("<b>DRE is mandatory in ALL patients presenting with constipation.</b> — Bailey & Love 28e"),sp(2), tb([["Finding on DRE","Interpretation"], ["Anal fissure — posterior midline","Pain → voluntary withholding → exacerbates constipation; sentinel tag"], ["External haemorrhoids","Pain/bleeding → patient avoids defaecation"], ["Rectal prolapse","Chronic straining → stretched ligaments; mucus discharge + incontinence"], ["Perineal descent on straining","Pelvic floor weakness; >3.5 cm = abnormal"], ["Empty rectum on DRE","SLOW TRANSIT — stool not reaching rectum; investigate with transit study"], ["Hard stool in rectum","Faecal impaction / outlet dysfunction"], ["Anal tone ↓","Sphincter damage; neurological cause; rectal prolapse"], ["Anal tone ↑ (very tight)","Anal fissure; dyssynergia; anxiety"], ["Anterior rectal wall bulge (women)","Rectocoele — stool traps in vaginal pouch"], ["Mass on DRE","CRC until proven otherwise → urgent 2WW referral"]], [CW*0.42,CW*0.58]),sp(3), al("A mass felt on DRE = CRC until proven otherwise. Never reassure and discharge without investigation."),sp(4), h4("SPECIALIST ANORECTAL PHYSIOLOGY TESTS"), tb([["Test","Method","What It Shows"], ["Balloon expulsion test","50 mL water balloon in rectum; patient expels on commode","Normal: <1 min. Prolonged = pelvic floor dyssynergia (anismus)"], ["Anorectal manometry","Catheter records resting + squeeze pressures + RAIR","↑ resting = fissure/dyssynergia. ABSENT RAIR = Hirschsprung's (pathognomonic)"], ["Defaecating proctogram\n(MRI preferred)","Contrast/MRI during straining and evacuation; lateral view","Rectocoele, intussusception, perineal descent, prolapse, incomplete emptying"], ["Colonic transit study\n(Sitzmarks)","20 radiopaque markers; AXR at Day 5","<5 markers = normal. Scattered = slow transit. Rectosigmoid = outlet dysfunction"], ["Pudendal nerve EMG","Needle EMG of puborectalis/EAS","Prolonged pudendal terminal motor latency → pudendal neuropathy"]], [CW*0.25,CW*0.35,CW*0.4]),sp(3), pe("Investigation sequence (Bailey & Love 28e): Exclude secondary causes (bloods + colonoscopy if alarm features) → Trial lifestyle + 2 laxatives → If chronic refractory → colonic transit study + anorectal manometry + proctogram → targeted treatment.")] s.append(PageBreak()) # PAGE 5 — INVESTIGATIONS + MANAGEMENT # LEFT s+=[h1("INVESTIGATIONS — CONSTIPATION"),ss("FIRST-LINE BLOODS"), tb([["Test","What to Find","Why It Matters"], ["FBC","Hb↓ + MCV↓ = IDA","Occult CRC / IBD bleeding → colonoscopy"], ["U&E","K⁺↓ (hypokalaemia)","Hypokalaemia causes constipation; hydration status"], ["Corrected Ca²⁺","Ca²⁺ >2.6 mmol/L","Hypercalcaemia → check PTH if elevated"], ["TFTs (TSH ± free T4)","TSH↑ = hypothyroidism","Most common reversible endocrine cause"], ["Glucose / HbA1c","DM diagnosis","Diabetic autonomic neuropathy"], ["Mg²⁺","Hypomagnesaemia","Check if Ca²⁺/K⁺ also low"], ["CRP / ESR","↑ inflammation","IBD, CRC, diverticulitis, abscess"], ["CEA","↑ in CRC","Not diagnostic alone; used for post-diagnosis monitoring"]], [CW*0.22,CW*0.3,CW*0.48]),sp(3), ss("IMAGING & ENDOSCOPY"), tb([["Investigation","Indication","Findings"], ["AXR (plain film)","Suspected impaction; obstruction; acute symptoms","Faecal loading; dilated loops (3–6–9 rule); megacolon; 'coffee bean' sigmoid volvulus"], ["Contrast enema\n(Gastrografin/barium)","Suspected Hirschsprung's; stricture","Transition zone (Hirschsprung's); strictures; mucosal pattern"], ["CT abdomen/pelvis","Mass / CRC staging; complicated disease","Extent of Ca, lymph nodes, mets, pelvic anatomy"], ["CT colonography","Elderly/frail unable to tolerate scope","Same sensitivity as colonoscopy; misses flat lesions; no biopsy"], ["Colonoscopy\n(GOLD STANDARD)","Alarm features; age >50; new onset; positive FOB","Full mucosal survey; biopsy; polypectomy — mandatory with any alarm feature"], ["Flexible sigmoidoscopy","<50 yrs; no alarm features; initial workup","Views rectum to descending colon only"]], [CW*0.27,CW*0.3,CW*0.43])] # RIGHT s+=[h1("MANAGEMENT — LAXATIVE STEP LADDER"),ss("STEP 1 — LIFESTYLE (ALWAYS FIRST)"), bl("<b>Dietary fibre:</b> Increase to 25–30g/day — fruits, vegetables, wholegrains, legumes"), bl("<b>Fluid intake:</b> 1.5–2 L/day — fibre CANNOT work without adequate water"), bl("<b>Exercise:</b> Regular aerobic activity directly ↑ colonic motility via ENS stimulation"), bl("<b>Toilet routine:</b> Regular attempt after meals (gastrocolic reflex); never ignore urge"), bl("<b>Positioning:</b> Footstool (squatting) straightens anorectal angle → easier evacuation"), bl("<b>Medication review:</b> Stop/switch constipating drugs wherever clinically safe"),sp(3), ss("STEP 2 — LAXATIVES (EVIDENCE-BASED LADDER)"), tb([["Class","Drug","Mechanism","Notes"], ["Bulk-forming\n(1st line — mild)","Ispaghula husk\n(Fybogel)","↑ stool bulk → stimulates peristalsis","Must take with ≥200 mL water. 2–3 days onset. Avoid in impaction. CI in dysphagia."], ["Osmotic\n(1st line — OIC)","Macrogol (Movicol)\nLactulose\nMg hydroxide","Non-absorbable polymer draws water osmotically into bowel\nNon-absorbable disaccharide\nOsmotic","Macrogol preferred — best evidence, less bloating. Lactulose → gas/bloating. Mg²⁺ CI in renal failure."], ["Stimulant\n(add-on / short-term)","Senna\nBisacodyl","Stimulate myenteric plexus → ↑ peristalsis + ↑ secretion","Onset 6–12 hrs. Avoid long-term (theoretical atonic colon risk). Suppository for quicker effect."], ["Stool softener","Docusate sodium","Surfactant — ↓ surface tension → softer stool","Minimal evidence alone. Useful post-op or with anal fissure."], ["Rectal agents","Phosphate enema\nGlycerol suppository","Local osmotic + stimulant\nRectal lubricant + mild irritant","For acute impaction/pre-procedure. Rapid onset (minutes)."], ["Secretagogue","Linaclotide 290 μg OD\nLubiprostone 8–24 μg BD","↑ intestinal Cl⁻ via GC-C → ↑ luminal fluid\n↑ ClC-2 Cl⁻ channel","NICE approved when ≥2 laxatives failed. Linaclotide 72 μg for IBS-C."], ["Prokinetic","Prucalopride 2 mg OD\n(1 mg if >65 yrs)","Selective 5-HT₄ agonist → ↑ colonic HAPCs","For chronic constipation after ≥2 laxatives. Strong RCT evidence."], ["PAMORA\n(OIC only)","Methylnaltrexone SC\nNaloxegol 25 mg OD","Peripheral μ-opioid receptor antagonist","Specifically for opioid-induced constipation (OIC). Does NOT reverse central analgesia."]], [CW*0.2,CW*0.2,CW*0.3,CW*0.3]),sp(3), pe("LAXATIVE ORDER: Bulk-forming → Osmotic (macrogol) → Stimulant → Combine → Secretagogue/Prucalopride. For OPIOID-INDUCED: start osmotic + stimulant TOGETHER on day 1. Bulk-forming alone is inadequate for OIC.")] s.append(PageBreak()) # PAGE 6 — SPECIFIC TREATMENTS + SURGERY # LEFT s+=[h1("MANAGEMENT — SPECIFIC CAUSES"), tb([["Cause","Treatment","Key Note"], ["Hypothyroidism","Levothyroxine replacement","Constipation resolves with euthyroid state. Recheck TFTs at 6–8 weeks."], ["Hypercalcaemia","IV 0.9% NaCl + IV bisphosphonate\n(zoledronate 4 mg)","Then treat underlying cause (PTHrP, malignancy, primary hyperPTH)"], ["Hypokalaemia","IV/oral K⁺ replacement","Address underlying cause (diuretics, vomiting, Conn's)"], ["Opioid-induced (OIC)","Macrogol + senna; add methylnaltrexone SC if refractory","Do NOT stop opioid without clinical review"], ["Anal fissure","Topical GTN 0.4% or diltiazem 2% gel BD × 8 wks","Botulinum toxin if topical fails; lateral internal sphincterotomy if chronic (>3 months)"], ["Faecal impaction","Macrogol disimpaction regimen (8 sachets/day × 3 days)","Phosphate enema if rectal. Manual evacuation under sedation if severe."], ["Pelvic floor dyssynergia","Biofeedback therapy (10–15 sessions)","Gold standard — 70% response rate. Retrains paradoxical contraction."], ["Slow transit constipation","Prucalopride 2mg OD (after 2 laxatives fail)","Subtotal colectomy + IRA if ALL medical therapy exhausted"], ["Rectocoele","Pelvic floor physiotherapy first","Surgical repair (posterior colporrhaphy/transanal) if symptomatic + >3 cm"], ["IBS-C","Low-FODMAP + linaclotide 72 μg OD + antispasmodics","CBT + gut-directed hypnotherapy significantly improves outcomes"], ["CRC causing obstruction","Emergency: Hartmann's / colonic stent as bridge","Elective: anterior resection or right hemicolectomy"], ["Ogilvie's syndrome\n(acute pseudo-obstruction)","IV neostigmine 2mg over 3–5 min","Monitor HR (bradycardia risk). Colonoscopic decompression if fails."]], [CW*0.27,CW*0.38,CW*0.35]),sp(4), h4("SURGICAL OPTIONS — REFRACTORY CONSTIPATION"), bd("<b>Prerequisites (Bailey & Love):</b> Pathophysiology confirmed with specialist tests + All conservative options exhausted (≥2 laxatives + prucalopride + biofeedback) + MDT review."),sp(3), tb([["Procedure","Indication","Complications"], ["Subtotal colectomy +\nileorectal anastomosis","Refractory slow transit; all medical therapy failed","Diarrhoea 5–15%; SBO 10–20%; faecal incontinence 10–20%"], ["STARR (stapled transanal\nrectal resection)","Rectal intussusception + rectocoele causing outlet dysfunction","Urgency; fistula; haemorrhage"], ["Posterior colporrhaphy /\ntransanal rectocoele repair","Symptomatic rectocoele failing physiotherapy","Good results for isolated rectocoele"], ["Ventral rectopexy\n(laparoscopic)","Rectal prolapse / intussusception","Mesh to sacral promontory; low recurrence"], ["Sacral nerve stimulation","Pelvic floor dysfunction; refractory outlet","Neuromodulation S2–S4; reversible"], ["ACE procedure","Severe intractable; neurogenic bowel","Appendicocoecostomy; antegrade irrigation"], ["Stoma (ileostomy/colostomy)","Last resort — refractory all therapies","May be only effective solution in severe cases"]], [CW*0.3,CW*0.35,CW*0.35])] # RIGHT s+=[h4("HIRSCHSPRUNG'S DISEASE — COMPLETE"), tb([["Feature","Details"], ["Pathology","Absent ganglion cells (Meissner's submucosal + Auerbach's myenteric plexus) in distal colorectum → tonic contraction → functional obstruction"], ["Cause","Failed neural crest cell migration (weeks 5–12 of embryogenesis). RET proto-oncogene mutation in 50%."], ["Segment length","Short segment 75% (rectosigmoid); Long segment 20%; Total colonic 5%"], ["Neonatal presentation","Failure to pass meconium within 48 hrs; abdominal distension; bilious vomiting"], ["Childhood presentation","Chronic constipation since birth; ribbon stools; growth retardation; never a normal bowel habit"], ["Life-threatening complication","HAEC (Hirschsprung-associated enterocolitis): fever + explosive diarrhoea + septic shock"], ["Gold standard diagnosis","Suction rectal biopsy — absent ganglion cells + ↑ AChE staining"], ["Contrast enema finding","Transition zone: narrow aganglionic segment → dilated proximal (cone-shaped transition)"], ["Manometry finding","Absent RAIR (recto-anal inhibitory reflex) — pathognomonic"], ["Treatment","Pull-through procedure (Swenson / Soave / Duhamel); colostomy if acute toxic megacolon"]], [CW*0.27,CW*0.73]),sp(4), h2("SPECIAL POPULATIONS"), ss("CONSTIPATION IN THE ELDERLY"), bl("Most common: polypharmacy (opioids, CCBs, anticholinergics) + immobility + dehydration + low fibre"), bl("<b>Faecal impaction → overflow diarrhoea:</b> liquid stool bypasses impaction — DO NOT give antidiarrhoeals"), bl("PR examination essential — confirm impaction; exclude rectal Ca"), bl("Macrogol disimpaction ± phosphate enema ± manual evacuation under sedation if severe"),sp(3), ss("CONSTIPATION IN PREGNANCY"), bl("Mechanism: progesterone ↓ gut smooth muscle + iron supplements + mechanical uterine compression"), bl("<b>Safe:</b> Ispaghula husk (1st line), lactulose, macrogol (short-term)"), bl("<b>Avoid:</b> Senna (1st trimester), castor oil (induces labour), mineral oils"),sp(3), ss("CONSTIPATION IN PARKINSON'S DISEASE"), bl("Mechanism: dopaminergic neuronal loss → ↓ enteric nervous system → ↓ peristalsis"), bl("Macrogol 1st line; consider prucalopride; review anti-Parkinson drugs (anticholinergics)"), bl("May precede motor symptoms by years — early GI manifestation of Parkinson's")] s.append(PageBreak()) # PAGE 7 — COMPLICATIONS · OSCE Q&A · MNEMONICS # LEFT s+=[h3("COMPLICATIONS OF CHRONIC CONSTIPATION"), tb([["Complication","Mechanism","Features"], ["Haemorrhoids","↑ venous pressure from chronic straining","Bright red bleeding; prolapse; mucus discharge; pruritus ani"], ["Anal fissure","Hard stool tears posterior midline anoderm","Severe pain on defaecation; bright red blood; sentinel skin tag"], ["Rectal prolapse","Chronic straining → stretched ligaments + pudendal neuropathy","Full-thickness mucosa protrudes; incontinence; mucus discharge"], ["Rectocoele","Anterior rectal wall herniates into vaginal wall","Need vaginal digitation to defaecate; confirmed on proctogram"], ["Faecal impaction","Chronic constipation + dehydration + immobility","Overflow diarrhoea; urinary retention; AKI in elderly"], ["Stercoral ulcer / perforation","Pressure necrosis of colon wall by impacted faeces","Antemesenteric wall; perforation → peritonitis (high mortality)"], ["Megacolon / volvulus","Chronic dilatation; Hirschsprung's; pseudo-obstruction","Sigmoid volvulus: 'coffee bean' on AXR; caecal volvulus"], ["Urinary retention","Faecal impaction compresses bladder/urethra","Common in elderly; disimpaction resolves urinary symptoms"], ["Stercoral perforation","Hard faecal mass erodes bowel wall (antemesenteric)","Presents as peritonitis — often missed; high mortality"]], [CW*0.25,CW*0.37,CW*0.38]),sp(4), h1("IBS-C vs FUNCTIONAL CONSTIPATION"), tb([["Feature","IBS-C","Functional Constipation"], ["Dominant symptom","Abdominal PAIN linked to defaecation","Defaecatory difficulty; bloating"], ["Investigations","All NORMAL","Normal after secondary causes excluded"], ["Treatment","Low-FODMAP, linaclotide 72μg, antispasmodics, CBT","Laxative ladder; prucalopride; biofeedback"], ["Pathophysiology","Visceral hypersensitivity + gut-brain axis","↓ colonic motility / outlet dysfunction"]], [CW*0.25,CW*0.375,CW*0.375]),sp(3), pe("IBS is a DIAGNOSIS OF EXCLUSION. Never diagnose IBS if: age >50 with new symptoms, alarm features, nocturnal symptoms, anaemia, weight loss, positive FOB, family Hx CRC.")] # RIGHT s+=[h1("OSCE VIVA — RAPID FIRE Q&A"), tb([["Examiner Asks...","Your Answer"], ["Define constipation (Rome IV)","≥2 of: straining, Bristol 1–2, incomplete evacuation, anorectal blockage, manual manoeuvres — in >25% defaecations; <3 BM/week; ≥3 months; onset ≥6 months"], ["Most common drug cause?","Opioids — μ-receptor → ↓ peristalsis + ↑ IAS tone. Also iron, Al antacids, TCAs, verapamil, ondansetron, clozapine"], ["Metabolic causes — 4 to exclude?","Hypothyroidism (TSH), hypercalcaemia (Ca²⁺), hypokalaemia (K⁺), diabetes (autonomic neuropathy)"], ["Empty rectum on DRE?","Slow transit — stool not reaching rectum. Investigate with Sitzmarks transit study"], ["Full rectum on DRE?","Outlet dysfunction OR faecal impaction. Disimpact FIRST before laxatives"], ["What is overflow diarrhoea?","Liquid stool bypasses faecal impaction. NEVER give antidiarrhoeals — disimpact first"], ["Balloon expulsion test — normal?","Expelled in <1 min. Prolonged = pelvic floor dyssynergia"], ["Absent RAIR means?","Pathognomonic of Hirschsprung's disease — confirm with suction rectal biopsy"], ["How to confirm Hirschsprung's?","Suction rectal biopsy — absent ganglion cells + ↑ AChE staining. Also absent RAIR on manometry"], ["Best treatment for dyssynergia?","Biofeedback therapy — 70% response; retrains paradoxical puborectalis contraction"], ["Prucalopride mechanism?","Selective 5-HT₄ agonist → ↑ colonic HAPCs (high-amplitude propagating contractions)"], ["OIC treatment?","Osmotic + stimulant from day 1 of opioid; add methylnaltrexone SC (PAMORA) if refractory"], ["Safe laxative in pregnancy?","Ispaghula husk (1st line) or lactulose. Avoid stimulants (senna) in 1st trimester"], ["Surgery for slow transit?","Subtotal colectomy + IRA — ONLY after all medical therapy failed + MDT review"], ["Stercoral perforation?","Pressure necrosis of antemesenteric colon wall by impacted faeces → peritonitis"], ["Ogilvie's syndrome treatment?","IV neostigmine 2mg IV over 3–5 min (monitor HR); colonoscopic decompression; caecostomy last resort"]], [CW*0.42,CW*0.58]),sp(4), gt("FINAL OSCE TIP — Always: 1) Drug history (opioids? iron? verapamil?), 2) Alarm features screen, 3) Bloods: TFTs + Ca²⁺ + FBC + K⁺, 4) PR examination — NEVER omit, 5) Colonoscopy if ANY alarm feature present.")] doc.build(s,canvasmaker=Cv) print("Built:",out) build() ENDSCRIPT echo "Script written. Running..." python3 /home/daytona/workspace/osce-booklets/build_const2.py 2>&1

Do

Running Command

cat > /home/daytona/workspace/osce-booklets/build_diarr2.py << 'ENDSCRIPT' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, HRFlowable, NextPageTemplate, PageBreak) from reportlab.lib.styles import ParagraphStyle from reportlab.lib.enums import TA_CENTER from reportlab.pdfgen import canvas as pdfgen_canvas NAVY=colors.HexColor('#1a2a4a'); GOLD=colors.HexColor('#c8a951') DB=colors.HexColor('#1a4971'); TL=colors.HexColor('#2a7a6b') RD=colors.HexColor('#c0392b'); RDBG=colors.HexColor('#fdecea') GN=colors.HexColor('#2e7d32'); GNBG=colors.HexColor('#e8f5e9') AM=colors.HexColor('#f57f17'); AMBG=colors.HexColor('#fff8e1') PR=colors.HexColor('#6a1b9a'); PRBG=colors.HexColor('#f3e5f5') W=colors.white; BK=colors.black GL=colors.HexColor('#f2f4f7'); GM=colors.HexColor('#d5dce8') PW,PH=A4; ML=13*mm; MR=13*mm; HH=18*mm; FH=10*mm UW=PW-ML-MR; CG=6*mm; CW=(UW-CG)/2 FTR='Sources: Bailey & Love 28e · Sleisenger & Fordtran · Harrison\'s 22e · Yamada\'s Gastroenterology · Goldman-Cecil' def mk(): d={} d['TM']=ParagraphStyle('TM',fontName='Helvetica-Bold',fontSize=18,textColor=W,leading=22,alignment=TA_CENTER) d['TS']=ParagraphStyle('TS',fontName='Helvetica-BoldOblique',fontSize=10,textColor=GOLD,leading=13,alignment=TA_CENTER) d['TT']=ParagraphStyle('TT',fontName='Helvetica',fontSize=8,textColor=colors.HexColor('#a0bcd8'),leading=11,alignment=TA_CENTER) d['H1']=ParagraphStyle('H1',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=DB,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H2']=ParagraphStyle('H2',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=TL,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H3']=ParagraphStyle('H3',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=RD,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H4']=ParagraphStyle('H4',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=PR,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['SS']=ParagraphStyle('SS',fontName='Helvetica-Bold',fontSize=8.5,textColor=DB,leading=11,spaceAfter=3,spaceBefore=5) d['BD']=ParagraphStyle('BD',fontName='Helvetica',fontSize=7.8,textColor=BK,leading=11,spaceAfter=2) d['BL']=ParagraphStyle('BL',fontName='Helvetica',fontSize=7.8,textColor=BK,leading=11,leftIndent=10,bulletIndent=2,spaceAfter=1.5) d['BB']=ParagraphStyle('BB',fontName='Helvetica-Bold',fontSize=7.8,textColor=DB,leading=11,leftIndent=10,bulletIndent=2,spaceAfter=1.5) d['PE']=ParagraphStyle('PE',fontName='Helvetica-BoldOblique',fontSize=7.8,textColor=colors.HexColor('#7b3f00'),backColor=AMBG,borderColor=AM,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['AL']=ParagraphStyle('AL',fontName='Helvetica-Bold',fontSize=7.8,textColor=RD,backColor=RDBG,borderColor=RD,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['GT']=ParagraphStyle('GT',fontName='Helvetica',fontSize=7.8,textColor=colors.HexColor('#1b5e20'),backColor=GNBG,borderColor=GN,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['PB']=ParagraphStyle('PB',fontName='Helvetica-BoldOblique',fontSize=7.8,textColor=PR,backColor=PRBG,borderColor=PR,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['TH']=ParagraphStyle('TH',fontName='Helvetica-Bold',fontSize=7.5,textColor=W,leading=10,alignment=TA_CENTER) d['TC']=ParagraphStyle('TC',fontName='Helvetica',fontSize=7.5,textColor=BK,leading=10) d['FO']=ParagraphStyle('FO',fontName='Helvetica-Oblique',fontSize=6.5,textColor=colors.HexColor('#aaaaaa'),leading=9,alignment=TA_CENTER) return d ST=mk() class Cv(pdfgen_canvas.Canvas): def __init__(self,f,**kw): super().__init__(f,**kw); self._st=[]; self._p=0 def showPage(self): self._st.append(dict(self.__dict__)); self._startPage() def save(self): for x in self._st: self.__dict__.update(x); self._d(); super().showPage() super().save() def _d(self): self._p+=1; c=self c.setFillColor(NAVY); c.rect(0,PH-HH,PW,HH,fill=1,stroke=0) c.setFillColor(GOLD); c.rect(0,PH-HH-1.5,PW,1.5,fill=1,stroke=0) c.setFillColor(GOLD); c.setFont('Helvetica-Bold',8); c.drawString(ML,PH-10*mm,f"Page {self._p}") c.setFillColor(W); c.setFont('Helvetica-Bold',12); c.drawCentredString(PW/2,PH-8.5*mm,"DIARRHOEA") c.setFont('Helvetica',7.5); c.setFillColor(colors.HexColor('#a0bcd8')) c.drawCentredString(PW/2,PH-13.5*mm,"Clinical OSCE Notes | Final Year MBBS") c.setFillColor(GOLD); c.setFont('Helvetica-Bold',7.5) c.drawString(ML,PH-HH-8,"◀ LEFT"); c.drawRightString(PW-MR,PH-HH-8,"RIGHT ▶") c.setFillColor(NAVY); c.rect(0,0,PW,FH+3,fill=1,stroke=0) c.setFillColor(GOLD); c.rect(0,FH+3,PW,1,fill=1,stroke=0) c.setFillColor(colors.HexColor('#aaaaaa')); c.setFont('Helvetica-Oblique',6) c.drawCentredString(PW/2,4*mm,FTR) if self._p>1: c.setStrokeColor(GM); c.setLineWidth(0.5) c.line(ML+CW+CG/2,PH-HH-12,ML+CW+CG/2,FH+6) def h1(t): return Paragraph(f"<b>{t}</b>",ST['H1']) def h2(t): return Paragraph(f"<b>{t}</b>",ST['H2']) def h3(t): return Paragraph(f"<b>{t}</b>",ST['H3']) def h4(t): return Paragraph(f"<b>{t}</b>",ST['H4']) def ss(t): return Paragraph(t,ST['SS']) def bd(t): return Paragraph(t,ST['BD']) def bl(t): return Paragraph(f"• {t}",ST['BL']) def bb(t): return Paragraph(f"• {t}",ST['BB']) def pe(t): return Paragraph(f"★ OSCE PEARL: {t}",ST['PE']) def al(t): return Paragraph(f"⚠ RED FLAG: {t}",ST['AL']) def gt(t): return Paragraph(t,ST['GT']) def pb(t): return Paragraph(f"🔬 PATHOPHYSIOLOGY: {t}",ST['PB']) def sp(h=3): return Spacer(1,h) def tb(rows,ws,bg=None): if bg is None: bg=DB ts=TableStyle([('BACKGROUND',(0,0),(-1,0),bg),('TEXTCOLOR',(0,0),(-1,0),W), ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'),('FONTSIZE',(0,0),(-1,-1),7.5), ('ROWBACKGROUNDS',(0,1),(-1,-1),[GL,W]),('GRID',(0,0),(-1,-1),0.4,GM), ('LEFTPADDING',(0,0),(-1,-1),4),('RIGHTPADDING',(0,0),(-1,-1),4), ('TOPPADDING',(0,0),(-1,-1),3),('BOTTOMPADDING',(0,0),(-1,-1),3), ('VALIGN',(0,0),(-1,-1),'TOP')]) wr=[] for ri,row in enumerate(rows): wr.append([Paragraph(c,ST['TH'] if ri==0 else ST['TC']) if isinstance(c,str) else c for c in row]) return Table(wr,colWidths=ws,style=ts,hAlign='LEFT') def build(): out='/home/daytona/workspace/osce-booklets/Diarrhoea_OSCE_Booklet_v2.pdf' by=FH+8; ty=PH-HH-14; fh=ty-by cf=Frame(ML,by,UW,fh,leftPadding=0,rightPadding=0,topPadding=0,bottomPadding=0) lf=Frame(ML,by,CW,fh,leftPadding=0,rightPadding=3,topPadding=0,bottomPadding=0) rff=Frame(ML+CW+CG,by,CW,fh,leftPadding=3,rightPadding=0,topPadding=0,bottomPadding=0) doc=BaseDocTemplate(out,pagesize=A4,leftMargin=ML,rightMargin=MR,topMargin=22*mm,bottomMargin=16*mm) doc.addPageTemplates([PageTemplate(id='cover',frames=[cf]),PageTemplate(id='two',frames=[lf,rff])]) s=[] # COVER s+=[NextPageTemplate('cover'),sp(26), Paragraph("DIARRHOEA",ST['TM']),sp(4), Paragraph("Clinical OSCE Notes | Final Year MBBS",ST['TS']),sp(6), Paragraph("Definition · Classification · Pathophysiology · Causes · History Taking<br/>Examination · Investigations · Management · C. difficile · IBD · HUS · OSCE Q&amp;A",ST['TT']), sp(7),HRFlowable(width="80%",thickness=1.5,color=GOLD,spaceAfter=8,hAlign='CENTER'), Paragraph(FTR,ST['FO']),PageBreak()] # PAGE 2 — DEFINITION / CLASSIFICATION / PATHOPHYSIOLOGY / CAUSES s.append(NextPageTemplate('two')) # LEFT s+=[h1("DEFINITION & CLASSIFICATION"), bd("<b>Definition:</b> Passage of ≥3 loose/watery stools per day, stool volume >200 mL/day, or stool weight >200 g/day. " "Most patients consider increased fluidity of stool to be the essential characteristic. — Sleisenger & Fordtran"),sp(3), bd("<b>Epidemiology:</b> Acute gastroenteritis affects 7.5% of Americans monthly. Chronic diarrhoea (≥4 weeks) " "affects 6.6% of population per year. >180,000 hospitalisations and 3,000 deaths/year in USA from gastroenteritis. " "Most common cause of preventable childhood mortality in developing countries. — Sleisenger & Fordtran"),sp(4), h1("CLASSIFICATION BY DURATION"), tb([["Type","Duration","Most Common Causes"], ["Acute","< 2 weeks","Infective gastroenteritis (viral/bacterial), food poisoning, traveller's diarrhoea, antibiotic-associated"], ["Persistent","2–4 weeks","Continuing infection (Giardia, C. diff), early IBD, tropical infection, post-infective IBS"], ["Chronic","> 4 weeks","IBD, IBS-D, malabsorption (coeliac, chronic pancreatitis), colorectal Ca, endocrine causes"]], [CW*0.18,CW*0.18,CW*0.64]),sp(4), h1("PATHOPHYSIOLOGY — 'SOME' MNEMONIC"), pb("All diarrhoea results from an imbalance between intestinal secretion and absorption. Normal net absorption = 8–9 L/day. " "Diarrhoea occurs when delivery to colon exceeds colonic absorptive capacity (~4 L/day). " "Mechanisms: Secretory, Osmotic, Motility, Exudative/Inflammatory. — Harrison's 22e"),sp(3), tb([["Mechanism","Pathophysiology","Key Features"], ["S — Secretory","Active Cl⁻/Na⁺ secretion into lumen via ↑ cAMP/cGMP/Ca²⁺. Toxin-mediated (cholera toxin activates adenylate cyclase → ↑↑ cAMP → ↑ CFTR Cl⁻ secretion).","Large volume watery stools. CONTINUES on fasting. Stool osmotic gap <50 mOsm/kg. Painless."], ["O — Osmotic","Unabsorbed solutes (lactose, Mg²⁺, PEG) draw water osmotically into lumen. Stool osmolality explained by electrolytes + unabsorbed solute.","STOPS on fasting. Bloating/gas. Stool osmotic gap >125 mOsm/kg. Improves with dietary exclusion."], ["M — Motility","↑ transit → inadequate absorption time. OR ↓ transit → bacterial overgrowth → secondary malabsorption.","Variable volume. IBS-D, hyperthyroidism, post-vagotomy. No structural lesion."], ["E — Exudative /\nInflammatory","Mucosal damage → protein/blood/pus exudation into lumen. Inflammation disrupts tight junctions → ↑ permeability.","SMALL volume + blood + pus + mucus. Urgency + tenesmus. Bowel-wall thickening on imaging."]], [CW*0.17,CW*0.43,CW*0.40],TL)] # RIGHT s+=[h1("STOOL OSMOTIC GAP — DISTINGUISH SECRETORY vs OSMOTIC"), bd("<b>Formula:</b> Stool osmotic gap = 290 − 2 × (stool Na⁺ + stool K⁺)"),sp(2), tb([["Gap (mOsm/kg)","Interpretation","Examples"], ["< 50","Secretory — electrolytes drive osmolality","Cholera, VIPoma, carcinoid, bile salt malabsorption"], ["50–125","Mixed / indeterminate","Some IBD; complex diarrhoea"], ["> 125","Osmotic — unabsorbed solutes drive osmolality","Lactose intolerance, coeliac, laxative abuse, Giardia"]], [CW*0.22,CW*0.42,CW*0.36]),sp(3), pe("Secretory diarrhoea: large volume, painless, CONTINUES on fasting. Osmotic diarrhoea: STOPS on fasting, worse after eating offending food. KEY distinction for both OSCE viva and clinical practice."),sp(4), h1("LARGE-VOLUME vs SMALL-VOLUME DIARRHOEA"), tb([["Type","Source","Characteristics","Think of..."], ["Large volume\n(>200 mL/episode)","Small bowel /\nproximal colon","Watery; no blood; no tenesmus; dehydration dominant","Cholera, ETEC, VIPoma, carcinoid, coeliac, Giardia"], ["Small volume\n(<200 mL/episode)","Distal colon /\nrectum","Blood + mucus + pus; urgency; tenesmus; lower abdominal pain","UC, Crohn's colitis, Shigella, C. diff, rectal Ca, proctitis"]], [CW*0.18,CW*0.18,CW*0.32,CW*0.32]),sp(4), h1("ACUTE vs CHRONIC DIARRHOEA — KEY DIFFERENCES"), tb([["Feature","Acute (<2 weeks)","Chronic (>4 weeks)"], ["Most common cause","Infective (viral > bacterial)","IBD, IBS-D, malabsorption"], ["Blood in stool","Shigella, EHEC, C. diff, ischaemic colitis","IBD, CRC, ischaemic colitis"], ["Priority investigation","Stool MC&S + C. diff toxin EIA","Colonoscopy + biopsy + faecal calprotectin"], ["Nocturnal symptoms","Yes (infective/inflammatory)","If yes = organic (IBD, secretory) — NOT IBS"], ["Management priority","Rehydration (ORS/IV) first","Diagnose underlying cause; treat specifically"]], [CW*0.22,CW*0.39,CW*0.39]),sp(3), pe("Nocturnal diarrhoea = ORGANIC cause (IBD, secretory tumour, microscopic colitis). IBS NEVER causes nocturnal diarrhoea — this is a gold-standard OSCE discriminator. — Harrison's 22e")] s.append(PageBreak()) # PAGE 3 — INFECTIVE CAUSES # LEFT s+=[h1("CAUSES — INFECTIVE (VIRAL)"), tb([["Organism","Clinical Features","Management"], ["Norovirus","Winter outbreaks; vomiting > diarrhoea; highly contagious (18 viral particles); 24–72 hrs. Hospital ward closures. Vomiting + watery diarrhoea.","Supportive ORS; isolate (contact precautions). Alcohol gel INEFFECTIVE — soap + water. Notify outbreak team."], ["Rotavirus","Children <5 yrs; winter; severe dehydration risk; leading cause of childhood diarrhoea mortality worldwide. Watery diarrhoea + vomiting.","ORS; IV fluids if severe. Rotavirus vaccine (Rotarix) prevents severe disease."], ["Adenovirus","Children; concurrent respiratory symptoms + diarrhoea. Longer duration (7–14 days) than other viral GE.","Supportive; self-limiting."], ["CMV","Immunocompromised (HIV CD4 <50, transplant); bloody diarrhoea; CMV colitis on scope (submucosal haemorrhage, 'owl-eye' inclusion bodies on biopsy).","IV ganciclovir / oral valganciclovir. Monitor renal function."], ["Hepatitis A / E","Faeco-oral; travellers; shellfish. Diarrhoea as prodrome before jaundice. Self-limiting.","Supportive; HAV vaccine for prevention."]], [CW*0.22,CW*0.46,CW*0.32]),sp(4), h1("CAUSES — INFECTIVE (BACTERIAL)"), tb([["Organism","Key Clinical Clue","Treatment"], ["Campylobacter jejuni","Most common bacterial cause in UK. Undercooked poultry. Prodrome of fever + rigors + myalgia → watery then bloody diarrhoea. RIF pain mimicking appendicitis.","Usually self-limiting 3–7 days. Azithromycin 500mg OD × 3d if severe/immunocomp. ↑ fluoroquinolone resistance."], ["Salmonella non-typhi","Eggs/poultry/reptiles. Watery then bloody diarrhoea. Fever. Bacteraemia risk in elderly/immunocomp/sickle cell.","Supportive. Ciprofloxacin 500mg BD × 5–7d ONLY if bacteraemic, immunocomp, or severe."], ["Shigella (dysentery)","Dysentery — small-volume bloody + mucoid + tenesmus + high fever. Very low inoculum (10–100 organisms). Person-to-person.","Ciprofloxacin 500mg BD × 3d OR azithromycin. Shorten illness significantly."], ["E. coli ETEC","Traveller's diarrhoea. Watery, non-bloody. Tropical countries. Most common bacterial cause of traveller's diarrhoea.","ORS. Azithromycin 500mg OD × 3d if severe. Bismuth subsalicylate for prophylaxis."], ["E. coli O157 (EHEC)","Bloody diarrhoea + colicky abdominal pain. HUS risk (haemolytic uraemia + ↓plt + AKI) 5–10 days later. Children. Undercooked beef.","SUPPORTIVE ONLY. NO antibiotics (↑ Shiga toxin release → ↑ HUS risk). Monitor FBC + renal function daily."], ["C. difficile","Post-antibiotics (clindamycin > amoxicillin > cephalosporins). Profuse watery/mucoid diarrhoea. Hospital-acquired. Elderly. Pseudomembranes on colonoscopy.","Oral vancomycin 125mg QDS × 10 days (NICE 2021 — NOT metronidazole monotherapy). Fidaxomicin for recurrence."], ["Vibrio cholerae","Rice-water stools. Massive secretory diarrhoea (up to 20 L/day). Life-threatening dehydration. Developing world outbreaks.","ORS (WHO formula). Doxycycline 300mg single dose. Aggressive IV rehydration if severe."], ["Salmonella typhi\n(Typhoid fever)","Sustained fever + relative bradycardia + rose spots (trunk) + splenomegaly + constipation THEN diarrhoea (pea soup stools). Step-ladder fever.","IV ceftriaxone 2g OD × 10–14d. OR azithromycin 1g OD × 5d. Notify PHE. Contact tracing."], ["Yersinia enterocolitica","RIF pain mimicking appendicitis (mesenteric adenitis). Cold climates (Scandinavia). Can cause pseudoappendicitis.","Usually self-limiting. Ciprofloxacin/co-trimoxazole if severe/immunocomp."], ["Staphylococcus aureus","Food poisoning. Preformed heat-stable toxin. Rapid onset 1–6 hrs after ingestion. Vomiting > diarrhoea. Buffet food.","Supportive; self-limiting 24 hrs. No antibiotics."], ["Bacillus cereus","Two syndromes: emetic (1–6 hrs — fried rice) and diarrhoeal (6–24 hrs). Preformed toxin.","Supportive; self-limiting."]], [CW*0.22,CW*0.46,CW*0.32])] # RIGHT s+=[h1("CAUSES — INFECTIVE (PROTOZOAL)"), tb([["Organism","Key Features","Treatment"], ["Giardia lamblia","Steatorrhoea + bloating + flatulence + malabsorption. Contaminated water/travel. Prolonged (weeks–months). No blood. Duodenal trophozoites on OGD biopsy.","Metronidazole 400mg TDS × 5–7 days. OR tinidazole 2g single dose. Test contacts."], ["Entamoeba histolytica","Amoebic dysentery — bloody + mucus. Amoebic liver abscess (RUQ pain + fever + ↑ ALP). Developing world / travellers.","Metronidazole 800mg TDS × 5d (tissue) + diloxanide furoate 500mg TDS × 10d (luminal cysts)."], ["Cryptosporidium parvum","Immunocompromised (HIV CD4 <200): chronic, life-threatening. Immunocompetent: self-limiting watery diarrhoea (2 weeks). Contaminated water.","Nitazoxanide 500mg BD × 3d. In HIV: optimise ART (immune reconstitution resolves diarrhoea)."], ["Cyclospora cayetanensis","Prolonged watery diarrhoea; relapsing course. Travellers. Contaminated fruit/water.","Co-trimoxazole (trimethoprim-sulfamethoxazole) × 7–10 days."], ["Microsporidium","HIV with CD4 <100. Chronic profuse watery diarrhoea + malabsorption.","Albendazole + ART optimisation."]], [CW*0.25,CW*0.43,CW*0.32]),sp(4), h1("CAUSES — NON-INFECTIVE"), tb([["Category","Condition","Key Feature"], ["IBD","Ulcerative Colitis","Bloody mucoid diarrhoea + urgency + tenesmus. Continuous from rectum. Always involves rectum."], ["IBD","Crohn's Disease","Variable — watery or bloody; skip lesions; perianal disease; malabsorption if small bowel."], ["Malabsorption","Coeliac Disease","Steatorrhoea + weight loss + bloating + IDA. Anti-tTG IgA. Villous atrophy on duodenal biopsy."], ["Malabsorption","Chronic Pancreatitis","Steatorrhoea + ↓ faecal elastase. Epigastric pain. Alcohol Hx. DM."], ["Malabsorption","Lactose Intolerance","Osmotic diarrhoea + bloating after dairy. ↓ lactase. Hydrogen breath test positive."], ["Malabsorption","Short Bowel Syndrome","Post-resection (>100 cm small bowel). High-volume secretory/osmotic diarrhoea."], ["Drugs","Antibiotics, metformin, laxatives, orlistat, PPIs, NSAIDs, colchicine, SSRIs, chemotherapy","Drug Hx essential in ALL chronic diarrhoea. Metformin affects 30%. Orlistat → steatorrhoea."], ["Endocrine","Hyperthyroidism","Diarrhoea + palpitations + weight loss + tremor. Check TFTs."], ["Endocrine","Carcinoid Syndrome","Diarrhoea + flushing + wheeze + right heart disease. ↑ 24-hr urinary 5-HIAA."], ["Endocrine","VIPoma (WDHA)","Massive watery diarrhoea + hypokalaemia + achlorhydria. ↑ serum VIP."], ["Endocrine","Zollinger-Ellison","Diarrhoea + recurrent peptic ulcers + diarrhoea. ↑ fasting gastrin. MEN-1."], ["Endocrine","Addison's Disease","Secretory diarrhoea + hypotension + hyperpigmentation + ↓Na⁺ + ↑K⁺."], ["Colorectal","CRC (right-sided)","Altered bowel habit + IDA + occult blood. Caecal Ca → watery diarrhoea + anaemia."], ["Colorectal","Ischaemic Colitis","Sudden bloody diarrhoea in elderly / vascular disease. Watershed zones (splenic flexure / sigmoid)."], ["Microscopic Colitis","Collagenous / Lymphocytic","Chronic watery non-bloody secretory diarrhoea in middle-aged women. Normal endoscopy — biopsy diagnosis. NSAIDs/PPIs."], ["Functional","IBS-D","Rome IV: abdominal pain + ≥3 BM/day + loose stools. NO nocturnal symptoms. Normal investigations."], ["Post-surgical","Post-cholecystectomy","Bile salt malabsorption → secretory diarrhoea. SeHCAT test confirms. Cholestyramine treats."]], [CW*0.2,CW*0.24,CW*0.56])] s.append(PageBreak()) # PAGE 4 — HISTORY TAKING # LEFT s+=[h1("HISTORY TAKING — DIARRHOEA"), bd("<b>Opening:</b> 'Can you tell me about your bowel habit? How many times a day? How long has this been going on?'"),sp(3), ss("1. CHARACTER OF DIARRHOEA"), tb([["Question","Clinical Meaning","Implication"], ["Onset + duration (acute/chronic)?","Acute <2 wks = infective; Chronic >4 wks = structural/functional","Key discriminator for workup"], ["Frequency — per day?",">3/day = diarrhoea. Nocturnal = ORGANIC","IBS NEVER nocturnal"], ["Volume — large or small?","Large = secretory/proximal; small = distal/inflammatory","Directs to mechanism"], ["Colour?","Pale/greasy = steatorrhoea; green = bile; black = UGI source","Steatorrhoea → malabsorption screen"], ["Blood in stool?","Bright red = lower GI; mixed = Ca/IBD; mucus = UC/Ca","ALARM if new onset >50 yrs"], ["Watery vs mucoid?","Watery = secretory/infective; mucoid + blood = inflammatory","Inflammatory pattern → colonoscopy"], ["Urgency / incontinence?","Rectal inflammation (UC, rectal Ca) → urgency + tenesmus","Sphincter damage → incontinence"], ["Nocturnal diarrhoea?","Organic (IBD, secretory, CRC). IBS NEVER nocturnal — diagnostic gold standard","Always ask at OSCE"], ["Steatorrhoea (oily, floats, difficult to flush)?","Malabsorption — coeliac, chronic pancreatitis, Giardia","→ Anti-tTG, faecal elastase, OGD + biopsy"], ["Fasting test?","Secretory: CONTINUES on fasting; Osmotic: STOPS","Differentiates mechanism"]], [CW*0.3,CW*0.38,CW*0.32]),sp(3), ss("2. ASSOCIATED SYMPTOMS"), tb([["Symptom Cluster","Diagnosis"], ["Abdominal pain + blood + mucus + urgency + tenesmus","UC — continuous from rectum"], ["RIF pain + skip lesions + systemic sx + perianal disease","Crohn's disease"], ["Fever + travel Hx + bloody stool","Dysentery (Shigella/amoebiasis) / typhoid"], ["Weight loss + night sweats + diarrhoea","IBD, TB, HIV, lymphoma, CRC"], ["Steatorrhoea + bloating + weight loss + IDA","Coeliac, chronic pancreatitis, Giardia"], ["Palpitations + sweating + tremor + weight loss","Hyperthyroidism → check TFTs"], ["Flushing + wheeze + watery diarrhoea","Carcinoid syndrome → 24-hr urinary 5-HIAA"], ["Post-antibiotic course (esp. clindamycin)","C. difficile — check toxin EIA"], ["Arthralgia + skin rash (pyoderma / EN)","IBD extra-intestinal manifestations"], ["Massive watery diarrhoea + hypokalaemia + weakness","VIPoma (WDHA syndrome)"]], [CW*0.5,CW*0.5])] # RIGHT s+=[al("ALARM FEATURES → urgent 2WW referral: Rectal bleeding, unintentional weight loss, IDA, palpable abdominal/rectal mass, age >50 with new bowel habit change, positive faecal occult blood."),sp(4), ss("3. DRUG HISTORY"), tb([["Drug","Type of Diarrhoea","Mechanism"], ["Antibiotics (clindamycin, amoxicillin, cephalosporins)","Infective / dysbiotic","Disrupts gut flora → C. diff overgrowth; most common iatrogenic cause"], ["Metformin","Osmotic/motility","GI irritation; dose-dependent; affects 30% patients"], ["Laxative abuse (osmotic)","Osmotic (factitious)","Stool osmotic gap >125; Mg²⁺ enemas; self-perpetuating"], ["NSAIDs / aspirin","Inflammatory","Mucosal inflammation; can cause microscopic colitis"], ["Orlistat","Steatorrhoeal","Inhibits pancreatic lipase → fat malabsorption by design"], ["SSRIs (sertraline)","Motility","↑ gut serotonin → ↑ motility"], ["PPIs (omeprazole)","Infective","↑ risk of C. diff; can cause diarrhoea directly"], ["Colchicine / Mg antacids","Osmotic","Osmotic mechanism; dose-dependent"], ["Olmesartan (ARB)","Sprue-like","Olmesartan-associated enteropathy — villous atrophy; resolves on stopping"], ["Chemotherapy / radiotherapy","Inflammatory","Mucosal damage; radiation proctitis"]], [CW*0.32,CW*0.22,CW*0.46]),sp(3), ss("4. SOCIAL & TRAVEL HISTORY"), bl("<b>Recent travel:</b> South Asia, Africa, SE Asia → ETEC, Giardia, amoebiasis, typhoid, cholera"), bl("<b>Food history:</b> Undercooked chicken → Campylobacter/Salmonella; shellfish → Norovirus/HAV; buffet → Staph toxin"), bl("<b>Water source:</b> Untreated well water → Giardia / Cryptosporidium / Cyclospora"), bl("<b>Contact illness:</b> Same-household cases → infective; notify PHE if notifiable"), bl("<b>Sexual history:</b> MSM → Shigella, LGV proctitis, Herpes, gonorrhoea rectal"), bl("<b>Immunosuppression:</b> HIV, transplant, steroids → Cryptosporidium, CMV, Microsporidium, MAI"), bl("<b>Occupation:</b> Food handler / HCW → notify PHE if notifiable organism"),sp(3), ss("5. PAST MEDICAL & FAMILY HISTORY"), bl("Known IBD → relapse? Change in symptoms? Compliance with medications?"), bl("Previous GI surgery → post-cholecystectomy BAM, short bowel syndrome, dumping syndrome"), bl("DM → autonomic neuropathy; coeliac → 10% 1st-degree relatives"), bl("Previous pancreatitis → exocrine insufficiency → steatorrhoea"), bl("Family Hx CRC / IBD → ↑ personal risk; genetic counselling")] s.append(PageBreak()) # PAGE 5 — EXAMINATION + INVESTIGATIONS # LEFT s+=[h2("CLINICAL EXAMINATION — DIARRHOEA"),ss("A. DEHYDRATION ASSESSMENT — DO FIRST"), bd("<b>Assess: pulse, BP, capillary refill, skin turgor, mucous membranes, urine output, sunken eyes</b>"),sp(2), tb([["Grade","Signs","Management"], ["None (0%)","Normal vitals; moist mucosa; normal skin turgor; normal UO","Oral fluids — maintain intake; ORS if at risk"], ["Mild (3–5%)","Thirst; dry mouth; ↓ UO; slightly ↓ skin turgor; tachycardia mildly","ORS 50 mL/kg over 4 hrs"], ["Moderate (5–8%)","Sunken eyes; poor skin turgor (>2 sec); HR >100; dry mucosa; oliguria; ↓ BP postural","ORS or IV Hartmann's 0.9% NaCl"], ["Severe (>8%)","Sunken eyes; mottled skin; HR >120; SBP <90; cold peripheries; confusion; anuria; shock","IV Hartmann's/NaCl URGENTLY + HDU admission"]], [CW*0.17,CW*0.5,CW*0.33]),sp(3), pe("Dehydration assessment: capillary refill >2s, skin turgor (stays tented >2s = moderate dehydration), sunken eyes, pulse, BP, urine output. In children: sunken fontanelle = significant dehydration. — Oxford Handbook"),sp(3), ss("B. GENERAL SIGNS"), tb([["Finding","Think of..."], ["Weight loss + pallor","CRC, IBD, malabsorption, chronic infection, lymphoma"], ["Goitre + tachycardia + tremor + exophthalmos","Hyperthyroidism → check TFTs"], ["Hyperpigmentation + hypotension + ↓Na⁺","Addison's disease → short synacthen test"], ["Flushing + facial telangiectasia + wheeze","Carcinoid syndrome → 24-hr urinary 5-HIAA"], ["Lymphadenopathy (generalised)","HIV, lymphoma, TB"], ["Oral ulcers + angular stomatitis","IBD (Crohn's); coeliac disease"], ["Perianal fistulae / skin tags / abscesses","Crohn's disease (perianal involvement in 30%)"], ["Pyoderma gangrenosum","IBD extra-intestinal (more common in UC)"], ["Erythema nodosum","IBD extra-intestinal; also sarcoidosis, streptococcal"], ["Dermatitis herpetiformis (itchy blistering rash — extensor surfaces)","Coeliac disease — check anti-tTG IgA + anti-DGP"]], [CW*0.4,CW*0.6])] # RIGHT s+=[ss("C. ABDOMINAL EXAMINATION"), tb([["Finding","Interpretation"], ["RIF mass / tenderness","Crohn's (terminal ileum), caecal Ca, appendicular mass"], ["LIF tenderness","UC, diverticular disease, sigmoid Ca, ischaemic colitis"], ["Hepatomegaly","Liver metastases from CRC; amoebic liver abscess; CLD"], ["Distension + ↑ bowel sounds","Infective / inflammatory — ↑ peristalsis"], ["Tender distension + fever","Consider toxic megacolon (UC/C. diff) — AXR urgently"], ["Rectal mass on PR","CRC — urgent 2WW referral"], ["Anal fissure / fistula / skin tags on DRE","Crohn's disease (pathognomonic of Crohn's)"]], [CW*0.4,CW*0.6]),sp(3), al("Toxic megacolon: colon >6 cm + fever + tachycardia + ↑ WCC + ↑ CRP → AXR immediately. " "Causes: severe UC, C. diff. DO NOT give antidiarrhoeals. Surgical review urgently."),sp(4), h1("INVESTIGATIONS — ACUTE DIARRHOEA"),ss("STOOL TESTS — MOST IMPORTANT IN ACUTE"), tb([["Test","What It Detects","When to Order"], ["Stool MC&S (culture)","Bacterial pathogens — Salmonella, Shigella, Campylobacter, E. coli, Yersinia","All acute diarrhoea >48 hrs; blood in stool; travel Hx; immunocomp"], ["C. diff toxin EIA\n(GDH + toxin A/B)","C. difficile toxins A and B. 2-stage: GDH screen (sensitive) + toxin confirm (specific).","Post-antibiotic; hospital-acquired; age >65; PPIs"], ["C. diff PCR (NAAT)","C. diff toxin genes — most sensitive","Equivocal EIA; high pre-test probability; outbreak"], ["Stool OCP\n(ova, cysts, parasites)","Giardia, Entamoeba, Cryptosporidium, Cyclospora","Travel Hx; prolonged >2 weeks; immunocomp; HIV"], ["Norovirus / Rotavirus PCR","Viral gastroenteritis pathogens","Winter outbreaks; children; ward closures"], ["Faecal calprotectin","Intestinal inflammation marker","Chronic diarrhoea screen: IBD vs IBS"], ["Faecal occult blood (FOB)","Occult GI blood loss","Chronic diarrhoea; alarm features; CRC screening"], ["Faecal elastase","Exocrine pancreatic function","Steatorrhoea; suspected chronic pancreatitis"]], [CW*0.27,CW*0.38,CW*0.35]),sp(3), pe("Faecal calprotectin is the KEY non-invasive test to distinguish IBD from IBS. " "Raised (>50 μg/g) = mucosal inflammation = IBD. Normal = IBS (no inflammation). " "Use BEFORE colonoscopy referral in chronic diarrhoea. — Sleisenger & Fordtran")] s.append(PageBreak()) # PAGE 6 — CHRONIC DIARRHOEA INVESTIGATIONS + MANAGEMENT # LEFT s+=[h1("INVESTIGATIONS — CHRONIC DIARRHOEA"),ss("BLOOD TESTS"), tb([["Test","What to Find","Why It Matters"], ["FBC","Hb↓ + MCV↓ = IDA; macrocytic = B12/folate↓; ↑ WCC = infective/IBD","IDA → CRC / coeliac / IBD"], ["CRP / ESR","Raised in IBD, infective, CRC","Normal in IBS and functional diarrhoea"], ["U&E","K⁺↓ + Na⁺↓ in severe diarrhoea; Cr↑ = AKI (dehydration/HUS)","Monitor electrolytes in all patients"], ["TFTs (TSH/T4)","Hyperthyroidism","Diarrhoea + weight loss + palpitations"], ["LFTs","Liver disease; amoebic abscess (↑ALP + bilirubin)","Amoebiasis; CLD; hepatic mets"], ["Anti-tTG IgA + serum IgA","Coeliac serology. MUST be eating gluten at test.","If IgA deficient: anti-DGP IgG instead"], ["B12 / Folate / Ferritin / Vit D","Malabsorption screen","Coeliac, Crohn's, short bowel"], ["Gut hormones (VIP, gastrin, 5-HIAA)","VIPoma, ZES, carcinoid","Only if clinical suspicion — expensive"], ["HIV test","CD4 count if positive","If risk factors — CMV, Crypto, MAI, microsporidium"], ["Blood cultures","Bacteraemia / typhoid","Fever + systemic illness + diarrhoea"]], [CW*0.25,CW*0.3,CW*0.45]),sp(3), ss("IMAGING & ENDOSCOPY"), tb([["Investigation","When to Use","Findings"], ["AXR","Acute severe colitis; toxic megacolon","Dilated colon >6 cm = toxic megacolon; thumbprinting = ischaemia/IBD"], ["USS abdomen","RIF pain; liver abscess; IBD","Bowel wall thickening; amoebic abscess; lymphadenopathy"], ["CT abdomen/pelvis","Severe/complicated IBD; CRC staging; ischaemic colitis","Extent of disease, LN, mets, mural thickening"], ["Colonoscopy + biopsy\n(GOLD STANDARD)","Chronic diarrhoea; alarm features; IBD diagnosis","Mucosal changes; polyps; Ca; microscopic colitis (biopsy normal mucosa)"], ["OGD + duodenal biopsy\n(4 samples minimum)","Coeliac; Giardia; Crohn's","Villous atrophy (coeliac); trophozoites (Giardia)"], ["MR enterography","Small bowel Crohn's; fistulae; strictures","No radiation; best for Crohn's activity"], ["Capsule endoscopy","Suspected small bowel Crohn's or obscure GI bleed","CI if stricture suspected"], ["Hydrogen breath test","Lactose intolerance; SIBO","↑ H₂ after lactose/lactulose ingestion"], ["SeHCAT scan\n(bile acid malabsorption)","Post-cholecystectomy diarrhoea; idiopathic BAM","Retained activity <15% at 7 days = BAM"]], [CW*0.28,CW*0.3,CW*0.42])] # RIGHT s+=[h1("MANAGEMENT — DIARRHOEA"),ss("STEP 1 — RESUSCITATION & SUPPORTIVE CARE"), bl("<b>Rehydration:</b> ORS (oral rehydration solution) — 1st line for mild-moderate dehydration"), bl("<b>ORS composition (WHO):</b> Na⁺ 75 mmol/L + K⁺ 20 mmol/L + glucose 75 mmol/L + Cl⁻ 65 mmol/L + citrate 10 mmol/L"), bl("<b>Mechanism of ORS:</b> Na⁺-glucose co-transporter (SGLT1) absorbs Na⁺ + glucose → water follows passively. Works even in secretory diarrhoea."), bl("<b>IV fluids:</b> Hartmann's or 0.9% NaCl for severe dehydration / unable to tolerate oral"), bl("<b>Electrolytes:</b> K⁺ replacement (hypokalaemia universal in severe diarrhoea)"), bl("<b>Diet:</b> Continue feeding; small bland meals. 'Starving' delays mucosal recovery."), bl("<b>Hygiene/isolation:</b> Contact precautions for C. diff/Norovirus. Soap+water (NOT alcohol for C. diff spores)"), bl("<b>Notify PHE:</b> Salmonella, Shigella, VTEC (E. coli O157), Cryptosporidium, Cholera, Typhoid — all notifiable"),sp(3), ss("STEP 2 — ANTIDIARRHOEAL AGENTS"), tb([["Drug","Mechanism","Use / Caution"], ["Loperamide\n(Imodium)","μ-opioid agonist → ↓ gut motility + ↑ sphincter tone","Non-bloody acute diarrhoea; traveller's diarrhoea (symptom control). NEVER in bloody/C. diff/EHEC → risk of toxic megacolon."], ["Codeine phosphate","Central + peripheral opioid","Short-term symptom control. Dependency risk in chronic use."], ["Bismuth subsalicylate","Antimicrobial + antisecretory","Traveller's diarrhoea prevention + treatment."], ["Cholestyramine","Bile acid sequestrant → binds bile acids","Post-cholecystectomy BAM; Crohn's with ileal resection."], ["Octreotide\n(somatostatin analogue)","↓ gut secretion + motility; ↓ tumour hormone secretion","VIPoma, carcinoid diarrhoea, high-output fistula, short bowel syndrome."]], [CW*0.22,CW*0.3,CW*0.48]),sp(3), al("NEVER give loperamide or antiperistaltic agents in: C. difficile colitis, E. coli O157 (EHEC), Shigella dysentery, or any bloody diarrhoea — risk of toxic megacolon / ↑ Shiga toxin release."),sp(4), ss("ORS CALCULATION — OSCE VIVA"), bd("<b>Mild dehydration (3–5%):</b> ORS 50 mL/kg over 4 hours + maintenance 100 mL/kg/day"), bd("<b>Moderate dehydration (5–8%):</b> ORS 100 mL/kg over 4 hours OR IV Hartmann's 20 mL/kg bolus"), bd("<b>Plus ongoing losses:</b> Replace 10 mL/kg per loose stool or vomit episode")] s.append(PageBreak()) # PAGE 7 — SPECIFIC TREATMENTS # LEFT s+=[h1("MANAGEMENT — SPECIFIC TREATMENTS"), tb([["Cause","First-Line Treatment","Key Points"], ["Viral gastroenteritis\n(Norovirus/Rotavirus)","Supportive ORS","Self-limiting 1–3 days. No antivirals. Isolate — contact precautions."], ["Campylobacter","Usually supportive","Azithromycin 500mg OD × 3d if severe/immunocomp. ↑ fluoroquinolone resistance."], ["Salmonella non-typhi","Usually supportive","Ciprofloxacin 500mg BD × 5–7d ONLY if bacteraemic/immunocomp/severe."], ["Typhoid (S. typhi)","IV ceftriaxone 2g OD × 14d","Or azithromycin. Notify PHE. Exclude food handlers."], ["Shigella (dysentery)","Ciprofloxacin 500mg BD × 3d","Or azithromycin. Shorten course significantly."], ["E. coli O157 (EHEC)","SUPPORTIVE ONLY — no antibiotics","Antibiotics → ↑ Shiga toxin release → ↑ HUS risk. Monitor FBC + renal function daily."], ["C. diff (mild-moderate)","Oral vancomycin 125mg QDS × 10d","1st line per NICE 2021. NOT metronidazole monotherapy any more."], ["C. diff (severe)","Oral vancomycin 500mg QDS + IV metronidazole 500mg TDS","Severe = WCC >15, Cr >1.5× baseline, temp >38.5°C, albumin <25"], ["C. diff (1st recurrence)","Fidaxomicin 200mg BD × 10d","Narrower spectrum → better preservation of microbiome. Preferred for recurrence."], ["C. diff (≥2nd recurrence)","Faecal microbiota transplantation (FMT)","~90% efficacy. Restores gut flora. Via colonoscopy, enema, or capsule."], ["Giardia","Metronidazole 400mg TDS × 5–7d","Or tinidazole 2g single dose. Test and treat contacts."], ["Entamoeba histolytica","Metronidazole 800mg TDS × 5d + diloxanide furoate 500mg TDS × 10d","Metronidazole = tissue phase. Diloxanide = luminal cysts (must treat both)."], ["Cryptosporidium (immunocomp)","Nitazoxanide 500mg BD × 3d + optimise ART","Self-limiting in immunocompetent."], ["Coeliac disease","Strict lifelong gluten-free diet","Monitor anti-tTG IgA annually. DXA scan. Supplement B12/Vit D/Fe."], ["UC (acute flare — mild-mod)","Mesalazine (5-ASA) oral + rectal","Prednisolone 40mg OD orally if inadequate response."], ["UC (acute flare — severe)","IV hydrocortisone 100mg QDS","Oxford criteria for severe colitis. Ciclosporin/infliximab if steroid-refractory."], ["Crohn's (acute flare)","Prednisolone 40mg OD or budesonide (ileocaecal)","Azathioprine/6-MP maintenance. Adalimumab/infliximab if biologics needed."], ["Carcinoid syndrome","Octreotide LAR SC monthly","Surgical resection if localised. Somatostatin receptor scintigraphy (Octreoscan) for staging."], ["VIPoma","Octreotide + surgical resection","Streptozocin if metastatic."], ["Lactose intolerance","Lactose-free diet + lactase enzyme supplements","Not an allergy; calcium supplementation needed."], ["Post-cholecystectomy BAM","Cholestyramine 4g BD–TDS","Or colesevelam. SeHCAT confirms diagnosis."], ["Microscopic colitis","Stop NSAIDs/PPIs + budesonide 9mg OD × 8 wks","Bismuth subsalicylate; mesalazine alternatives."]], [CW*0.25,CW*0.38,CW*0.37])] # RIGHT s+=[h4("C. DIFFICILE — DETAILED APPROACH"),ss("RISK FACTORS"), bl("<b>Antibiotics:</b> Clindamycin (highest risk), amoxicillin, cephalosporins (3rd gen), ciprofloxacin"), bl("<b>Hospital/care home:</b> C. diff spores survive on surfaces for months; faeco-oral spread"), bl("<b>Age >65:</b> ↓ immune response; ↓ microbiome diversity"), bl("<b>PPIs:</b> ↓ gastric acidity → ↑ spore survival → ↑ risk 2–3×"), bl("<b>Immunosuppression:</b> Steroids, chemotherapy, HIV, organ transplant"), bl("<b>Previous C. diff episode:</b> 20–25% risk of recurrence after 1st episode"),sp(3), ss("DIAGNOSIS"), tb([["Test","Detail"], ["Stool toxin EIA\n(GDH + toxin A/B)","2-stage: GDH screen (highly sensitive) → toxin A/B confirm (specific). Rapid. First-line."], ["Stool PCR (NAAT)","Most sensitive; detects toxin genes. High NPV. Use if EIA equivocal."], ["Colonoscopy","Pseudomembranes (raised yellow/cream plaques) = pathognomonic. Reserved for equivocal/complex cases."], ["AXR / CT abdomen","Toxic megacolon: colon >6 cm; thumbprinting; free air if perforated."]], [CW*0.35,CW*0.65]),sp(3), ss("SEVERITY GRADING (NICE 2021)"), tb([["Grade","Criteria"], ["Mild","Diarrhoea only; WCC normal; no systemic features"], ["Moderate","WCC 11–15; Cr <1.5× baseline; temp <38.5°C"], ["Severe","WCC >15 OR Cr >1.5× baseline OR albumin <25 OR temp >38.5°C"], ["Life-threatening","Hypotension; toxic megacolon (colon >6 cm); ileus; CT evidence of severe colitis"]], [CW*0.25,CW*0.75]),sp(3), pe("C. diff: Stop implicated antibiotic where possible. Use soap + water (NOT alcohol — spores not killed by alcohol). Contact + enteric precautions. Review and rationalise PPIs. Metronidazole monotherapy is NO LONGER first-line (NICE 2021)."),sp(4), h4("INFLAMMATORY BOWEL DISEASE — DIARRHOEA"), tb([["Feature","Ulcerative Colitis","Crohn's Disease"], ["Distribution","Continuous from rectum proximally — ALWAYS involves rectum","Any GI tract mouth to anus; SKIP lesions; spares rectum 50%"], ["Diarrhoea type","Bloody mucoid; urgency; tenesmus","Variable: watery, bloody, or malabsorptive"], ["Depth","Mucosal only (superficial)","Transmural (full thickness)"], ["Histology","Crypt abscesses; goblet cell depletion; no granulomas","Non-caseating granulomas; cobblestone appearance"], ["Smoking","Protective (paradoxically)","Worsens disease — pro-inflammatory"], ["Key complication","Toxic megacolon; ↑ CRC risk (pancolitis >8 yrs)","Strictures; fistulae; perianal disease; short bowel"], ["Surgery","Proctocolectomy = CURATIVE","NOT curative — reserved for complications"]], [CW*0.22,CW*0.39,CW*0.39]),sp(3), pe("Crohn's: granulomas + skip lesions + transmural + smoking worsens. UC: continuous + mucosal only + smoking protects + CURATIVE surgery. Classic OSCE reversal trap!")] s.append(PageBreak()) # PAGE 8 — HUS + TRAVELLER'S + OSCE Q&A # LEFT s+=[h3("HAEMOLYTIC URAEMIC SYNDROME (HUS)"), bd("<b>Classic Triad:</b> Microangiopathic haemolytic anaemia + Thrombocytopaenia + Acute kidney injury"),sp(2), tb([["Feature","Details"], ["Organism","E. coli O157:H7 (STEC) — Shiga toxin producing E. coli; also Shigella dysenteriae type 1"], ["Mechanism","Shiga toxin damages glomerular endothelium → thrombotic microangiopathy → platelet thrombi → haemolysis + AKI + ↓plt"], ["Typical patient","Child with bloody diarrhoea → 5–10 days later: pallor + oliguria + petechiae"], ["Triad","Microangiopathic haemolytic anaemia + Thrombocytopaenia + AKI"], ["FBC","↓ Hb; ↓ platelets; schistocytes (fragmented RBCs) on blood film = MAHA"], ["Blood film","Schistocytes/helmet cells — microangiopathic haemolysis"], ["Renal","↑↑ creatinine; haematuria + proteinuria on urinalysis"], ["LDH","↑↑ — marker of haemolysis"], ["Management","SUPPORTIVE: IV fluids; RBC transfusion if Hb <7; dialysis if severe AKI; plt transfusion generally AVOIDED (can worsen)"], ["NO antibiotics","Antibiotics ↑ Shiga toxin release → worsen HUS"], ["NO antidiarrhoeals","Loperamide → ↑ toxin retention → worsen HUS"], ["Prognosis","~5% mortality; 25% long-term renal sequelae"]], [CW*0.28,CW*0.72]),sp(4), h1("TRAVELLER'S DIARRHOEA"), bd("<b>Definition:</b> ≥3 loose stools/day + ≥1 of: nausea, vomiting, cramps, fever, blood — during or within 10 days of return from travel"),sp(3), tb([["Region","Commonest Organisms","Risk Level"], ["South / SE Asia","ETEC, EAEC, Campylobacter, typhoid, cholera","High (30–70%)"], ["Sub-Saharan Africa","ETEC, Shigella, typhoid, Giardia, amoebiasis","High"], ["Latin America","ETEC, EAEC, Shigella","High"], ["Middle East","ETEC, Cryptosporidium, Giardia","Moderate"], ["Southern Europe","Campylobacter, Salmonella","Low–moderate"]], [CW*0.27,CW*0.45,CW*0.28]),sp(3), ss("MANAGEMENT OF TRAVELLER'S DIARRHOEA"), bl("ORS + bland diet. Loperamide for non-bloody watery diarrhoea (symptom control for travel)."), bl("<b>Azithromycin 500mg OD × 3d</b> — antibiotic of choice (covers Campylobacter + resistance)"), bl("Ciprofloxacin 500mg BD × 3d — alternative (↑ resistance in SE Asia)"), bl("<b>Prevention:</b> 'Boil it, cook it, peel it, or forget it'. Avoid ice/tap water/street food."), bl("Bismuth subsalicylate — prophylaxis for high-risk short trips")] # RIGHT s+=[h1("OSCE VIVA — RAPID FIRE Q&A"), tb([["Examiner Asks...","Your Answer"], ["Define diarrhoea","≥3 loose stools/day or stool volume >200 mL/day or stool weight >200 g/day"], ["Secretory vs osmotic — key test?","Fasting test: secretory CONTINUES; osmotic STOPS. Stool osmotic gap: <50 = secretory; >125 = osmotic"], ["Nocturnal diarrhoea — significance?","Organic cause (IBD, secretory tumour, CRC). IBS NEVER causes nocturnal diarrhoea"], ["Most common bacterial cause UK?","Campylobacter jejuni — undercooked poultry"], ["Traveller's diarrhoea commonest organism?","ETEC (enterotoxigenic E. coli) — watery, no blood; treat with azithromycin"], ["C. diff risk factors?","Recent antibiotics (clindamycin, amoxicillin, cephalosporins), hospital, age >65, PPIs, immunosuppression"], ["C. diff first-line treatment 2021?","Oral vancomycin 125mg QDS × 10 days — NOT metronidazole monotherapy (NICE 2021)"], ["C. diff why NOT alcohol gel?","Alcohol does NOT kill C. diff spores — use soap and water only"], ["Why NO antibiotics in E. coli O157?","Antibiotics lyse bacteria → ↑ Shiga toxin release → ↑ HUS risk; supportive only"], ["Best test to distinguish IBD from IBS?","Faecal calprotectin — raised (>50 μg/g) in IBD; normal in IBS"], ["Carcinoid syndrome — diagnosis?","24-hour urinary 5-HIAA. Octreoscan for tumour localisation. Treat with octreotide."], ["Coeliac test — what must patient be doing?","Eating gluten — anti-tTG IgA is falsely negative on gluten-free diet"], ["Child with bloody diarrhoea → oliguria + pallor. Diagnosis?","HUS (E. coli O157). Triad: MAHA + ↓plt + AKI. NO antibiotics, NO antidiarrhoeals. Supportive + dialysis."], ["Post-cholecystectomy diarrhoea mechanism?","Bile salt malabsorption (BAM) → secretory diarrhoea. SeHCAT confirms. Treat with cholestyramine."], ["What is FMT and when used?","Faecal microbiota transplantation — for recurrent C. diff (≥2 episodes); ~90% efficacy; restores gut flora"], ["UC vs Crohn's — one key difference?","UC always involves rectum (continuous); Crohn's spares rectum in 50% (skip lesions). Granulomas = Crohn's."], ["Microscopic colitis — features?","Chronic watery non-bloody diarrhoea; normal colonoscopy; biopsy = collagen/lymphocytic infiltrate; NSAIDs/PPIs cause; treat with budesonide"], ["Stool osmotic gap >125?","Osmotic diarrhoea — unabsorbed solutes. Stops on fasting. Coeliac, lactose intolerance, laxative abuse."]], [CW*0.42,CW*0.58]),sp(4), h1("MNEMONICS — DIARRHOEA"),ss("CAUSES — 'ABCDE'"), tb([["Letter","Category","Example"], ["A","Antibiotics / post-infective","C. diff; antibiotic-associated diarrhoea"], ["B","Bowel disease (IBD / Ca)","Crohn's, UC, CRC, ischaemic colitis"], ["C","Coeliac / Chronic pancreatitis","Steatorrhoea — malabsorption pattern"], ["D","Drugs / Diet","Laxatives, metformin, orlistat, lactose, olmesartan"], ["E","Endocrine / Exotic infection","Carcinoid, VIPoma, thyroid; Giardia, amoebiasis"]], [CW*0.08,CW*0.3,CW*0.62],TL),sp(3), ss("MECHANISM — 'SOME'"), tb([["S","Secretory","Large volume; continues on fasting; osmotic gap <50"], ["O","Osmotic","Stops on fasting; bloating; osmotic gap >125"], ["M","Motility","Variable; no structural lesion; IBS, hyperthyroid"], ["E","Exudative","Blood + pus; small volume; urgency; inflammation"]], [CW*0.07,CW*0.2,CW*0.73],DB),sp(4), gt("FINAL OSCE TIP: In any acute diarrhoea — 1) Assess dehydration (vitals + skin turgor + UO), 2) Stool MC&S + C. diff toxin EIA, 3) ORS / IV fluids + electrolyte correction. Then investigate cause. NEVER give loperamide if bloody or suspect C. diff.")] doc.build(s,canvasmaker=Cv) print("Built:",out) build() ENDSCRIPT echo "Script written. Running..." python3 /home/daytona/workspace/osce-booklets/build_diarr2.py 2>&1

Running Command

python3 -c " from pypdf import PdfReader; import os for f,label in [ ('/home/daytona/workspace/osce-booklets/Constipation_OSCE_Booklet_v2.pdf','CONSTIPATION'), ('/home/daytona/workspace/osce-booklets/Diarrhoea_OSCE_Booklet_v2.pdf','DIARRHOEA'), ]: r=PdfReader(f); sz=os.path.getsize(f) print(f'{label}: {len(r.pages)} pages, {sz//1024} KB') for i,p in enumerate(r.pages): t=p.extract_text() print(f' p{i+1}: {t[:70].strip()}') "

Both booklets are done. Here are your downloads:

Constipation OSCE Booklet v2

PDF Document · PDF

— 8 pages, 40 KB

Diarrhoea OSCE Booklet v2

PDF Document · PDF

— 12 pages, 54 KB

Here's what's covered — sourced from Bailey & Love 28e, Sleisenger & Fordtran, Harrison's 22e, Yamada's Gastroenterology, Goldman-Cecil:
Constipation (8 pages)
  • Epidemiology + Rome IV criteria with clinical significance
  • Full pathophysiology — HAPCs, RAIR, dyssynergia, Hirschsprung's mechanism
  • SPINE mnemonic causes table (full detail) + primary functional causes
  • History taking: 5 sections including full drug table with mechanisms
  • Examination: General + Abdominal + DRE findings + specialist anorectal tests (manometry, Sitzmarks, proctogram, balloon expulsion)
  • Investigations: Bloods + imaging + endoscopy
  • Laxative ladder: 8 classes with mechanisms, doses, and notes (including PAMORAs for OIC)
  • Specific treatments for 12 conditions + surgical options table
  • Hirschsprung's complete + special populations (elderly, pregnancy, Parkinson's)
  • Complications table + IBS-C vs functional constipation + 16-question Viva Q&A
Diarrhoea (12 pages)
  • Definitions + epidemiology from Sleisenger & Fordtran
  • SOME mechanism classification + stool osmotic gap formula + large vs small volume
  • Full infective causes: 12 bacterial organisms + 5 viral + 5 protozoal — all with treatment
  • Full non-infective causes: 18 conditions (IBD, malabsorption, endocrine, drugs, CRC, microscopic colitis)
  • History taking: 10-question character table + 10 associated symptom clusters
  • Drug causes table with mechanisms + full social/travel history
  • Dehydration grading table + general signs + abdominal exam
  • Investigations: stool tests + full blood panel + imaging/endoscopy
  • Management: ORS mechanism + antidiarrhoeals + 22 specific treatments
  • C. difficile complete section: risk factors, diagnosis, severity grading (NICE 2021)
  • IBD comparison table (UC vs Crohn's)
  • HUS complete section + Traveller's diarrhoea by region
  • 18-question OSCE Viva + ABCDE + SOME mnemonics

Okay now make for abdominal pain one booklet and lower gi bleeding one booklet which I have already attached one booklet

Running Command

cd /home/daytona/workspace && python3 -c " import subprocess, os base='attachments/93929b43-6afe-45b8-83b8-959a1bf7b45a/' for fname,tag in [('Abdominal Pain - SGT.pdf','abpain'),('lower GI bleeding.pdf','lgib')]: os.makedirs(f'/tmp/{tag}_imgs',exist_ok=True) subprocess.run(['pdftoppm','-r','200',base+fname,f'/tmp/{tag}_imgs/page'],capture_output=True) imgs=sorted(os.listdir(f'/tmp/{tag}_imgs')) print(f'{tag}: {len(imgs)} pages') all_text=[] for img in imgs: r=subprocess.run(['tesseract',f'/tmp/{tag}_imgs/{img}','stdout','--psm','3'],capture_output=True,text=True) all_text.append(r.stdout) with open(f'/tmp/{tag}_ocr.txt','w') as f: f.write('\n'.join(all_text)) print(open(f'/tmp/{tag}_ocr.txt').read()[:1200]) print('---') " 2>&1

Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Running Command

cat > /home/daytona/workspace/osce-booklets/build_abdpain.py << 'ENDSCRIPT' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, HRFlowable, NextPageTemplate, PageBreak) from reportlab.lib.styles import ParagraphStyle from reportlab.lib.enums import TA_CENTER from reportlab.pdfgen import canvas as pdfgen_canvas NAVY=colors.HexColor('#1a2a4a'); GOLD=colors.HexColor('#c8a951') DB=colors.HexColor('#1a4971'); TL=colors.HexColor('#2a7a6b') RD=colors.HexColor('#c0392b'); RDBG=colors.HexColor('#fdecea') GN=colors.HexColor('#2e7d32'); GNBG=colors.HexColor('#e8f5e9') AM=colors.HexColor('#f57f17'); AMBG=colors.HexColor('#fff8e1') PR=colors.HexColor('#6a1b9a'); PRBG=colors.HexColor('#f3e5f5') W=colors.white; BK=colors.black GL=colors.HexColor('#f2f4f7'); GM=colors.HexColor('#d5dce8') PW,PH=A4; ML=13*mm; MR=13*mm; HH=18*mm; FH=10*mm UW=PW-ML-MR; CG=6*mm; CW=(UW-CG)/2 FTR="Sources: Bailey & Love 28e \xb7 Sleisenger & Fordtran \xb7 Harrison's 22e \xb7 Rosen's Emergency Medicine \xb7 Oxford Handbook" TOPIC="ABDOMINAL PAIN" def mk(): d={} d['TM']=ParagraphStyle('TM',fontName='Helvetica-Bold',fontSize=18,textColor=W,leading=22,alignment=TA_CENTER) d['TS']=ParagraphStyle('TS',fontName='Helvetica-BoldOblique',fontSize=10,textColor=GOLD,leading=13,alignment=TA_CENTER) d['TT']=ParagraphStyle('TT',fontName='Helvetica',fontSize=8,textColor=colors.HexColor('#a0bcd8'),leading=11,alignment=TA_CENTER) d['H1']=ParagraphStyle('H1',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=DB,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H2']=ParagraphStyle('H2',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=TL,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H3']=ParagraphStyle('H3',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=RD,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['H4']=ParagraphStyle('H4',fontName='Helvetica-Bold',fontSize=9,textColor=W,backColor=PR,borderPadding=(3,5,3,5),leading=12,spaceAfter=4,spaceBefore=6) d['SS']=ParagraphStyle('SS',fontName='Helvetica-Bold',fontSize=8.5,textColor=DB,leading=11,spaceAfter=3,spaceBefore=5) d['BD']=ParagraphStyle('BD',fontName='Helvetica',fontSize=7.8,textColor=BK,leading=11,spaceAfter=2) d['BL']=ParagraphStyle('BL',fontName='Helvetica',fontSize=7.8,textColor=BK,leading=11,leftIndent=10,bulletIndent=2,spaceAfter=1.5) d['PE']=ParagraphStyle('PE',fontName='Helvetica-BoldOblique',fontSize=7.8,textColor=colors.HexColor('#7b3f00'),backColor=AMBG,borderColor=AM,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['AL']=ParagraphStyle('AL',fontName='Helvetica-Bold',fontSize=7.8,textColor=RD,backColor=RDBG,borderColor=RD,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['GT']=ParagraphStyle('GT',fontName='Helvetica',fontSize=7.8,textColor=colors.HexColor('#1b5e20'),backColor=GNBG,borderColor=GN,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['PB']=ParagraphStyle('PB',fontName='Helvetica-BoldOblique',fontSize=7.8,textColor=PR,backColor=PRBG,borderColor=PR,borderWidth=0.8,borderPadding=(4,5,4,5),leading=11,spaceAfter=4,spaceBefore=4) d['TH']=ParagraphStyle('TH',fontName='Helvetica-Bold',fontSize=7.5,textColor=W,leading=10,alignment=TA_CENTER) d['TC']=ParagraphStyle('TC',fontName='Helvetica',fontSize=7.5,textColor=BK,leading=10) d['FO']=ParagraphStyle('FO',fontName='Helvetica-Oblique',fontSize=6.5,textColor=colors.HexColor('#aaaaaa'),leading=9,alignment=TA_CENTER) return d ST=mk() class Cv(pdfgen_canvas.Canvas): def __init__(self,f,**kw): super().__init__(f,**kw); self._st=[]; self._p=0 def showPage(self): self._st.append(dict(self.__dict__)); self._startPage() def save(self): for x in self._st: self.__dict__.update(x); self._d(); super().showPage() super().save() def _d(self): self._p+=1; c=self c.setFillColor(NAVY); c.rect(0,PH-HH,PW,HH,fill=1,stroke=0) c.setFillColor(GOLD); c.rect(0,PH-HH-1.5,PW,1.5,fill=1,stroke=0) c.setFillColor(GOLD); c.setFont('Helvetica-Bold',8); c.drawString(ML,PH-10*mm,f"Page {self._p}") c.setFillColor(W); c.setFont('Helvetica-Bold',12); c.drawCentredString(PW/2,PH-8.5*mm,TOPIC) c.setFont('Helvetica',7.5); c.setFillColor(colors.HexColor('#a0bcd8')) c.drawCentredString(PW/2,PH-13.5*mm,"Clinical OSCE Notes | Final Year MBBS") c.setFillColor(GOLD); c.setFont('Helvetica-Bold',7.5) c.drawString(ML,PH-HH-8,"LEFT"); c.drawRightString(PW-MR,PH-HH-8,"RIGHT") c.setFillColor(NAVY); c.rect(0,0,PW,FH+3,fill=1,stroke=0) c.setFillColor(GOLD); c.rect(0,FH+3,PW,1,fill=1,stroke=0) c.setFillColor(colors.HexColor('#aaaaaa')); c.setFont('Helvetica-Oblique',6) c.drawCentredString(PW/2,4*mm,FTR) if self._p>1: c.setStrokeColor(GM); c.setLineWidth(0.5) c.line(ML+CW+CG/2,PH-HH-12,ML+CW+CG/2,FH+6) def h1(t): return Paragraph(f"<b>{t}</b>",ST['H1']) def h2(t): return Paragraph(f"<b>{t}</b>",ST['H2']) def h3(t): return Paragraph(f"<b>{t}</b>",ST['H3']) def h4(t): return Paragraph(f"<b>{t}</b>",ST['H4']) def ss(t): return Paragraph(t,ST['SS']) def bd(t): return Paragraph(t,ST['BD']) def bl(t): return Paragraph(f"• {t}",ST['BL']) def pe(t): return Paragraph(f"* OSCE PEARL: {t}",ST['PE']) def al(t): return Paragraph(f"! RED FLAG: {t}",ST['AL']) def gt(t): return Paragraph(t,ST['GT']) def pb(t): return Paragraph(f"PATHOPHYSIOLOGY: {t}",ST['PB']) def sp(h=3): return Spacer(1,h) def tb(rows,ws,bg=None): if bg is None: bg=DB ts=TableStyle([('BACKGROUND',(0,0),(-1,0),bg),('TEXTCOLOR',(0,0),(-1,0),W), ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'),('FONTSIZE',(0,0),(-1,-1),7.5), ('ROWBACKGROUNDS',(0,1),(-1,-1),[GL,W]),('GRID',(0,0),(-1,-1),0.4,GM), ('LEFTPADDING',(0,0),(-1,-1),4),('RIGHTPADDING',(0,0),(-1,-1),4), ('TOPPADDING',(0,0),(-1,-1),3),('BOTTOMPADDING',(0,0),(-1,-1),3), ('VALIGN',(0,0),(-1,-1),'TOP')]) wr=[] for ri,row in enumerate(rows): wr.append([Paragraph(c,ST['TH'] if ri==0 else ST['TC']) if isinstance(c,str) else c for c in row]) return Table(wr,colWidths=ws,style=ts,hAlign='LEFT') def build(): out='/home/daytona/workspace/osce-booklets/Abdominal_Pain_OSCE_Booklet.pdf' by=FH+8; ty=PH-HH-14; fh=ty-by cf=Frame(ML,by,UW,fh,leftPadding=0,rightPadding=0,topPadding=0,bottomPadding=0) lf=Frame(ML,by,CW,fh,leftPadding=0,rightPadding=3,topPadding=0,bottomPadding=0) rff=Frame(ML+CW+CG,by,CW,fh,leftPadding=3,rightPadding=0,topPadding=0,bottomPadding=0) doc=BaseDocTemplate(out,pagesize=A4,leftMargin=ML,rightMargin=MR,topMargin=22*mm,bottomMargin=16*mm) doc.addPageTemplates([PageTemplate(id='cover',frames=[cf]),PageTemplate(id='two',frames=[lf,rff])]) s=[] # COVER s+=[NextPageTemplate('cover'),sp(26),Paragraph("ABDOMINAL PAIN",ST['TM']),sp(4), Paragraph("Clinical OSCE Notes | Final Year MBBS",ST['TS']),sp(6), Paragraph("Pathophysiology · Classification · Causes by Region · History Taking (SOCRATES)<br/>Examination · Investigations · Specific Conditions · Acute Abdomen · OSCE Q&amp;A",ST['TT']), sp(7),HRFlowable(width="80%",thickness=1.5,color=GOLD,spaceAfter=8,hAlign='CENTER'), Paragraph(FTR,ST['FO']),PageBreak()] s.append(NextPageTemplate('two')) # PAGE 2 — PATHOPHYSIOLOGY + CLASSIFICATION # LEFT s+=[h1("PATHOPHYSIOLOGY OF ABDOMINAL PAIN"), pb("Three types of abdominal pain based on nerve fibre type and pathway: Visceral (sympathetic C-fibres — poorly localised, dull/crampy, midline), Somatic/Parietal (A-delta fibres — sharply localised, movement-aggravated, peritoneal irritation), Referred (distant site, same dermatome as organ). — Sleisenger & Fordtran"),sp(3), tb([["Type","Nerve Fibres","Character","Localisation","Examples"], ["Visceral","C-fibres via sympathetic chain","Dull, crampy, colicky, nausea-producing","Poorly localised — midline (foregut: epigastric; midgut: periumbilical; hindgut: hypogastric)","Early appendicitis (periumbilical), small bowel obstruction, biliary colic, ureteric colic"], ["Somatic /\nParietal","A-delta fibres — parietal peritoneum","Sharp, severe, well-localised; worse on movement / deep breathing","Precisely localised to site of disease","Peritonitis, perforated viscus, parietal peritoneal irritation"], ["Referred","Convergence of visceral + somatic afferents at same spinal level","Felt at site distant from organ; same dermatomal level","At predictable distant sites","Diaphragm irritation → shoulder tip; biliary → right scapula; MI → epigastric; ureteric → groin"]], [CW*0.15,CW*0.17,CW*0.23,CW*0.25,CW*0.2]),sp(4), h1("REFERRED PAIN — CLASSIC SITES"), tb([["Origin","Referred To","Dermatomal Level"], ["Diaphragm (irritation — blood/pus)","Shoulder tip (ipsilateral)","C3, C4, C5 — phrenic nerve"], ["Gallbladder / biliary","Right scapula / right shoulder","T7–T9"], ["Pancreas (posterior)","Central back / interscapular","T6–T10"], ["Aortic aneurysm","Back (loin to groin)","L1"], ["Ureteric colic","Loin → groin → genitalia (ipsilateral)","T10–L1"], ["Testicular torsion / ovarian","Lower abdomen ↔ groin","T10"], ["MI (inferior wall)","Epigastric — mimics peptic ulcer","T6–T10 (cardiac afferents)"], ["Subphrenic abscess","Shoulder tip + hiccups","C3–C5 (phrenic)"], ["Appendix (retrocaecal)","Flank / back","T10–L1"]], [CW*0.3,CW*0.38,CW*0.32]),sp(4), pe("Shoulder tip pain in a patient post-laparoscopy or with ectopic pregnancy = diaphragmatic irritation by blood. NEVER attribute to musculoskeletal without excluding intra-abdominal pathology.")] # RIGHT s+=[h1("CAUSES BY ABDOMINAL REGION"), tb([["Region","Differential Diagnosis"], ["RIGHT UPPER QUADRANT\n(RUQ)","Biliary colic, acute cholecystitis, cholangitis, choledocholithiasis, Mirizzi syndrome, hepatitis (viral, alcoholic, ischaemic), hepatic abscess/Ca, right-sided pneumonia (referred), right-sided pleural effusion, peptic ulcer, Budd-Chiari syndrome, Fitz-Hugh-Curtis syndrome (PID-related perihepatitis)"], ["EPIGASTRIC","Peptic ulcer disease (gastric + duodenal), GORD/oesophagitis, acute pancreatitis, gastritis, Mallory-Weiss, gastroparesis, inferior MI (referred — MUST exclude), mesenteric ischaemia, aortic aneurysm (leaking)"], ["LEFT UPPER QUADRANT\n(LUQ)","Splenic pathology (splenomegaly, infarct, abscess, rupture), gastric ulcer, pancreatitis (tail), left-sided pneumonia (referred), left renal colic, left adrenal pathology"], ["RIGHT ILIAC FOSSA\n(RIF)","Appendicitis #1, ovarian cyst (right) / torsion / ruptured, ectopic pregnancy (right), Crohn's disease (terminal ileum), mesenteric adenitis, Meckel's diverticulitis, right ureteric colic, psoas abscess, caecal volvulus, caecal carcinoma, Yersinia (pseudo-appendicitis), right inguinal hernia"], ["LEFT ILIAC FOSSA\n(LIF)","Diverticulitis (sigmoid) #1, ovarian cyst (left) / torsion, ectopic pregnancy (left), Crohn's/UC, left ureteric colic, sigmoid volvulus, sigmoid carcinoma, left inguinal hernia, constipation (loaded sigmoid)"], ["PERIUMBILICAL","Early appendicitis (visceral pain before somatic shift), small bowel obstruction, mesenteric ischaemia, aortic pathology, umbilical hernia, Meckel's diverticulum"], ["HYPOGASTRIC /\nSUPRAPUBIC","Cystitis/UTI, acute urinary retention, ovarian pathology, fibroids, PID, endometriosis, bladder tumour, sigmoid colon, rectal pathology"], ["LOIN / FLANK","Ureteric/renal colic, pyelonephritis, renal infarct, retroperitoneal haematoma, psoas abscess"], ["GENERALISED","Peritonitis (generalised), intestinal obstruction, mesenteric ischaemia, irritable bowel, diabetic ketoacidosis (DKA), Addisonian crisis, sickle cell crisis, acute porphyria, lead poisoning"]], [CW*0.22,CW*0.78]),sp(3), al("EPIGASTRIC PAIN + ECG CHANGES OR RISK FACTORS: Exclude inferior MI FIRST before assuming GI cause. Always do an ECG in epigastric pain presenting to A&E.")] s.append(PageBreak()) # PAGE 3 — HISTORY TAKING # LEFT s+=[h1("HISTORY TAKING — SOCRATES"), bd("<b>Opening:</b> 'Can you tell me about your pain? Where is it and when did it start?'"),sp(3), ss("S — SITE"), tb([["Location","Most Likely Diagnosis"], ["Epigastric","PUD, pancreatitis, GORD, gastritis, inferior MI (referred)"], ["RUQ","Biliary colic, cholecystitis, hepatitis, right renal colic"], ["LUQ","Splenic pathology, gastric ulcer, pancreatitis tail, left renal colic"], ["RIF","Appendicitis, ovarian cyst/torsion, ectopic, Crohn's (TI), mesenteric adenitis"], ["LIF","Diverticulitis, ovarian, sigmoid volvulus, sigmoid Ca, constipation"], ["Periumbilical","Early appendicitis (visceral), SBO, mesenteric ischaemia"], ["Loin radiating to groin","Ureteric colic"], ["Generalised","Peritonitis, generalised obstruction, DKA, sickle cell crisis"]], [CW*0.33,CW*0.67]),sp(3), ss("O — ONSET"), tb([["Onset","Think of..."], ["Sudden (seconds — 'maximal from start')","Ruptured AAA, ruptured ectopic, perforated viscus, mesenteric artery occlusion, testicular/ovarian torsion"], ["Rapid (minutes)","Biliary colic, ureteric colic, MI"], ["Gradual (hours)","Appendicitis, pancreatitis, cholecystitis, SBO"], ["Slow (days–weeks)","Malignancy, IBD, chronic pancreatitis, abscess"]], [CW*0.38,CW*0.62]),sp(3), ss("C — CHARACTER"), tb([["Character","Think of..."], ["Colicky (comes and goes in waves)","Obstruction — intestinal, biliary, ureteric. Smooth muscle spasm."], ["Constant, severe, boring","Pancreatitis (radiates to back), aortic aneurysm"], ["Sharp, well-localised, worse on movement","Peritonitis — parietal peritoneum involved"], ["Burning / gnawing","PUD (epigastric), GORD (retrosternal)"], ["Crampy + diarrhoea","Infective gastroenteritis, IBD, IBS"], ["Tearing + back pain","Aortic dissection — EMERGENCY"]], [CW*0.38,CW*0.62]),sp(3), ss("R — RADIATION"), bl("Epigastric → back: Pancreatitis; penetrating PU; AAA"), bl("RUQ → right scapula: Biliary colic / cholecystitis"), bl("Loin → groin → genitalia: Ureteric colic"), bl("Epigastric → jaw/arm: MI — always exclude"), bl("Shoulder tip: Diaphragmatic irritation (ectopic, subphrenic abscess)")] # RIGHT s+=[ss("A — ASSOCIATED SYMPTOMS"), tb([["Associated Symptom","Diagnosis to Consider"], ["Nausea + vomiting","Obstruction, pancreatitis, cholecystitis, appendicitis, MI"], ["Vomiting relieves pain","PUD (duodenal)"], ["Vomiting precedes pain then haematemesis","Mallory-Weiss tear"], ["Fever + rigors","Cholangitis (Charcot's triad), pyelonephritis, diverticulitis, appendicitis"], ["Jaundice + RUQ pain + fever (Charcot's triad)","Ascending cholangitis — EMERGENCY"], ["Charcot's + confusion + hypotension (Reynolds' pentad)","Suppurative cholangitis — LIFE-THREATENING"], ["Diarrhoea + rectal bleeding","IBD, ischaemic colitis, infective colitis, CRC"], ["Absolute constipation + distension + vomiting","Bowel obstruction"], ["Haematuria + loin pain","Ureteric colic, pyelonephritis, renal Ca"], ["Missed period + pain (women)","Ectopic pregnancy — urgent beta-hCG + USS"], ["Weight loss + anorexia","Malignancy — urgent investigation"], ["Melaena / dark stool","Upper GI source (peptic ulcer, varices)"], ["Pale fatty stool (steatorrhoea)","Pancreatic/biliary pathology"], ["Urinary symptoms (dysuria, frequency)","UTI, cystitis — but exclude appendicitis in RIF"]], [CW*0.42,CW*0.58]),sp(3), ss("T — TIMING"), tb([["Timing Pattern","Significance"], ["Nocturnal pain awakening patient","Duodenal ulcer — acid secretion at night; also IBD; NOT IBS"], ["Pain 30 min after meals (postprandial)","Mesenteric angina; gastric ulcer"], ["Pain 2–3 hrs after meals; night pain; relieved by food/antacids","Duodenal ulcer"], ["Episodic (15–30 min episodes then resolve)","Biliary colic — stone impaction then displacement"], ["Constant and worsening","Acute cholecystitis, appendicitis — stone impacted"], ["Pain worse on breathing/movement","Peritonism — parietal peritoneal involvement"], ["Intermittent colicky","Intestinal obstruction, IBS, IBD (Crohn's)"]], [CW*0.42,CW*0.58]),sp(3), ss("E — EXACERBATING / RELIEVING"), tb([["Factor","Significance"], ["Food relieves — duodenal type","Duodenal ulcer (food buffers acid)"], ["Food worsens","Gastric ulcer; mesenteric angina (fear of eating); pancreatitis"], ["Leaning forward relieves","Pancreatitis (reduces pressure on coeliac plexus)"], ["Movement worsens","Peritonitis — involuntary guarding, rigidity"], ["Antacids / PPI relieve","Acid-related: PUD, GORD"], ["Antispasmodics relieve","IBS, biliary colic, ureteric colic"], ["Defaecation relieves","IBS; diverticular disease"], ["Inspiration worsens (Murphy's sign positive)","Acute cholecystitis"]], [CW*0.42,CW*0.58])] s.append(PageBreak()) # PAGE 4 — HISTORY CONT. + EXAMINATION # LEFT s+=[ss("S — SEVERITY"), bd("Score pain 0–10. Objective correlates: grimacing, position, mobility, vital signs. " "Pain out of proportion to examination findings = MESENTERIC ISCHAEMIA until proven otherwise."),sp(2), tb([["Score","Clinical Significance"], ["9–10 / 10","Catastrophic — ruptured AAA, ruptured ectopic, perforated viscus, mesenteric ischaemia"], ["7–8 / 10","Severe acute — appendicitis, pancreatitis, cholecystitis, renal colic"], ["5–6 / 10","Moderate — uncomplicated PUD, diverticulitis, IBD flare"], ["<5 / 10","Mild — functional, IBS, early inflammatory, musculoskeletal"]], [CW*0.2,CW*0.8]),sp(4), h1("DRUG + SOCIAL HISTORY — ABDOMINAL PAIN"), tb([["Drug / Factor","Significance"], ["NSAIDs / aspirin","PUD, gastritis, GI bleeding, colitis"], ["Steroids (long-term)","May mask peritonism; ↑ PUD risk; ↑ perforation risk"], ["Anticoagulants / antiplatelets","GI bleeding; retroperitoneal haematoma"], ["Opioids","Constipation; pseudo-obstruction; may mask pain"], ["Alcohol","Pancreatitis (#1 cause in UK), alcoholic hepatitis, gastritis"], ["Smoking","↑ PUD risk; ↑ Crohn's risk; ↑ AAA risk"], ["OCP / HRT","Mesenteric ischaemia; Budd-Chiari; ↑ gallstone risk"], ["Previous abdominal surgery","Adhesional obstruction #1 cause SBO"], ["Immunosuppression","Atypical presentation; serious pathology may present mildly"], ["Cocaine / stimulants","Mesenteric ischaemia; intestinal infarction"], ["Family Hx CRC / IBD / AAA","↑ personal risk — important in chronic/recurrent pain"], ["Menstrual cycle / LMP","Ectopic; ovarian cyst; endometriosis; mittelschmerz"]], [CW*0.35,CW*0.65]),sp(4), h1("NON-GI CAUSES OF ABDOMINAL PAIN — MUST NOT MISS"), tb([["Cause","Clue","Test"], ["Inferior MI","Epigastric pain + cardiac risk factors + no vomiting initially","ECG + troponin"], ["Pulmonary embolism","RUQ/RIF pain + pleuritic + tachycardia + hypoxia","CTPA + D-dimer"], ["Right lower lobe pneumonia","RUQ pain + fever + cough + dull base right","CXR"], ["Diabetic ketoacidosis (DKA)","Generalised pain + N&V + ↑ glucose + polyuria","BM + ABG + urinalysis"], ["Ectopic pregnancy","Lower abdominal pain + missed period + shoulder tip + haemodynamic instability","Beta-hCG + USS"], ["Addisonian crisis","Diffuse pain + hypotension + ↓Na⁺ + ↑K⁺","Cortisol + short synacthen test"], ["Acute porphyria","Severe diffuse pain + neuropsychiatric + no peritonism","Urine porphyrins"], ["Sickle cell crisis","Severe pain + known haemoglobinopathy","FBC + sickle screen"], ["Lead poisoning","Colicky pain + constipation + blue gingival line","Blood lead level"], ["Herpes zoster","Dermatomal pain BEFORE rash appears","Clinical + Tzanck smear"], ["Retroperitoneal bleed","Flank pain + Cullen's/Grey Turner's signs","CT abdomen"]], [CW*0.27,CW*0.43,CW*0.3])] # RIGHT s+=[h2("CLINICAL EXAMINATION — ABDOMINAL PAIN"),ss("A. BEFORE YOU TOUCH THE PATIENT"), bl("Observe: distress level, position (lying still = peritonitis; writhing = colic)"), bl("Vital signs: HR, BP, RR, temperature, SpO2, GCS"), bl("Shock signs: pallor, cold peripheries, capillary refill >2s, diaphoresis"), bl("End of bed: jaundice, cachexia, abdominal distension"),sp(3), ss("PATIENT POSITION — DIAGNOSTIC CLUE"), tb([["Position","Diagnosis"], ["Lies very still — afraid to move","Peritonitis (movement aggravates parietal pain)"], ["Writhing / cannot get comfortable","Colic — renal, biliary, intestinal"], ["Knees drawn up (foetal position)","Pancreatitis / peritonitis"], ["Leaning forward — sitting up","Pancreatitis (relieves pressure on coeliac plexus)"], ["Rigid abdomen — board-like","Generalised peritonitis (perforated viscus)"]], [CW*0.4,CW*0.6]),sp(3), ss("B. INSPECTION"), bl("Distension: fluid (ascites), gas (obstruction), mass, fat"), bl("Visible peristalsis: small bowel obstruction (in thin patients)"), bl("Scars: previous surgery → adhesions (SBO)"), bl("Pulsatile mass: AAA"), bl("Cullen's sign (periumbilical bruising) + Grey Turner's sign (flank bruising): severe acute pancreatitis / retroperitoneal haemorrhage"),sp(3), ss("C. PALPATION — SYSTEMATIC"), tb([["Finding","Interpretation"], ["Tenderness — localised","Organ-specific pathology at that site"], ["Guarding (voluntary)","Protective muscle contraction — pain on palpation"], ["Guarding (involuntary/rigidity)","Parietal peritoneal irritation — peritonism"], ["Rebound tenderness","Peritoneal inflammation — Blumberg's sign"], ["Board-like rigidity (generalised)","Generalised peritonitis — perforated viscus"], ["Murphy's sign positive","Acute cholecystitis — inspiration halted by pain"], ["Rovsing's sign positive","Appendicitis — RIF pain on palpation of LIF"], ["Psoas sign positive","Retrocaecal appendicitis / psoas abscess"], ["Obturator sign positive","Pelvic appendicitis / pelvic abscess"], ["Carnett's sign positive","Abdominal wall pain — pain ↑ with tensed muscles"], ["Pulsatile expansile mass (epigastric)","AAA — emergency imaging"], ["Periumbilical tenderness","Early appendicitis (visceral); mesenteric ischaemia"], ["Shifting dullness + fluid thrill","Ascites — liver disease, malignancy, heart failure"]], [CW*0.4,CW*0.6]),sp(3), ss("D. AUSCULTATION"), tb([["Bowel Sounds","Interpretation"], ["Absent (>2 min listening)","Ileus — post-operative; peritonitis; prolonged obstruction"], ["Tinkling / high-pitched","Mechanical small bowel obstruction"], ["Normal / hyperactive","Early obstruction; gastroenteritis; IBD"], ["Bruit (periumbilical)","Renal artery stenosis; mesenteric ischaemia; AAA"]], [CW*0.4,CW*0.6]),sp(3), ss("E. DRE — ALWAYS OFFER IN ABDOMINAL PAIN"), bl("Tenderness anteriorly: pelvic appendicitis, PID, pelvic abscess"), bl("Boggy mass: pelvic abscess, rectal Ca"), bl("Fresh blood: lower GI pathology"), bl("Melaena: upper GI source")] s.append(PageBreak()) # PAGE 5 — SPECIFIC SIGNS + INVESTIGATIONS # LEFT s+=[h4("SPECIFIC CLINICAL SIGNS — HIGH-YIELD FOR OSCE"), tb([["Sign","How Elicited","Indicates"], ["Murphy's sign","Deep palpation RUQ during inspiration → patient stops breathing due to pain","Acute cholecystitis (positive in 97%)"], ["Rovsing's sign","LIF palpation → pain felt in RIF","Appendicitis — peritoneal irritation transmits to RIF"], ["Psoas sign","Extend right hip (patient lying left lateral) → RIF pain","Retrocaecal appendicitis or psoas abscess"], ["Obturator sign","Flex + internally rotate right hip → RIF pain","Pelvic appendicitis or pelvic abscess"], ["Blumberg's sign\n(rebound tenderness)","Slow deep palpation then sudden release → sharp pain","Peritoneal irritation — peritonitis"], ["Carnett's sign","Palpate tender spot; ask patient to tense abdomen; if pain ↑ = positive","Abdominal wall pain (not visceral) — if positive, GI cause unlikely"], ["Cullen's sign","Periumbilical ecchymosis (bruising)","Retroperitoneal haemorrhage — severe pancreatitis / ruptured AAA"], ["Grey Turner's sign","Flank ecchymosis","Retroperitoneal haemorrhage — severe pancreatitis"], ["Kehr's sign","LUQ/shoulder tip pain radiating to left shoulder, worse supine","Splenic rupture / diaphragmatic irritation"], ["Dance's sign","Empty RIF on palpation","Intussusception — bowel has moved into the intussusceptum"], ["Courvoisier's sign","Palpable non-tender gallbladder + jaundice","Pancreatic head Ca (NOT gallstone — gallstone causes fibrosis/shrinkage of GB)"]], [CW*0.22,CW*0.38,CW*0.4]),sp(4), h2("INVESTIGATIONS — ACUTE ABDOMINAL PAIN"),ss("IMMEDIATE BLOODS"), tb([["Test","What to Find","Significance"], ["FBC","WCC↑ — infection/inflammation; Hb↓ — haemorrhage","WCC >11 → inflammation; >15 → severe/perforation"], ["U&E","K⁺, Na⁺, creatinine, urea","Dehydration, AKI, Addison's (↓Na, ↑K)"], ["LFTs","Bilirubin↑, ALP↑, ALT/AST↑","Biliary obstruction; hepatitis; pancreatic disease"], ["Amylase / Lipase","Amylase >3× ULN = pancreatitis","Lipase more specific. Can be raised in non-pancreatic causes."], ["CRP","Raised in inflammation","Rises 6–12 hrs after onset; better marker of severity than WCC at 24 hrs"], ["Coagulation","PT/INR, APTT","Liver disease; DIC; coagulopathy"], ["Glucose","Hyperglycaemia — DKA; hypoglycaemia — Addison's","Always check with abdominal pain + altered consciousness"], ["Beta-hCG","Positive in pregnancy","Ectopic until proven otherwise if positive + abdominal pain"], ["Blood cultures","Bacteraemia — cholangitis, appendix perforation, peritonitis","Before antibiotics"], ["Lactate",">2 mmol/L = inadequate perfusion","Mesenteric ischaemia, septic shock, haemorrhage"], ["ABG","pH, base excess, pO2","Metabolic acidosis (DKA, ischaemia, sepsis)"]], [CW*0.22,CW*0.28,CW*0.5])] # RIGHT s+=[ss("URINE & SPECIAL TESTS"), bl("<b>Urinalysis + MSU:</b> Haematuria (ureteric colic, UTI); leucocytes (UTI, appendicitis — can have sterile pyuria)"), bl("<b>Beta-hCG (urine/serum):</b> ALL women of reproductive age with lower abdominal pain"), bl("<b>Urine porphyrins:</b> Suspect acute porphyria (diffuse pain + neuropsychiatric)"), bl("<b>ECG:</b> All patients with epigastric pain — exclude inferior MI"),sp(3), ss("IMAGING"), tb([["Investigation","Indication","Findings / Notes"], ["Erect CXR","Suspected perforation (1st line)","Free air under diaphragm = perforated viscus. Also shows pneumonia (referred pain)."], ["Supine AXR","Obstruction; volvulus; constipation; ischaemia","3-6-9 rule: SB >3cm, LB >6cm, caecum >9cm = dilated. Coffee bean = sigmoid volvulus. Thumbprinting = ischaemia."], ["USS abdomen","Biliary; gynaecological; renal; appendix (thin patient)","Best for gallstones (95% sensitivity). Murphy's USS for cholecystitis. Cannot exclude appendicitis."], ["CT abdomen/pelvis\n+ contrast (GOLD STANDARD)","Unclear diagnosis; severe pain; elderly; immunosuppressed; post-op","Diagnoses most acute abdominal causes. CT = modality of choice for AAA, pancreatitis staging, Ca staging."], ["CT angiography","Suspected mesenteric ischaemia; AAA; GI haemorrhage","Direct arterial/venous visualisation. Can show 'thumbprinting', 'pneumatosis intestinalis'."], ["MRI abdomen","Biliary (MRCP); appendicitis in pregnancy; pelvic pathology","No radiation — preferred in pregnant patients. MRCP = choledocholithiasis."], ["MRCP","Suspected common bile duct stones; biliary stricture","Non-invasive alternative to ERCP for diagnosis"], ["Nuclear medicine\n(HIDA scan)","Suspected acute cholecystitis when USS inconclusive","Biliary excretion — non-filling of GB = cystic duct obstruction"], ["Diagnostic laparoscopy","Undiagnosed acute pain; suspected appendicitis in young women","Therapeutic at same sitting if needed"]], [CW*0.28,CW*0.3,CW*0.42]),sp(3), pe("ERECT CXR is the first investigation for suspected perforation — free air under diaphragm (seen in 70–80% of perforations). If CXR negative but clinical suspicion high → CT abdomen (much more sensitive — detects small amounts of free gas)."),sp(4), h3("ACUTE ABDOMEN — SURGICAL EMERGENCIES"), bd("<b>Definition:</b> Sudden severe abdominal pain requiring immediate surgical assessment and potentially emergency surgery."),sp(2), tb([["Condition","Key Features","Initial Action"], ["Perforated viscus\n(PUD most common)","Sudden-onset severe epigastric pain → generalised; board-like rigidity; absent bowel sounds; free air on CXR","NBM + NG tube + IV fluids + IV antibiotics + urgent surgical review → laparotomy"], ["Ruptured AAA","Severe back/loin pain + pulsatile abdominal mass + haemodynamic collapse. May be misdiagnosed as renal colic.","IV access × 2 + bloods + group & save. CT if stable. Straight to theatre if unstable."], ["Mesenteric ischaemia","Pain out of proportion to examination. AF/atherosclerosis. Metabolic acidosis. Bloody diarrhoea late.","CT angiography urgently. Resuscitate. Anticoagulate. Embolectomy/bowel resection."], ["Acute severe\ncholangitis","Charcot's triad (RUQ pain + fever + jaundice) + Reynolds' pentad (+ hypotension + confusion)","IV antibiotics + ERCP + biliary drainage urgently. HDU admission."], ["Ruptured ectopic\npregnancy","Lower abdominal pain + shock + positive beta-hCG + shoulder tip pain. Silent pelvis on USS.","Immediate resuscitation. Emergency laparotomy or laparoscopic salpingectomy."], ["Intestinal\nobstruction","Colicky pain + vomiting + absolute constipation + distension. AXR shows dilated loops.","NBM + NG tube + IV fluids + electrolyte correction + surgical review."]], [CW*0.22,CW*0.43,CW*0.35])] s.append(PageBreak()) # PAGE 6 — SPECIFIC CONDITIONS # LEFT s+=[h1("SPECIFIC CONDITIONS — HISTORY DISCRIMINATORS"), tb([["Condition","Diagnostic History Features"], ["Appendicitis","Periumbilical pain → migrates to RIF over 4–6 hrs (visceral to somatic). Anorexia + low-grade fever + N&V. Rovsing's positive. Rebound in RIF. WCC ↑, CRP ↑."], ["Acute cholecystitis","Constant RUQ pain after fatty meal. Fever + N&V. Murphy's sign positive. USS: wall thickening + pericholecystic fluid + gallstones. WCC ↑."], ["Biliary colic","Episodic severe RUQ/epigastric pain (15–30 min) after fatty food. Radiates to right scapula. NO fever. Resolves completely. USS: gallstones."], ["Ascending cholangitis","Charcot's triad: RUQ pain + fever + jaundice. Life-threatening if Reynolds' pentad. ERCP + drainage = definitive."], ["Acute pancreatitis","Severe epigastric pain radiating to back. Worse lying flat; relieved leaning forward. N&V. Amylase/lipase >3× ULN. USS first; CT for severity."], ["PUD — gastric ulcer","Epigastric pain DURING or immediately AFTER eating. Weight loss, anorexia (fear of eating). Older patients."], ["PUD — duodenal ulcer","Epigastric pain 2–3 hrs after eating + nocturnal pain (awakens patient). RELIEVED by food/antacids. Younger patients."], ["Perforated peptic ulcer","Sudden-onset severe epigastric pain → generalised. Board-like rigidity. Free air on erect CXR. Emergency laparotomy."], ["Acute diverticulitis","LIF pain + fever + altered bowel habit. Elderly. Localised tenderness/guarding LIF. WCC ↑. CT confirms. IV antibiotics ± drainage."], ["Ureteric colic","Severe loin pain radiating to groin/genitalia. Colicky. Haematuria. Patient writhes. KUB/CT-KUB = gold standard."], ["Ectopic pregnancy","Lower abdominal pain + missed period + shoulder tip pain. Any sexually active woman of reproductive age. Haemodynamic instability = emergency."], ["Mesenteric ischaemia","Pain out of proportion to examination. AF/vascular disease/recent MI. Metabolic acidosis. Bloody diarrhoea = late sign. CT angiography urgently."], ["Ruptured AAA","Sudden severe back/loin pain + collapse. Pulsatile expansile epigastric mass. Haemodynamic instability. 50% dead before hospital."], ["Ovarian cyst torsion","Sudden-onset severe unilateral lower abdominal pain. N&V. Young women. USS shows absent Doppler flow. Emergency surgery."]], [CW*0.27,CW*0.73])] # RIGHT s+=[h4("ALVARADO SCORE — APPENDICITIS"), tb([["Feature","Score"], ["Migratory RIF pain (visceral to somatic shift)","1"], ["Anorexia","1"], ["Nausea / vomiting","1"], ["RIF tenderness","2"], ["Rebound tenderness","1"], ["Elevated temperature (>37.3°C)","1"], ["Leucocytosis (WCC >10 × 10⁹/L)","2"], ["Shift to left (neutrophilia)","1"], ["TOTAL","10"]], [CW*0.7,CW*0.3]),sp(2), bd("<b>Score 1–4:</b> Low probability — CT if uncertainty. <b>Score 5–6:</b> Possible — consider CT/observation. <b>Score 7–10:</b> Probable appendicitis — surgical review + appendicectomy."),sp(3), pe("OSCE TIP: The most common surgical emergency in the world is appendicitis. Classic migration of pain (periumbilical → RIF) occurs in 50–60%. RIF pain + anorexia + fever + N&V = appendicitis until proven otherwise."),sp(4), h1("ACUTE PANCREATITIS — GLASGOW/IMRIE SEVERITY SCORING"), tb([["Criterion","Threshold","Score"], ["PaO2","< 8 kPa","1"], ["Age","> 55 years","1"], ["Neutrophils (WCC)","WCC > 15 × 10⁹/L","1"], ["Calcium (corrected)","< 2.0 mmol/L","1"], ["Renal (urea)","BUN > 16 mmol/L","1"], ["Enzymes (LDH)","LDH > 600 IU/L","1"], ["Albumin","< 32 g/L","1"], ["Glucose","> 10 mmol/L","1"]], [CW*0.4,CW*0.3,CW*0.3]),sp(2), bd("<b>Score ≥3 within 48 hrs = SEVERE pancreatitis</b> → HDU/ICU admission. Revised Atlanta: mild / moderately severe (transient organ failure <48 hrs) / severe (persistent organ failure >48 hrs)."),sp(3), pe("Causes of pancreatitis: GET SMASHED — Gallstones #1 (45%), Ethanol #2 (35%), Trauma, Steroids, Mumps/viruses, Autoimmune, Scorpion sting/hyperlipidaemia, Hypercalcaemia/Hypothermia, ERCP/Emboli, Drugs (azathioprine, thiazides, tetracyclines)."),sp(4), h1("INTESTINAL OBSTRUCTION — KEY POINTS"), tb([["Feature","Small Bowel","Large Bowel"], ["Pain","Central colicky; early vomiting","Lower colicky; late vomiting"], ["Vomiting","Early; bilious","Late; faeculent"], ["Distension","Central (SB loops)","Peripheral (large frame)"], ["AXR","Central valvulae conniventes (full width)","Peripheral haustral folds (partial width)"], ["Commonest cause","Adhesions #1 (70%); then hernia","CRC #1; then diverticular; then volvulus"]], [CW*0.25,CW*0.375,CW*0.375]),sp(3), al("CLOSED-LOOP OBSTRUCTION: segment obstructed at both ends (volvulus, incarcerated hernia). Highest risk of strangulation and perforation. CT urgently. Surgery does not wait.")] s.append(PageBreak()) # PAGE 7 — MANAGEMENT + OSCE Q&A # LEFT s+=[h1("MANAGEMENT — ACUTE ABDOMINAL PAIN"),ss("INITIAL MANAGEMENT — 'DRIP AND SUCK'"), bl("<b>A — Airway:</b> Protect if GCS ≤8; intubate if needed"), bl("<b>B — Breathing:</b> O2 via mask; SpO2 >95%; ABG if severe"), bl("<b>C — Circulation:</b> 2× large-bore IV cannulae; IV fluids (Hartmann's / 0.9% NaCl)"), bl("<b>Monitoring:</b> HR, BP, SpO2, urine output (catheterise → target >0.5 mL/kg/hr)"), bl("<b>Bloods:</b> FBC, U&E, LFTs, amylase, CRP, coag, G&S, blood cultures, lactate, beta-hCG"), bl("<b>NG tube:</b> If vomiting/obstruction — decompress stomach; nil by mouth"), bl("<b>Analgesia:</b> IV morphine 2.5–5mg titrated — does NOT mask surgical signs (evidence-based)"), bl("<b>Antibiotics:</b> If suspected infection — co-amoxiclav IV or piperacillin-tazobactam IV"), bl("<b>Imaging:</b> Erect CXR + AXR (first line); CT abdomen/pelvis (if diagnosis unclear)"), bl("<b>Surgical review:</b> All acute presentations with peritonism or haemodynamic instability"),sp(4), h2("MANAGEMENT BY SPECIFIC CONDITION"), tb([["Condition","Key Management"], ["Appendicitis","Appendicectomy (laparoscopic preferred). Antibiotics pre-op. If perforated: washout + drain ± right hemicolectomy."], ["Acute cholecystitis","IV antibiotics (co-amoxiclav). Laparoscopic cholecystectomy (ideally within 72 hrs of admission — index admission surgery)."], ["Cholangitis","IV antibiotics (piperacillin-tazobactam) + ERCP + biliary drainage within 24 hrs. HDU if Reynolds' pentad."], ["Biliary colic","Analgesia (NSAIDs + opioids). Elective laparoscopic cholecystectomy outpatient."], ["Acute pancreatitis","Aggressive IV fluids (Hartmann's 250 mL/hr). Analgesia. NBM. ERCP if gallstone pancreatitis + cholangitis. ICU if severe."], ["Perforated PU","NBM + NG + IV fluids + IV antibiotics (co-amoxiclav). Emergency laparoscopic / open repair (Graham's omental patch). H. pylori eradication post-op."], ["Ruptured AAA","Call vascular + theatre immediately. O⁻ blood if crashing. EVAR (endovascular) if anatomy suitable. Open repair if unstable/unsuitable."], ["Bowel obstruction","NBM + NG decompression + IV fluids + electrolytes. CT to identify cause and exclude strangulation. Surgery if closed loop/strangulation/failure of conservative."], ["Ureteric colic","Analgesia (NSAIDs 1st line — diclofenac PR; then opioids). IV fluids. CT-KUB confirms. Alpha-blocker (tamsulosin) for spontaneous passage. Urological referral for >5mm or failed passage."], ["Ectopic pregnancy","Resuscitate. Beta-hCG + USS. Methotrexate if haemodynamically stable + small unruptured. Laparoscopic salpingectomy if ruptured/unstable."], ["Mesenteric ischaemia","Resuscitate. CT angiography. Anticoagulation (heparin). Embolectomy/thrombolysis + bowel resection of non-viable bowel. ICU post-op."]], [CW*0.27,CW*0.73])] # RIGHT s+=[h1("OSCE VIVA — RAPID FIRE Q&A"), tb([["Examiner Asks...","Your Answer"], ["3 types of abdominal pain?","Visceral (poorly localised, C-fibres, midline), somatic/parietal (sharp, well-localised, A-delta, worse on movement), referred (distant site, same dermatome)"], ["Pain out of proportion to examination findings?","Mesenteric ischaemia until proven otherwise — CT angiography urgently"], ["Patient writhing vs lying still?","Writhing = colic (biliary/ureteric/intestinal). Lying still = peritonitis (movement aggravates parietal pain)."], ["Murphy's sign — what is it?","RUQ deep palpation during inspiration → patient stops breathing due to pain. Positive in acute cholecystitis."], ["Rovsing's sign?","LIF palpation → pain felt in RIF. Indicates peritoneal irritation from appendicitis."], ["Free air on erect CXR?","Perforated viscus. Commonest cause: perforated peptic ulcer. Emergency laparotomy."], ["Charcot's triad?","RUQ pain + fever + jaundice = ascending cholangitis"], ["Reynolds' pentad?","Charcot's triad + hypotension + confusion = suppurative cholangitis — life-threatening; ERCP + drainage urgently"], ["Courvoisier's sign?","Palpable non-tender gallbladder + jaundice = pancreatic head Ca (NOT gallstone — gallstones cause GB fibrosis/shrinkage)"], ["First investigation for epigastric pain in A&E?","ECG — to exclude inferior MI before assuming GI cause"], ["Pain worse on inspiration (Murphy's)?","Acute cholecystitis"], ["Alvarado score ≥7?","Probable appendicitis — surgical review + appendicectomy"], ["Glasgow/Imrie score ≥3 at 48 hrs?","Severe acute pancreatitis → HDU/ICU"], ["Causes of pancreatitis?","GET SMASHED: Gallstones, Ethanol, Trauma, Steroids, Mumps, Autoimmune, Scorpion/lipids, Hypercalcaemia, ERCP, Drugs"], ["Commonest cause of SBO?","Adhesions (70% in UK) from previous surgery"], ["Commonest cause of LBO?","Colorectal carcinoma #1; diverticular disease #2; volvulus #3"], ["Ectopic + haemodynamically unstable?","Emergency laparoscopic salpingectomy. Resuscitate with IV fluids + O⁻ blood while arranging theatre."], ["Cullen's + Grey Turner's signs?","Retroperitoneal haemorrhage — severe pancreatitis or ruptured AAA"]], [CW*0.42,CW*0.58]),sp(4), gt("FINAL OSCE TIP — For any acute abdominal pain: 1) Assess haemodynamic stability first (shocked = emergency), 2) Characterise using SOCRATES, 3) Always exclude MI (ECG), ectopic (beta-hCG), and perforation (erect CXR) early. 4) CT abdomen/pelvis = the investigation of choice when diagnosis is unclear in acute setting.")] doc.build(s,canvasmaker=Cv) print("Built:",out) build() ENDSCRIPT python3 /home/daytona/workspace/osce-booklets/build_abdpain.py 2>&1

Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.
attachment
attachment

Can u do one like this for constipation

Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.
This is a shared conversation. Sign in to Orris to start your own chat.