I am mbbs student and tomorrow is my 1st day of third year and tomorrow is my posting on dental ward and I don't know about dental history taking and all the other things so please guide me and provide me well formed PDF

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

mkdir -p /home/daytona/workspace/dental-guide && ls /home/daytona/workspace/dent…

Writing File

~/dental-guide/dental_guide.py

#!/usr/bin/env python3
"""Generate a comprehensive dental ward guide PDF for MBBS 3rd year students."""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, PageBreak, ListFlowable, ListItem, KeepTogether
)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.pdfgen import canvas
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate, Frame

OUTPUT_PATH = "/home/daytona/workspace/dental-guide/Dental_Ward_Guide_MBBS_3rdYear.pdf"

# ──────────────────────────────────────────────
# COLORS
# ──────────────────────────────────────────────
DARK_BLUE   = colors.HexColor("#1B3A6B")
MED_BLUE    = colors.HexColor("#2563EB")
LIGHT_BLUE  = colors.HexColor("#DBEAFE")
TEAL        = colors.HexColor("#0D9488")
LIGHT_TEAL  = colors.HexColor("#CCFBF1")
ORANGE      = colors.HexColor("#EA580C")
LIGHT_ORANGE= colors.HexColor("#FED7AA")
RED         = colors.HexColor("#DC2626")
LIGHT_RED   = colors.HexColor("#FEE2E2")
GREEN       = colors.HexColor("#16A34A")
LIGHT_GREEN = colors.HexColor("#DCFCE7")
YELLOW      = colors.HexColor("#CA8A04")
LIGHT_YELLOW= colors.HexColor("#FEF9C3")
GRAY_BG     = colors.HexColor("#F8FAFC")
DARK_GRAY   = colors.HexColor("#374151")
MID_GRAY    = colors.HexColor("#6B7280")
LIGHT_GRAY  = colors.HexColor("#E5E7EB")
WHITE       = colors.white

PAGE_W, PAGE_H = A4
MARGIN = 1.8 * cm


# ──────────────────────────────────────────────
# PAGE NUMBERING
# ──────────────────────────────────────────────
def add_page_number(canvas_obj, doc):
    canvas_obj.saveState()
    canvas_obj.setFont("Helvetica", 8)
    canvas_obj.setFillColor(MID_GRAY)
    page_num = canvas_obj.getPageNumber()
    canvas_obj.drawString(MARGIN, 1.0 * cm, "Dental Ward Guide | MBBS 3rd Year")
    canvas_obj.drawRightString(PAGE_W - MARGIN, 1.0 * cm, f"Page {page_num}")
    # top line
    canvas_obj.setStrokeColor(LIGHT_GRAY)
    canvas_obj.setLineWidth(0.5)
    canvas_obj.line(MARGIN, PAGE_H - 1.4*cm, PAGE_W - MARGIN, PAGE_H - 1.4*cm)
    # bottom line
    canvas_obj.line(MARGIN, 1.4*cm, PAGE_W - MARGIN, 1.4*cm)
    canvas_obj.restoreState()


# ──────────────────────────────────────────────
# STYLES
# ──────────────────────────────────────────────
def make_styles():
    base = getSampleStyleSheet()

    styles = {}

    styles["cover_title"] = ParagraphStyle(
        "cover_title", fontName="Helvetica-Bold", fontSize=28,
        textColor=WHITE, alignment=TA_CENTER, spaceAfter=8, leading=34)

    styles["cover_sub"] = ParagraphStyle(
        "cover_sub", fontName="Helvetica", fontSize=14,
        textColor=colors.HexColor("#BFDBFE"), alignment=TA_CENTER, spaceAfter=6)

    styles["cover_note"] = ParagraphStyle(
        "cover_note", fontName="Helvetica-Oblique", fontSize=11,
        textColor=colors.HexColor("#E0F2FE"), alignment=TA_CENTER)

    styles["h1"] = ParagraphStyle(
        "h1", fontName="Helvetica-Bold", fontSize=16,
        textColor=WHITE, spaceBefore=6, spaceAfter=4, leading=20)

    styles["h2"] = ParagraphStyle(
        "h2", fontName="Helvetica-Bold", fontSize=13,
        textColor=DARK_BLUE, spaceBefore=10, spaceAfter=4, leading=17)

    styles["h3"] = ParagraphStyle(
        "h3", fontName="Helvetica-Bold", fontSize=11,
        textColor=TEAL, spaceBefore=8, spaceAfter=3, leading=14)

    styles["body"] = ParagraphStyle(
        "body", fontName="Helvetica", fontSize=10,
        textColor=DARK_GRAY, leading=15, spaceAfter=4, alignment=TA_JUSTIFY)

    styles["bullet"] = ParagraphStyle(
        "bullet", fontName="Helvetica", fontSize=10,
        textColor=DARK_GRAY, leading=14, spaceAfter=2,
        leftIndent=12, bulletIndent=0)

    styles["bold_bullet"] = ParagraphStyle(
        "bold_bullet", fontName="Helvetica-Bold", fontSize=10,
        textColor=DARK_BLUE, leading=14, spaceAfter=2, leftIndent=12)

    styles["small"] = ParagraphStyle(
        "small", fontName="Helvetica", fontSize=8.5,
        textColor=MID_GRAY, leading=12, spaceAfter=2)

    styles["table_header"] = ParagraphStyle(
        "table_header", fontName="Helvetica-Bold", fontSize=9,
        textColor=WHITE, alignment=TA_CENTER)

    styles["table_cell"] = ParagraphStyle(
        "table_cell", fontName="Helvetica", fontSize=9,
        textColor=DARK_GRAY, leading=12)

    styles["table_cell_bold"] = ParagraphStyle(
        "table_cell_bold", fontName="Helvetica-Bold", fontSize=9,
        textColor=DARK_BLUE, leading=12)

    styles["warning"] = ParagraphStyle(
        "warning", fontName="Helvetica-Bold", fontSize=10,
        textColor=RED, leading=14)

    styles["tip"] = ParagraphStyle(
        "tip", fontName="Helvetica-Oblique", fontSize=9.5,
        textColor=TEAL, leading=13)

    styles["toc_entry"] = ParagraphStyle(
        "toc_entry", fontName="Helvetica", fontSize=10,
        textColor=DARK_GRAY, leading=16, leftIndent=10)

    styles["toc_section"] = ParagraphStyle(
        "toc_section", fontName="Helvetica-Bold", fontSize=11,
        textColor=DARK_BLUE, leading=18, spaceBefore=4)

    return styles


# ──────────────────────────────────────────────
# HELPER: colored box
# ──────────────────────────────────────────────
def section_header_table(title_para, bg_color=DARK_BLUE, w=None):
    """Full-width colored header bar."""
    usable = PAGE_W - 2 * MARGIN
    t = Table([[title_para]], colWidths=[w or usable])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg_color),
        ("ROUNDEDCORNERS", [4, 4, 4, 4]),
        ("TOPPADDING", (0,0), (-1,-1), 7),
        ("BOTTOMPADDING", (0,0), (-1,-1), 7),
        ("LEFTPADDING", (0,0), (-1,-1), 12),
        ("RIGHTPADDING", (0,0), (-1,-1), 12),
    ]))
    return t


def callout_box(content_rows, bg=LIGHT_BLUE, border=MED_BLUE, w=None):
    """Colored callout box."""
    usable = PAGE_W - 2 * MARGIN
    t = Table(content_rows, colWidths=[w or usable])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("BOX", (0,0), (-1,-1), 1.5, border),
        ("ROUNDEDCORNERS", [4, 4, 4, 4]),
        ("TOPPADDING", (0,0), (-1,-1), 6),
        ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("LEFTPADDING", (0,0), (-1,-1), 10),
        ("RIGHTPADDING", (0,0), (-1,-1), 10),
    ]))
    return t


def two_col_table(rows, col_headers, col_widths, hdr_bg=DARK_BLUE):
    """Standard two-or-more-column table."""
    header = [Paragraph(h, ParagraphStyle("th", fontName="Helvetica-Bold",
                fontSize=9, textColor=WHITE, alignment=TA_CENTER)) for h in col_headers]
    data = [header]
    for row in rows:
        data.append([Paragraph(str(c), ParagraphStyle("td", fontName="Helvetica",
                    fontSize=9, textColor=DARK_GRAY, leading=12)) for c in row])
    t = Table(data, colWidths=col_widths, repeatRows=1)
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,0), hdr_bg),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, GRAY_BG]),
        ("GRID", (0,0), (-1,-1), 0.5, LIGHT_GRAY),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("VALIGN", (0,0), (-1,-1), "TOP"),
    ]))
    return t


# ──────────────────────────────────────────────
# DOCUMENT BUILDER
# ──────────────────────────────────────────────
def build_pdf():
    doc = SimpleDocTemplate(
        OUTPUT_PATH,
        pagesize=A4,
        leftMargin=MARGIN, rightMargin=MARGIN,
        topMargin=2.0*cm, bottomMargin=2.0*cm,
        title="Dental Ward Guide - MBBS 3rd Year",
        author="Orris Medical AI",
        subject="Dental History Taking & Ward Basics"
    )

    S = make_styles()
    story = []
    usable = PAGE_W - 2 * MARGIN

    # ════════════════════════════════════════════
    # COVER PAGE
    # ════════════════════════════════════════════
    # Blue gradient-like cover using a table
    cover_data = [
        [Paragraph("🦷", ParagraphStyle("emoji", fontSize=48, alignment=TA_CENTER, spaceAfter=4))],
        [Spacer(1, 0.3*cm)],
        [Paragraph("DENTAL WARD", S["cover_title"])],
        [Paragraph("SURVIVAL GUIDE", ParagraphStyle("ct2", fontName="Helvetica-Bold",
            fontSize=26, textColor=colors.HexColor("#93C5FD"), alignment=TA_CENTER))],
        [Spacer(1, 0.5*cm)],
        [Paragraph("For MBBS 3rd Year Students", S["cover_sub"])],
        [Paragraph("First Day Posting Edition", S["cover_sub"])],
        [Spacer(1, 1.2*cm)],
        [Paragraph("History Taking  |  Examination  |  Common Conditions  |  Drug Chart Tips",
                   S["cover_note"])],
        [Spacer(1, 1.5*cm)],
        [Paragraph("Prepared with content from: Rosen's Emergency Medicine, Scott-Brown's ORL,",
                   ParagraphStyle("src", fontName="Helvetica", fontSize=8.5,
                       textColor=colors.HexColor("#93C5FD"), alignment=TA_CENTER))],
        [Paragraph("Cummings Otolaryngology, Robbins Pathology & Harrison's Medicine",
                   ParagraphStyle("src2", fontName="Helvetica", fontSize=8.5,
                       textColor=colors.HexColor("#93C5FD"), alignment=TA_CENTER))],
        [Spacer(1, 0.8*cm)],
        [Paragraph("July 2026", ParagraphStyle("date", fontName="Helvetica-Oblique",
            fontSize=10, textColor=colors.HexColor("#BFDBFE"), alignment=TA_CENTER))],
    ]
    cover = Table(cover_data, colWidths=[usable])
    cover.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), DARK_BLUE),
        ("TOPPADDING", (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING", (0,0), (-1,-1), 20),
        ("RIGHTPADDING", (0,0), (-1,-1), 20),
        ("ROUNDEDCORNERS", [8,8,8,8]),
    ]))
    story.append(Spacer(1, 2*cm))
    story.append(cover)
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # TABLE OF CONTENTS
    # ════════════════════════════════════════════
    story.append(section_header_table(Paragraph("TABLE OF CONTENTS", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.4*cm))

    toc_items = [
        ("1", "Dental History Taking (Step-by-Step)", "3"),
        ("2", "Clinical Examination of the Oral Cavity", "5"),
        ("3", "Dental Pain - Differential Diagnosis", "7"),
        ("4", "Common Dental Conditions You Will See", "8"),
        ("5", "Dental Charting & Tooth Numbering", "11"),
        ("6", "Ward Essentials - Prescriptions & Investigations", "12"),
        ("7", "Key Drug Doses & Analgesic Ladder", "13"),
        ("8", "Quick Reference Cheat Sheet", "14"),
    ]

    toc_data = [
        [Paragraph(f"Section {n}", S["toc_section"]),
         Paragraph(title, S["toc_entry"]),
         Paragraph(f"Pg {pg}", ParagraphStyle("pg", fontName="Helvetica-Bold",
             fontSize=10, textColor=MED_BLUE, alignment=TA_CENTER))]
        for n, title, pg in toc_items
    ]
    toc_table = Table(toc_data, colWidths=[2.8*cm, 11.5*cm, 1.5*cm])
    toc_table.setStyle(TableStyle([
        ("ROWBACKGROUNDS", (0,0), (-1,-1), [WHITE, GRAY_BG]),
        ("LINEBELOW", (0,0), (-1,-1), 0.5, LIGHT_GRAY),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("VALIGN", (0,0), (-1,-1), "MIDDLE"),
    ]))
    story.append(toc_table)
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # SECTION 1: DENTAL HISTORY TAKING
    # ════════════════════════════════════════════
    story.append(section_header_table(
        Paragraph("SECTION 1: DENTAL HISTORY TAKING", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.3*cm))

    story.append(Paragraph(
        "Dental history taking follows the same SOAP framework as general medicine, "
        "but with specialized questions targeting the teeth, gums, jaw, and related "
        "structures. Always take a full history before examining the patient.",
        S["body"]))
    story.append(Spacer(1, 0.3*cm))

    # 1.1 Chief Complaint
    story.append(section_header_table(
        Paragraph("1.1  Chief Complaint (CC)", S["h1"]), TEAL))
    story.append(Spacer(1, 0.2*cm))
    story.append(callout_box([[Paragraph(
        "<b>Ask:</b> \"What brings you to the dental clinic today?\"<br/>"
        "Record in the patient's own words. Common dental complaints include:<br/>"
        "Toothache / Pain | Swelling of face or gums | Bleeding gums | "
        "Broken/loose tooth | Bad breath (halitosis) | Difficulty chewing or opening mouth | "
        "Sores or ulcers in the mouth | Sensitivity to hot/cold",
        S["body"])]],
        bg=LIGHT_TEAL, border=TEAL))
    story.append(Spacer(1, 0.3*cm))

    # 1.2 History of Presenting Illness
    story.append(Paragraph("1.2  History of Presenting Illness (HPI) - Use SOCRATES", S["h2"]))
    story.append(Paragraph(
        "For any dental pain, use the SOCRATES mnemonic:", S["body"]))

    socrates_data = [
        ["S", "Site", "Which tooth/area? Upper or lower jaw? Right or left side?"],
        ["O", "Onset", "When did it start? Sudden or gradual? After dental procedure?"],
        ["C", "Character", "Sharp? Dull? Throbbing? Burning? Constant or intermittent?"],
        ["R", "Radiation", "Does pain spread to ear, jaw, temple, neck, or opposite side?"],
        ["A", "Associations", "Swelling? Fever? Pus? Bad taste in mouth? Difficulty swallowing?"],
        ["T", "Time course", "How long does the pain last? Getting better or worse?"],
        ["E", "Exacerbating / Relieving",
         "Worse with: hot/cold food? Biting? Sweet food? Better with analgesics?"],
        ["S", "Severity", "Pain score 0-10. Does it wake you from sleep?"],
    ]
    t = Table(socrates_data, colWidths=[0.8*cm, 3.5*cm, usable-0.8*cm-3.5*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (0,-1), DARK_BLUE),
        ("TEXTCOLOR", (0,0), (0,-1), WHITE),
        ("FONTNAME", (0,0), (0,-1), "Helvetica-Bold"),
        ("FONTSIZE", (0,0), (-1,-1), 9),
        ("ROWBACKGROUNDS", (1,0), (-1,-1), [LIGHT_BLUE, WHITE]),
        ("FONTNAME", (1,0), (1,-1), "Helvetica-Bold"),
        ("TEXTCOLOR", (1,0), (1,-1), DARK_BLUE),
        ("GRID", (0,0), (-1,-1), 0.5, LIGHT_GRAY),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("VALIGN", (0,0), (-1,-1), "TOP"),
    ]))
    story.append(t)
    story.append(Spacer(1, 0.3*cm))

    # Important Clinical Distinctions box
    story.append(callout_box([[Paragraph(
        "<b>KEY CLINICAL DISTINCTION (from Rosen's Emergency Medicine):</b><br/>"
        "β€’ <b>Reversible pulpitis:</b> Pain triggered by hot/cold; stops when stimulus removed<br/>"
        "β€’ <b>Irreversible pulpitis:</b> Spontaneous throbbing pain; lingers after stimulus; "
        "may radiate<br/>"
        "β€’ <b>Periapical abscess:</b> Exquisite tenderness to percussion of the tooth",
        S["body"])]],
        bg=LIGHT_YELLOW, border=YELLOW))
    story.append(Spacer(1, 0.3*cm))

    # 1.3 Past Dental History
    story.append(Paragraph("1.3  Past Dental History (PDH)", S["h2"]))
    past_dental = [
        ("Previous dental treatment", "Fillings, root canals, extractions, orthodontics, dentures"),
        ("Last dental visit", "When was it? Why?"),
        ("Dental trauma", "Any previous injury to teeth or jaw?"),
        ("Anaesthetic reactions", "Any problems with local anaesthesia (LA) before?"),
        ("Oral hygiene habits", "How often do they brush? Floss? Mouthwash?"),
        ("Fluoride exposure", "Fluoride toothpaste? Fluoride supplements in childhood?"),
    ]
    pdh_table = two_col_table(
        past_dental,
        ["Question Area", "What to Ask"],
        [5.5*cm, usable - 5.5*cm]
    )
    story.append(pdh_table)
    story.append(Spacer(1, 0.3*cm))

    # 1.4 Past Medical History
    story.append(Paragraph("1.4  Past Medical History (PMH) - Dental Relevance", S["h2"]))
    story.append(Paragraph(
        "Several systemic conditions directly affect dental management and examination findings:",
        S["body"]))

    pmh_data = [
        ["Diabetes mellitus", "Impaired wound healing, increased susceptibility to infection, "
         "poor periodontal health"],
        ["Hypertension", "Many antihypertensives cause gingival hyperplasia (e.g. amlodipine, nifedipine)"],
        ["Cardiac disease / valve replacement",
         "Infective endocarditis prophylaxis - ask about rheumatic heart disease, "
         "prosthetic valves (amoxicillin 2g before procedure)"],
        ["Bleeding disorders / anticoagulants",
         "Warfarin, heparin, aspirin, clopidogrel - risk of post-extraction bleeding"],
        ["Bisphosphonate therapy",
         "Osteonecrosis of the jaw (ONJ) risk after dental extractions"],
        ["Immunosuppression (HIV, steroids, chemotherapy)",
         "Oral candidiasis, ANUG, delayed healing"],
        ["Epilepsy",
         "Phenytoin causes gingival hyperplasia"],
        ["SjΓΆgren's syndrome",
         "Xerostomia (dry mouth) leading to rampant dental caries"],
        ["Pregnancy",
         "Pregnancy gingivitis; avoid elective procedures in first trimester"],
    ]
    pmh_table = two_col_table(pmh_data,
        ["Medical Condition", "Dental Relevance"],
        [4.5*cm, usable - 4.5*cm], hdr_bg=ORANGE)
    story.append(pmh_table)
    story.append(Spacer(1, 0.3*cm))

    # 1.5 Drug History
    story.append(Paragraph("1.5  Drug History", S["h2"]))
    story.append(callout_box([[Paragraph(
        "<b>Always ask about:</b> Current medications (especially anticoagulants, "
        "antihypertensives, bisphosphonates, steroids, immunosuppressants) | "
        "<b>Allergies</b> (especially penicillin/amoxicillin - very common in dental practice) | "
        "<b>OTC drugs</b> (aspirin, NSAIDs - bleeding risk) | "
        "<b>Herbal supplements</b> (garlic, ginkgo - antiplatelet effects)",
        S["body"])]],
        bg=LIGHT_RED, border=RED))
    story.append(Spacer(1, 0.3*cm))

    # 1.6 Family, Social, Systemic Review
    story.append(Paragraph("1.6  Family History, Social History & Systemic Review", S["h2"]))
    fss_data = [
        ["Family History",
         "Dental malocclusion, cleft palate, hereditary gingival fibromatosis, "
         "amelogenesis/dentinogenesis imperfecta"],
        ["Social History",
         "Smoking (periodontal disease, oral cancer risk) | Alcohol (oral cancer, "
         "xerostomia) | Diet (frequency of sugar intake - caries risk) | Occupation | "
         "Stress (bruxism/TMJ)"],
        ["Systemic Review",
         "Dysphagia | Trismus (difficulty opening mouth) | Neck swelling / lymphadenopathy | "
         "Fever/chills | Unintentional weight loss | Otalgia (referred pain from teeth) | "
         "Paraesthesia of lip/chin (sign of serious pathology)"],
    ]
    fss_table = two_col_table(fss_data,
        ["Category", "Key Points"],
        [3.5*cm, usable - 3.5*cm], hdr_bg=TEAL)
    story.append(fss_table)
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # SECTION 2: CLINICAL EXAMINATION
    # ════════════════════════════════════════════
    story.append(section_header_table(
        Paragraph("SECTION 2: CLINICAL EXAMINATION OF THE ORAL CAVITY", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.3*cm))
    story.append(Paragraph(
        "A dental examination follows a systematic sequence: EXTRA-ORAL first, "
        "then INTRA-ORAL. Always ensure good lighting. Ideally, seat the patient "
        "semi-reclined at 45 degrees.",
        S["body"]))
    story.append(Spacer(1, 0.2*cm))

    # 2.1 Extra-Oral
    story.append(section_header_table(
        Paragraph("2.1  Extra-Oral Examination", S["h1"]), TEAL))
    story.append(Spacer(1, 0.2*cm))

    extra_oral = [
        ["General", "General appearance, nutritional status, pallor, jaundice, cyanosis"],
        ["Face", "Symmetry? Swelling? Sinus tracts? Scars? Bruising? Erythema?"],
        ["Skin", "Rashes, pigmentation (Addison's), vesicles (herpes zoster)"],
        ["Lymph nodes",
         "Palpate: submental, submandibular, pre- & post-auricular, cervical chain, "
         "parotid. Note: size, consistency, tenderness, fixity"],
        ["Lips",
         "Colour, angular cheilitis, herpes labialis (cold sores), cleft lip, "
         "dryness (Sjogren's)"],
        ["TMJ",
         "Palpate joint with patient opening and closing. Note clicks, crepitus, "
         "pain, restricted range (normally 35-50mm between incisors)"],
        ["Parotid & submandibular glands",
         "Enlargement? Tenderness? Note parotitis, Sjogren's, stone obstruction"],
        ["Muscles of mastication",
         "Palpate masseter & temporalis for tenderness (bruxism, TMJ disorder)"],
    ]
    t = two_col_table(extra_oral,
        ["Examination Component", "What to Look For"],
        [4.5*cm, usable - 4.5*cm], hdr_bg=TEAL)
    story.append(t)
    story.append(Spacer(1, 0.3*cm))

    # 2.2 Intra-Oral
    story.append(section_header_table(
        Paragraph("2.2  Intra-Oral Examination (SALTTS Mnemonic)", S["h1"]), MED_BLUE))
    story.append(Spacer(1, 0.2*cm))

    story.append(callout_box([[Paragraph(
        "<b>SALTTS:</b> Soft tissues | Alveolar bone | Lips (inside) | Tongue | Teeth | Saliva",
        S["body"])]],
        bg=LIGHT_BLUE, border=MED_BLUE))
    story.append(Spacer(1, 0.2*cm))

    saltts_data = [
        ["S", "Soft tissues",
         "Inspect all mucosa: buccal, labial, floor of mouth, hard & soft palate. "
         "Look for ulcers, white/red patches, swellings, pigmentation"],
        ["A", "Alveolar bone/Gingiva",
         "Colour (healthy = coral pink), consistency, contour, calculus. "
         "Bleeding on probing? Recession? Pocket depth? Swelling?"],
        ["L", "Lips (inner aspect)",
         "Mucoceles, fibromata, Fordyce spots, labial frenum attachment"],
        ["T", "Tongue",
         "Dorsum: coating, fissures, geographic tongue, ulcers, macroglossia. "
         "Lateral borders: red/white lesion = rule out malignancy. "
         "Ventral surface & floor of mouth: Wharton's duct orifices"],
        ["T", "Teeth",
         "Count teeth present. Note: caries (look for dark/cavitated areas), "
         "mobility (Grade I/II/III), fractures, discolouration, "
         "attrition/erosion/abrasion, crowding/spacing"],
        ["S", "Saliva",
         "Is saliva present and of normal consistency? Xerostomia? "
         "Pooling suggesting poor swallowing?"],
    ]
    t2 = Table(saltts_data, colWidths=[0.8*cm, 3.0*cm, usable - 3.8*cm])
    t2.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (0,-1), MED_BLUE),
        ("TEXTCOLOR", (0,0), (0,-1), WHITE),
        ("FONTNAME", (0,0), (0,-1), "Helvetica-Bold"),
        ("FONTNAME", (1,0), (1,-1), "Helvetica-Bold"),
        ("TEXTCOLOR", (1,0), (1,-1), DARK_BLUE),
        ("ROWBACKGROUNDS", (1,0), (-1,-1), [LIGHT_BLUE, WHITE]),
        ("GRID", (0,0), (-1,-1), 0.5, LIGHT_GRAY),
        ("FONTSIZE", (0,0), (-1,-1), 9),
        ("TOPPADDING", (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
        ("VALIGN", (0,0), (-1,-1), "TOP"),
    ]))
    story.append(t2)
    story.append(Spacer(1, 0.3*cm))

    # 2.3 Specific Dental Tests
    story.append(Paragraph("2.3  Specific Dental Tests", S["h2"]))
    tests_data = [
        ["Percussion test", "Tap tooth with handle of dental mirror. "
         "Pain = periapical periodontitis/abscess (indicates infection has spread beyond root apex)"],
        ["Bite test", "Patient bites on tongue blade; if pain = cracked tooth syndrome or "
         "periapical pathology"],
        ["Thermal sensitivity test",
         "Cold spray or warm water. Response to cold: if brief = reversible pulpitis; "
         "if prolonged/lingering = irreversible pulpitis. No response = necrotic pulp"],
        ["Palpation of the alveolus",
         "Fluctuance = abscess ready for drainage. Hard swelling = cellulitis. "
         "Check buccal sulcus opposite root apex"],
        ["Probing", "Periodontal probe measures pocket depth around each tooth. "
         "Normal < 3mm. Deep pocket > 4mm suggests periodontitis"],
        ["Mobility test",
         "Grade I: slight mobility. Grade II: > 1mm bucco-lingual. "
         "Grade III: mobile in all directions including vertical. "
         "Causes: periodontitis, periapical infection, trauma, tumour"],
    ]
    tests_table = two_col_table(tests_data,
        ["Test", "Interpretation"],
        [4.0*cm, usable - 4.0*cm], hdr_bg=ORANGE)
    story.append(tests_table)
    story.append(Spacer(1, 0.3*cm))

    # Red flag lesions box
    story.append(callout_box([[Paragraph(
        "<b>RED FLAG ORAL LESIONS - Report to Senior Immediately:</b><br/>"
        "β€’ Any white patch (leukoplakia) or red patch (erythroplakia) that does NOT rub off<br/>"
        "β€’ Ulcer present > 3 weeks that has NOT healed<br/>"
        "β€’ Unexplained paraesthesia/numbness of lip or chin<br/>"
        "β€’ Unexplained tooth mobility without periodontal disease<br/>"
        "β€’ Hard, fixed, non-tender lymphadenopathy<br/>"
        "β€’ Any suspicious mass: refer for biopsy",
        S["body"])]],
        bg=LIGHT_RED, border=RED))
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # SECTION 3: DIFFERENTIAL DIAGNOSIS OF DENTAL PAIN
    # ════════════════════════════════════════════
    story.append(section_header_table(
        Paragraph("SECTION 3: DENTAL PAIN - DIFFERENTIAL DIAGNOSIS", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.3*cm))

    story.append(Paragraph(
        "Dental pain is most commonly odontogenic (arising from the tooth or its supporting "
        "structures). However, non-odontogenic causes must always be excluded, especially "
        "when multiple teeth are involved or the pain does not respond to dental treatment.",
        S["body"]))
    story.append(Spacer(1, 0.2*cm))

    dd_data = [
        ["Reversible Pulpitis",
         "Brief pain to cold/sweet; stops when stimulus removed; no spontaneous pain",
         "Filling/restoration"],
        ["Irreversible Pulpitis",
         "Spontaneous, throbbing, lingering pain; worse at night; hard to localize",
         "Root canal or extraction"],
        ["Periapical Abscess",
         "Severe constant pain; exquisitely tender to percussion; possible swelling",
         "Drainage + root canal or extraction"],
        ["Periodontal Abscess",
         "Pain in gum, not the tooth; swelling near gum line; pus from pocket; "
         "tooth tender to lateral pressure",
         "Drainage, scaling"],
        ["Pericoronitis",
         "Pain around partially erupted wisdom tooth (3rd molar); "
         "swollen operculum; trismus; halitosis",
         "Irrigation, metronidazole; consider extraction"],
        ["Cracked Tooth",
         "Sharp pain on biting/releasing; thermal sensitivity; hard to localize",
         "Crown, root canal or extraction"],
        ["Dry Socket (Alveolar Osteitis)",
         "Severe throbbing pain 2-4 days after extraction; empty socket; foul smell",
         "Alvogyl dressing; analgesics"],
        ["TMJ Disorder",
         "Pre-auricular pain; jaw clicking; pain on chewing; tender masseter/temporalis",
         "Soft diet, splint, physio"],
        ["Maxillary Sinusitis",
         "Upper posterior tooth pain, bilateral, worse on bending forward; "
         "associated with nasal congestion",
         "Treat sinusitis"],
        ["Trigeminal Neuralgia",
         "Electric shock, lancinating pain; triggered by touch/eating; "
         "no dental cause found",
         "Carbamazepine"],
        ["Salivary Gland Stone (Sialolithiasis)",
         "Swelling under jaw at mealtimes; colicky pain; tender gland",
         "Hydration, massage; surgery if persistent"],
    ]
    dd_table = two_col_table(dd_data,
        ["Diagnosis", "Key Features", "Management"],
        [3.8*cm, 8.0*cm, usable - 11.8*cm], hdr_bg=RED)
    story.append(dd_table)
    story.append(Spacer(1, 0.2*cm))
    story.append(callout_box([[Paragraph(
        "<b>From Harrison's Principles:</b> Dental pathology can cause pain radiating to the ear "
        "(referred otalgia). Bruxism, malocclusion, and temporomandibular disorder may present "
        "with tenderness over the muscular attachments of the jaw muscles.",
        S["tip"])]],
        bg=LIGHT_TEAL, border=TEAL))
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # SECTION 4: COMMON DENTAL CONDITIONS
    # ════════════════════════════════════════════
    story.append(section_header_table(
        Paragraph("SECTION 4: COMMON DENTAL CONDITIONS YOU WILL SEE", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.3*cm))

    # 4.1 Dental Caries
    story.append(section_header_table(
        Paragraph("4.1  Dental Caries (Tooth Decay)", S["h1"]), MED_BLUE))
    story.append(Spacer(1, 0.2*cm))
    story.append(Paragraph(
        "Dental caries is the most common cause of odontogenic pain. It results from the "
        "dissolution of tooth mineral by acids produced by bacterial fermentation of dietary "
        "sugars. Streptococcus mutans is the key pathogen.",
        S["body"]))
    caries_data = [
        ["Stage", "Findings", "Symptoms"],
        ["Initial (White Spot)", "Chalky white spot; no cavitation", "None"],
        ["Enamel caries", "Cavitation confined to enamel", "None or mild sensitivity"],
        ["Dentine caries", "Cavity into dentine", "Sensitivity to sweet/hot/cold"],
        ["Pulpitis", "Caries into pulp", "Spontaneous throbbing pain"],
        ["Necrotic pulp", "Pulp dead", "Pain may subside then return with abscess"],
        ["Periapical abscess", "Infection at root apex", "Severe pain, swelling, fever"],
    ]
    stages_table = Table(
        [[Paragraph(c, ParagraphStyle("th", fontName="Helvetica-Bold",
            fontSize=9, textColor=WHITE if i==0 else DARK_GRAY,
            alignment=TA_CENTER if i==0 else TA_LEFT))
          for c in row]
         for i, row in enumerate(caries_data)],
        colWidths=[3.5*cm, 6.0*cm, usable - 9.5*cm]
    )
    stages_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,0), MED_BLUE),
        ("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_BLUE]),
        ("GRID", (0,0), (-1,-1), 0.5, LIGHT_GRAY),
        ("TOPPADDING", (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING", (0,0), (-1,-1), 6),
    ]))
    story.append(stages_table)
    story.append(Spacer(1, 0.2*cm))

    # 4.2 Periodontal Disease
    story.append(section_header_table(
        Paragraph("4.2  Periodontal Disease (Gingivitis & Periodontitis)", S["h1"]), ORANGE))
    story.append(Spacer(1, 0.2*cm))
    story.append(Paragraph(
        "<b>Gingivitis</b> = Inflammation confined to the gingiva (reversible). "
        "<b>Periodontitis</b> = Inflammation that destroys the periodontal ligament and alveolar "
        "bone (irreversible - leads to tooth loss).",
        S["body"]))
    story.append(Spacer(1, 0.1*cm))
    perio_data = [
        ["Gingivitis",
         "Bleeding on brushing; red, swollen gums; bad breath",
         "Plaque/calculus accumulation; smoking; hormonal (pregnancy)",
         "Scaling & oral hygiene instruction"],
        ["Chronic Periodontitis",
         "Deep pockets > 4mm; bone loss on X-ray; tooth mobility; recession",
         "Chronic bacterial biofilm; smoking; diabetes; genetics",
         "Deep scaling (root planing), antibiotics if severe"],
        ["Acute Necrotizing Ulcerative Gingivitis (ANUG/'Trench Mouth')",
         "Punched-out ulcers of interdental papillae; intense pain; bleeding; "
         "characteristic foul halitosis",
         "Fusobacterium & spirochetes; stress; smoking; immunosuppression (HIV)",
         "Metronidazole 200mg TDS, chlorhexidine rinse, debridement"],
        ["Pericoronitis",
         "Pain & swelling around partially erupted tooth; operculum inflamed; trismus",
         "Impacted/partially erupted third molar (wisdom tooth)",
         "Irrigation under operculum with chlorhexidine; metronidazole if spreading"],
    ]
    perio_table = two_col_table(perio_data,
        ["Condition", "Clinical Features", "Cause", "Treatment"],
        [3.0*cm, 4.5*cm, 3.5*cm, usable-11.0*cm], hdr_bg=ORANGE)
    story.append(perio_table)
    story.append(Spacer(1, 0.3*cm))

    # 4.3 Oral Ulcers
    story.append(section_header_table(
        Paragraph("4.3  Oral Ulcers", S["h1"]), TEAL))
    story.append(Spacer(1, 0.2*cm))
    ulcer_data = [
        ["Aphthous Ulcer\n(most common)",
         "Shallow, round/oval; yellow/grey base with red halo; very painful; "
         "non-infective; heals in 7-14 days",
         "Spontaneous remission; chlorhexidine mouthwash; topical steroids (triamcinolone)"],
        ["Herpetic Stomatitis (HSV-1)",
         "Multiple vesicles that rupture into ulcers; fever; "
         "lymphadenopathy; affects gingiva; children > adults",
         "Aciclovir (if early/severe); analgesics; topical lignocaine for pain"],
        ["Traumatic Ulcer",
         "Single ulcer at site of trauma (denture, sharp tooth, biting cheek); "
         "irregular edge; heals when cause removed",
         "Remove cause; reassess at 2 weeks if not healed"],
        ["Oral Candidiasis\n(Thrush)",
         "White curdy plaques on erythematous base; rub off leaving bleeding surface; "
         "immunosuppressed, infants, denture wearers",
         "Nystatin oral drops or lozenges; fluconazole if severe"],
        ["Malignant Ulcer",
         "Indurated (hard) rolled edges; painless initially; does NOT heal; "
         "> 3 weeks; lateral tongue or floor of mouth most common",
         "URGENT referral for biopsy; surgical excision; radiotherapy"],
    ]
    ulcer_table = two_col_table(ulcer_data,
        ["Type", "Clinical Features", "Management"],
        [3.5*cm, 6.5*cm, usable - 10.0*cm], hdr_bg=TEAL)
    story.append(ulcer_table)
    story.append(Spacer(1, 0.3*cm))

    # 4.4 Swellings
    story.append(section_header_table(
        Paragraph("4.4  Oro-Facial Swellings", S["h1"]), RED))
    story.append(Spacer(1, 0.2*cm))
    swelling_data = [
        ["Dental Abscess", "Tender, fluctuant swelling in buccal sulcus opposite apex; "
         "fistula (sinus tract) may be present",
         "Drain abscess; extract/RCT tooth; antibiotics if systemic features"],
        ["Pericoronitis", "Swelling behind last visible molar; trismus",
         "Irrigation; metronidazole; operculum removal or extraction"],
        ["Cellulitis (spreading infection)",
         "Brawny, non-fluctuant, diffuse; erythema; fever; danger of Ludwig's angina "
         "(bilateral submandibular space - AIRWAY EMERGENCY)",
         "IV antibiotics; hospital admission; surgical drainage; "
         "CALL SENIOR if floor of mouth raised"],
        ["Gingival Hyperplasia",
         "Painless overgrowth of gingiva; drug-induced (phenytoin, nifedipine, ciclosporin) "
         "or idiopathic",
         "Remove causative drug if possible; gingivectomy"],
        ["Ranula",
         "Translucent bluish swelling in floor of mouth; "
         "pseudocyst of sublingual gland",
         "Surgical excision or marsupialisation"],
        ["Epulis",
         "Pedunculated growth from gingival margin; fibrous / vascular / giant cell types",
         "Excision biopsy to rule out malignancy"],
    ]
    swelling_table = two_col_table(swelling_data,
        ["Type", "Features", "Management"],
        [3.5*cm, 6.5*cm, usable - 10.0*cm], hdr_bg=RED)
    story.append(swelling_table)
    story.append(Spacer(1, 0.2*cm))

    story.append(callout_box([[Paragraph(
        "<b>EMERGENCY ALERT - Ludwig's Angina:</b> Bilateral submandibular cellulitis causing "
        "floor of mouth elevation, drooling, inability to swallow, stridor. "
        "This is a life-threatening airway emergency. Call senior IMMEDIATELY. "
        "IV penicillin + metronidazole; prepare for emergency airway management.",
        S["warning"])]],
        bg=LIGHT_RED, border=RED))
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # SECTION 5: TOOTH NUMBERING
    # ════════════════════════════════════════════
    story.append(section_header_table(
        Paragraph("SECTION 5: DENTAL CHARTING & TOOTH NUMBERING", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.3*cm))

    story.append(Paragraph(
        "Two main systems are used in dental practice. The FDI (Two-digit) system is "
        "internationally standard; the Universal numbering system is used in North America.",
        S["body"]))
    story.append(Spacer(1, 0.2*cm))

    # FDI System
    story.append(Paragraph("FDI Two-Digit System (Most Commonly Used in India):", S["h3"]))
    story.append(callout_box([[Paragraph(
        "<b>How it works:</b> First digit = Quadrant (1=Upper Right, 2=Upper Left, 3=Lower Left, 4=Lower Right)<br/>"
        "Second digit = Tooth position (1=central incisor to 8=3rd molar)<br/><br/>"
        "<b>UPPER JAW:</b> 18 17 16 15 14 13 12 11 | 21 22 23 24 25 26 27 28<br/>"
        "<b>LOWER JAW:</b> 48 47 46 45 44 43 42 41 | 31 32 33 34 35 36 37 38<br/><br/>"
        "Example: Tooth 36 = Lower left first molar | Tooth 11 = Upper right central incisor<br/>"
        "Primary teeth: 51-55 (upper right), 61-65 (upper left), 71-75 (lower left), 81-85 (lower right)",
        S["body"])]],
        bg=LIGHT_BLUE, border=MED_BLUE))
    story.append(Spacer(1, 0.2*cm))

    # Tooth Types
    teeth_types_data = [
        ["Incisors (1,2)", "Cutting; single rooted; 4 upper, 4 lower"],
        ["Canines (3)", "Tearing; single root, longest root; 2 upper, 2 lower"],
        ["Premolars (4,5)", "Crushing; 1-2 roots; 4 upper, 4 lower (no premolars in primary)"],
        ["Molars (6,7,8)", "Grinding; multi-rooted; 6 upper (3 roots), 6 lower (2 roots) + 4 wisdom teeth"],
    ]
    teeth_table = two_col_table(teeth_types_data,
        ["Tooth Type", "Function & Anatomy"],
        [4.0*cm, usable - 4.0*cm], hdr_bg=MED_BLUE)
    story.append(teeth_table)
    story.append(Spacer(1, 0.2*cm))

    # Mobility grading
    story.append(Paragraph("Tooth Mobility Grading:", S["h3"]))
    mob_data = [
        ["Grade I", "Slightly more than normal mobility; up to 1mm bucco-lingually"],
        ["Grade II", "Moderately more mobility; > 1mm bucco-lingually; no vertical displacement"],
        ["Grade III", "Severe mobility; moves in all directions including vertically/axially"],
    ]
    mob_table = two_col_table(mob_data,
        ["Grade", "Description"],
        [2.5*cm, usable - 2.5*cm])
    story.append(mob_table)
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # SECTION 6: WARD ESSENTIALS
    # ════════════════════════════════════════════
    story.append(section_header_table(
        Paragraph("SECTION 6: WARD ESSENTIALS - PRESCRIPTIONS & INVESTIGATIONS", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.3*cm))

    # Investigations
    story.append(Paragraph("6.1  Common Investigations in the Dental Ward", S["h2"]))
    inv_data = [
        ["IOPA X-ray (Intra-Oral Periapical)",
         "Single tooth and periapical region; most common dental X-ray; "
         "shows caries, periapical pathology, bone levels, root morphology"],
        ["OPG (Orthopantomogram / Panorex)",
         "Full mouth X-ray; shows all teeth, TMJ, sinuses, mandible; "
         "screening, wisdom teeth, fractures, cysts, TMJ"],
        ["Bitewing X-ray",
         "Interproximal surfaces of posterior teeth; detects interproximal caries and bone levels"],
        ["CBCT (Cone Beam CT)",
         "3D imaging for implant planning, complex pathology, surgical planning"],
        ["Blood Tests",
         "Pre-op: CBC, BT/CT, blood glucose, HIV, Hepatitis B/C serology for surgical cases<br/>"
         "INR (if on warfarin - target < 3.5 for minor oral surgery)<br/>"
         "HbA1c for diabetics"],
        ["Biopsy",
         "Excision or incision biopsy for any suspicious lesion; send in 10% formalin"],
        ["Pus Swab",
         "For culture & sensitivity from abscess before antibiotics if available"],
    ]
    inv_table = two_col_table(inv_data,
        ["Investigation", "Purpose & Notes"],
        [4.5*cm, usable - 4.5*cm], hdr_bg=MED_BLUE)
    story.append(inv_table)
    story.append(Spacer(1, 0.3*cm))

    story.append(Paragraph("6.2  Pre-Operative Considerations for Dental Procedures", S["h2"]))
    preop_data = [
        ["Anticoagulants",
         "Warfarin: check INR (< 3.5 for simple extraction); "
         "do NOT routinely stop warfarin for minor oral surgery<br/>"
         "Aspirin: no need to stop for most minor procedures<br/>"
         "DOAC (rivaroxaban, apixaban): consult haematology for complex surgery"],
        ["Diabetes",
         "Schedule procedure early in the morning; check blood glucose; "
         "ensure patient has eaten appropriately; have glucose available post-op"],
        ["Penicillin Allergy",
         "Document clearly; use clindamycin or metronidazole as alternatives for dental infections"],
        ["Infective Endocarditis (IE) Prophylaxis",
         "Only for high-risk patients (prosthetic valves, prior IE, congenital heart disease):<br/>"
         "Amoxicillin 2g orally 30-60 mins before procedure<br/>"
         "If penicillin allergic: clindamycin 600mg"],
        ["Bisphosphonates/Denosumab",
         "Risk of medication-related osteonecrosis of the jaw (MRONJ); "
         "assess risk vs benefit; consult before any dental extraction"],
    ]
    preop_table = two_col_table(preop_data,
        ["Consideration", "Key Points"],
        [4.0*cm, usable - 4.0*cm], hdr_bg=ORANGE)
    story.append(preop_table)
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # SECTION 7: DRUG DOSES
    # ════════════════════════════════════════════
    story.append(section_header_table(
        Paragraph("SECTION 7: KEY DRUG DOSES & ANALGESIC LADDER", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.3*cm))

    # Analgesic Ladder
    story.append(Paragraph("7.1  Dental Pain - Analgesic Ladder", S["h2"]))
    story.append(callout_box([[Paragraph(
        "<b>STEP 1 (Mild pain):</b> Ibuprofen 400-600mg every 6-8 hrs (with food) OR "
        "Paracetamol 1g every 6 hrs<br/>"
        "<b>STEP 2 (Moderate pain):</b> Ibuprofen 400mg + Paracetamol 1g (combination at scheduled times)<br/>"
        "<b>STEP 3 (Severe pain):</b> Add Diclofenac 50mg TDS or short course opioid (tramadol 50mg TDS)<br/>"
        "<b>Note:</b> NSAIDs given at SCHEDULED intervals are MORE effective than opioids for dental pain. "
        "Do not prescribe opioids for chronic dental problems. "
        "(Source: Rosen's Emergency Medicine)",
        S["body"])]],
        bg=LIGHT_GREEN, border=GREEN))
    story.append(Spacer(1, 0.2*cm))

    # Common drug table
    story.append(Paragraph("7.2  Common Drugs in Dental Practice", S["h2"]))
    drugs_data = [
        ["Amoxicillin", "500mg TDS x 5 days",
         "First-line for dental infections; NOT for simple pulpitis"],
        ["Amoxicillin + Clavulanate\n(Co-Amoxiclav / Augmentin)", "625mg TDS x 5 days",
         "More severe or resistant infections"],
        ["Metronidazole (Flagyl)", "200-400mg TDS x 5 days",
         "Anaerobic infections; ANUG; pericoronitis; avoid alcohol during course"],
        ["Clindamycin", "300mg TDS x 5 days",
         "Penicillin allergy alternative; bone infections; risk of C.diff"],
        ["Chlorhexidine 0.2% mouthwash", "10ml twice daily",
         "Antiseptic rinse; ANUG; post-extraction; gingivitis"],
        ["Benzydamine (Difflam)", "Rinse/gargle 15ml every 3hrs",
         "Anti-inflammatory mouthwash for post-op pain"],
        ["Nystatin suspension", "100,000 units/ml, 1ml QID",
         "Oral candidiasis; hold in mouth then swallow"],
        ["Fluconazole", "150mg single dose or 50mg daily x 7 days",
         "Moderate-severe oral candidiasis"],
        ["Aciclovir", "200mg 5x/day x 5 days (start within 72 hrs)",
         "Primary herpetic gingivostomatitis"],
        ["Lignocaine 2% + Adrenaline 1:80,000", "Local infiltration",
         "Most common dental LA; max 4.4mg/kg lignocaine; avoid adrenaline in CV disease"],
        ["Topical benzocaine 20%", "Apply to gingiva before LA injection",
         "Surface anaesthesia to reduce injection discomfort"],
    ]
    drugs_table = two_col_table(drugs_data,
        ["Drug", "Dose/Regimen", "Notes"],
        [4.5*cm, 4.0*cm, usable - 8.5*cm], hdr_bg=TEAL)
    story.append(drugs_table)
    story.append(Spacer(1, 0.2*cm))

    story.append(callout_box([[Paragraph(
        "<b>Antibiotics in Dentistry (Important!):</b> Antibiotics are NOT indicated for "
        "reversible or irreversible pulpitis (dental pain without systemic signs). "
        "They are indicated only when there is spreading infection, systemic signs "
        "(fever, lymphadenopathy, trismus), or immunocompromised patients. "
        "Definitive treatment is always surgical (drainage/extraction/root canal).",
        S["tip"])]],
        bg=LIGHT_YELLOW, border=YELLOW))
    story.append(PageBreak())

    # ════════════════════════════════════════════
    # SECTION 8: QUICK REFERENCE CHEAT SHEET
    # ════════════════════════════════════════════
    story.append(section_header_table(
        Paragraph("SECTION 8: QUICK REFERENCE CHEAT SHEET", S["h1"]), DARK_BLUE))
    story.append(Spacer(1, 0.3*cm))

    # History checklist
    story.append(Paragraph("HISTORY TAKING CHECKLIST", S["h2"]))
    checklist_items = [
        "[ ] Chief complaint in patient's own words",
        "[ ] SOCRATES for pain: Site / Onset / Character / Radiation / Associations / Time / Exacerbating-Relieving / Severity",
        "[ ] Duration & progression of complaint",
        "[ ] Previous dental treatment for this problem",
        "[ ] Past dental history: extractions, fillings, root canals, dentures",
        "[ ] Past medical history (diabetes, cardiac disease, bleeding disorders, Sjogren's)",
        "[ ] Drug history: anticoagulants, bisphosphonates, amlodipine, phenytoin, steroids",
        "[ ] Allergies (especially penicillin)",
        "[ ] Social: smoking, alcohol, diet/sugar intake, stress (bruxism)",
        "[ ] Family: inherited dental conditions, malocclusion",
        "[ ] Systemic review: dysphagia, trismus, lymphadenopathy, fever, weight loss",
    ]
    for item in checklist_items:
        story.append(Paragraph(item, S["bullet"]))
    story.append(Spacer(1, 0.3*cm))

    # Examination checklist
    story.append(Paragraph("EXAMINATION CHECKLIST", S["h2"]))
    exam_checklist = [
        "[ ] EXTRA-ORAL: General appearance, facial symmetry, swelling, lymph nodes (submental/submandibular/cervical)",
        "[ ] TMJ palpation (clicks, crepitus, pain, restricted opening)",
        "[ ] Parotid & submandibular gland palpation",
        "[ ] Lips: colour, angular cheilitis, herpes labialis, dryness",
        "[ ] INTRA-ORAL (use SALTTS): Soft tissues / Alveolar (gingiva) / Lips (inner) / Tongue / Teeth / Saliva",
        "[ ] Teeth: count, caries, fracture, mobility, discolouration",
        "[ ] Gingiva: colour, contour, bleeding on probing, swelling, recession",
        "[ ] Tongue: dorsum, lateral borders (any white/red patches = biopsy), ventral surface",
        "[ ] Floor of mouth: Wharton's ducts, any swelling/masses",
        "[ ] Hard & soft palate: ulcers, torus palatinus, perforation",
        "[ ] SPECIAL TESTS: Percussion, bite test, thermal, palpation of alveolus",
    ]
    for item in exam_checklist:
        story.append(Paragraph(item, S["bullet"]))
    story.append(Spacer(1, 0.3*cm))

    # Mnemonics box
    story.append(callout_box([[Paragraph(
        "<b>KEY MNEMONICS:</b><br/>"
        "β€’ <b>SOCRATES</b> = Site, Onset, Character, Radiation, Associations, Time course, "
        "Exacerbating/relieving, Severity<br/>"
        "β€’ <b>SALTTS</b> = Soft tissues, Alveolar bone/gingiva, Lips, Tongue, Teeth, Saliva<br/>"
        "β€’ <b>3 P's of Pulpitis</b> = Pain triggered by temperature (Pulpitis); "
        "Pain on Percussion (Periapical); Pus = Abscess<br/>"
        "β€’ <b>Recall RED FLAGS</b> = Ulcer > 3 weeks | White/Red patch | Hard lymph node | "
        "Paraesthesia | Unexplained mobility",
        S["body"])]],
        bg=LIGHT_YELLOW, border=YELLOW))
    story.append(Spacer(1, 0.2*cm))

    # Common presentations summary
    story.append(Paragraph("COMMON PRESENTATIONS AT A GLANCE", S["h2"]))
    pres_data = [
        ["Toothache + cold sensitivity (brief)", "Reversible pulpitis", "Filling"],
        ["Toothache + spontaneous at night", "Irreversible pulpitis", "Root canal / Extraction"],
        ["Tender on tapping tooth", "Periapical periodontitis", "Root canal / Extraction"],
        ["Swelling in gum + fever", "Dental abscess", "Drain + Antibiotics + RCT/EXT"],
        ["Swollen jaw + can't open mouth", "Pericoronitis / Spreading cellulitis",
         "Metronidazole; check for Ludwig's"],
        ["Multiple oral ulcers + fever (child)", "Primary herpetic stomatitis",
         "Aciclovir + supportive"],
        ["White non-scrapable oral patch", "Leukoplakia (potentially malignant)",
         "Urgent biopsy"],
        ["Gum bleeding on brushing", "Gingivitis", "Scaling + OHI"],
        ["Dry socket 2-4 days post-extraction", "Alveolar osteitis",
         "Alvogyl dressing + analgesia"],
        ["Floor of mouth raised + drooling", "Ludwig's Angina - EMERGENCY",
         "IV Antibiotics + Airway management"],
    ]
    pres_table = two_col_table(pres_data,
        ["Presentation", "Likely Diagnosis", "Initial Action"],
        [5.0*cm, 5.0*cm, usable - 10.0*cm], hdr_bg=DARK_BLUE)
    story.append(pres_table)
    story.append(Spacer(1, 0.3*cm))

    # Final note
    story.append(callout_box([[Paragraph(
        "<b>Remember for your first posting:</b> Always wash hands before and after "
        "examining a patient. Use gloves, mask, and protective eyewear. Never attempt "
        "a procedure without supervision. If in doubt - ask your senior. Good luck!",
        ParagraphStyle("final", fontName="Helvetica-Bold", fontSize=11,
            textColor=DARK_BLUE, alignment=TA_CENTER, leading=16))]],
        bg=LIGHT_BLUE, border=MED_BLUE))

    story.append(Spacer(1, 0.5*cm))
    story.append(HRFlowable(width=usable, color=LIGHT_GRAY))
    story.append(Spacer(1, 0.2*cm))
    story.append(Paragraph(
        "Content compiled from: Rosen's Emergency Medicine (9th Ed) | Scott-Brown's ORL Head & Neck Surgery | "
        "Cummings Otolaryngology | Robbins & Kumar Basic Pathology | Harrison's Principles of Internal Medicine 22E | "
        "K.J. Lee's Essential Otolaryngology",
        S["small"]))

    # ──── BUILD ────
    doc.build(story, onFirstPage=add_page_number, onLaterPages=add_page_number)
    print(f"PDF created: {OUTPUT_PATH}")

if __name__ == "__main__":
    build_pdf()
Running Command

cd /home/daytona/workspace/dental-guide && python3 dental_guide.py 2>&1

Running Command

ls -lh /home/daytona/workspace/dental-guide/Dental_Ward_Guide_MBBS_3rdYear.pdf

Your PDF is ready! Download it here:

Dental Ward Guide MBBS 3rdYear

PDF Document Β· PDF


Here is a summary of everything the guide covers so you know exactly what to study tonight:

What's Inside Your Dental Ward Survival Guide (14 pages)

Section 1 - Dental History Taking (Step-by-Step)

  • Chief Complaint - how to record it verbatim
  • SOCRATES mnemonic adapted for dental pain (Site, Onset, Character, Radiation, Associations, Time, Exacerbating/Relieving, Severity)
  • KEY CLINICAL TIP: Reversible pulpitis (pain stops when stimulus removed) vs Irreversible pulpitis (spontaneous throbbing, lingers) vs Periapical abscess (tender on percussion)
  • Past Dental History - what specific questions to ask
  • Past Medical History with dental relevance table - diabetes, cardiac disease, anticoagulants, bisphosphonates, Sjogren's, epilepsy, pregnancy
  • Drug history - which drugs to specifically ask about and why
  • Family/Social/Systemic review including smoking, alcohol, sugar intake, stress (bruxism)

Section 2 - Clinical Examination

  • Extra-oral exam - face, lymph nodes, TMJ, parotid/submandibular glands
  • SALTTS mnemonic for intra-oral: Soft tissues / Alveolar (gingiva) / Lips / Tongue / Teeth / Saliva
  • Special dental tests - percussion test, bite test, thermal sensitivity test, palpation, probing, mobility grading
  • Red flag lesions - when to call your senior immediately

Section 3 - Differential Diagnosis of Dental Pain

  • Full table: Reversible/Irreversible Pulpitis, Periapical Abscess, Periodontal Abscess, Pericoronitis, Cracked tooth, Dry socket, TMJ disorder, Maxillary sinusitis, Trigeminal neuralgia - with features and management

Section 4 - Common Conditions You Will See

  • Dental caries - staged progression table (white spot to periapical abscess)
  • Periodontal disease - gingivitis, periodontitis, ANUG (trench mouth), pericoronitis
  • Oral ulcers - aphthous, herpetic, traumatic, candidiasis, malignant
  • Oro-facial swellings - abscess, cellulitis, gingival hyperplasia, ranula, epulis
  • Ludwig's Angina - emergency alert box

Section 5 - Tooth Numbering (FDI system)

  • Explains the two-digit system used in India

Section 6 - Ward Essentials

  • IOPA, OPG, bitewing X-rays, blood tests, biopsy, swab
  • Pre-op considerations: anticoagulants, diabetes, IE prophylaxis, bisphosphonates

Section 7 - Drug Doses

  • Analgesic ladder for dental pain
  • Full drug table: amoxicillin, metronidazole, clindamycin, chlorhexidine, nystatin, fluconazole, aciclovir, lignocaine

Section 8 - Quick Reference Cheat Sheet

  • History checklist you can run through in the ward
  • Examination checklist
  • Key mnemonics
  • 10 common presentations at a glance (what they are and what to do)

Key thing to remember for tomorrow: For any patient with tooth pain, the most important questions are whether pain is spontaneous vs triggered, and whether the tooth is tender on tapping (percussion). These two answers tell you most of what you need to know. Good luck on your first day!

I dont need pdf , provide all in this page

Got it! Here is everything you need for your first dental ward posting, all in one place.

🦷 Dental Ward Survival Guide - MBBS 3rd Year


SECTION 1: DENTAL HISTORY TAKING


1.1 Chief Complaint (CC)

Ask: "What brings you to the dental clinic today?"
Record it in the patient's own words. Common dental complaints:
  • Toothache / Pain
  • Swelling of face or gums
  • Bleeding gums
  • Broken or loose tooth
  • Bad breath (halitosis)
  • Difficulty chewing or opening mouth
  • Sores or ulcers in the mouth
  • Sensitivity to hot or cold food/drinks

1.2 History of Presenting Illness - Use SOCRATES

For any dental pain, use the SOCRATES mnemonic:
LetterMeaningWhat to Ask
SSiteWhich tooth/area? Upper or lower jaw? Right or left side?
OOnsetWhen did it start? Sudden or gradual? After dental procedure?
CCharacterSharp? Dull? Throbbing? Burning? Constant or intermittent?
RRadiationDoes pain spread to the ear, jaw, temple, neck, or opposite side?
AAssociationsSwelling? Fever? Pus? Bad taste in mouth? Difficulty swallowing?
TTime courseHow long does the pain last? Getting better or worse over time?
EExacerbating / RelievingWorse with hot/cold food? Biting? Sweet food? Better with analgesics?
SSeverityPain score 0-10. Does it wake you from sleep?
KEY CLINICAL DISTINCTION (Rosen's Emergency Medicine):
  • Reversible pulpitis: Pain triggered by hot/cold; stops when stimulus is removed
  • Irreversible pulpitis: Spontaneous throbbing pain; lingers after stimulus; may radiate
  • Periapical abscess: Exquisitely tender on tapping (percussion) of the tooth

1.3 Past Dental History (PDH)

Question AreaWhat to Ask
Previous dental treatmentFillings, root canals, extractions, orthodontics, dentures
Last dental visitWhen was it? Why did they go?
Dental traumaAny previous injury to teeth or jaw?
Anaesthetic reactionsAny problems with local anaesthesia (LA) before?
Oral hygiene habitsHow often do they brush? Floss? Use mouthwash?
Fluoride exposureFluoride toothpaste? Fluoride supplements in childhood?

1.4 Past Medical History (PMH) - Dental Relevance

Several systemic conditions directly affect dental management:
Medical ConditionDental Relevance
Diabetes mellitusImpaired wound healing, increased infection susceptibility, poor periodontal health
HypertensionCalcium channel blockers (amlodipine, nifedipine) cause gingival hyperplasia
Cardiac disease / valve replacementInfective endocarditis prophylaxis needed - ask about rheumatic heart disease, prosthetic valves
Bleeding disorders / anticoagulantsWarfarin, heparin, aspirin, clopidogrel - risk of post-extraction bleeding
Bisphosphonate therapyOsteonecrosis of the jaw (ONJ) risk after dental extractions
Immunosuppression (HIV, steroids, chemo)Oral candidiasis, ANUG, delayed healing
EpilepsyPhenytoin causes gingival hyperplasia
Sjogren's syndromeXerostomia (dry mouth) leading to rampant dental caries
PregnancyPregnancy gingivitis; avoid elective dental procedures in first trimester

1.5 Drug History

Always ask about:
  • Current medications - especially anticoagulants, antihypertensives, bisphosphonates, steroids, immunosuppressants
  • Allergies - especially penicillin/amoxicillin, very common in dental practice
  • OTC drugs (aspirin, NSAIDs - bleeding risk)
  • Herbal supplements (garlic, ginkgo - antiplatelet effects)

1.6 Family History, Social History & Systemic Review

CategoryKey Points
Family HistoryDental malocclusion, cleft palate, hereditary gingival fibromatosis, amelogenesis/dentinogenesis imperfecta
Social HistorySmoking (periodontal disease, oral cancer risk) - Alcohol (oral cancer, xerostomia) - Diet (frequency of sugar intake - caries risk) - Stress (bruxism/TMJ)
Systemic ReviewDysphagia - Trismus (difficulty opening mouth) - Neck swelling/lymphadenopathy - Fever/chills - Unintentional weight loss - Otalgia (referred pain from teeth) - Paraesthesia of lip/chin (sign of serious pathology - REFER URGENTLY)


SECTION 2: CLINICAL EXAMINATION OF THE ORAL CAVITY

Always examine EXTRA-ORAL first, then INTRA-ORAL. Seat the patient semi-reclined at 45 degrees with good lighting. Use gloves, mask, and protective eyewear.

2.1 Extra-Oral Examination

ComponentWhat to Look For
GeneralGeneral appearance, nutritional status, pallor, jaundice, cyanosis
FaceSymmetry? Swelling? Sinus tracts? Scars? Bruising? Erythema?
SkinRashes, pigmentation (Addison's), vesicles (herpes zoster)
Lymph nodesPalpate: submental, submandibular, pre- & post-auricular, cervical chain, parotid. Note size, consistency, tenderness, fixity
LipsColour, angular cheilitis, herpes labialis (cold sores), cleft lip, dryness (Sjogren's)
TMJPalpate joint with patient opening and closing. Note clicks, crepitus, pain, restricted range (normally 35-50mm between incisors)
Parotid & submandibular glandsEnlargement? Tenderness? (parotitis, Sjogren's, stone obstruction)
Muscles of masticationPalpate masseter and temporalis for tenderness (bruxism, TMJ disorder)

2.2 Intra-Oral Examination - Use SALTTS Mnemonic

SALTTS = Soft tissues | Alveolar (gingiva) | Lips (inside) | Tongue | Teeth | Saliva
LetterAreaWhat to Examine
SSoft tissuesInspect all mucosa: buccal, labial, floor of mouth, hard and soft palate. Look for ulcers, white/red patches, swellings, pigmentation
AAlveolar bone/GingivaColour (healthy = coral pink), consistency, contour, calculus. Bleeding on probing? Recession? Pocket depth? Swelling?
LLips (inner aspect)Mucoceles, fibromata, Fordyce spots, labial frenum attachment
TTongueDorsum: coating, fissures, geographic tongue, ulcers, macroglossia. Lateral borders: any red/white lesion = rule out malignancy. Ventral surface and floor of mouth: Wharton's duct orifices
TTeethCount teeth present. Note: caries (dark/cavitated areas), mobility (Grade I/II/III), fractures, discolouration, attrition/erosion/abrasion, crowding/spacing
SSalivaIs saliva present and of normal consistency? Xerostomia? Pooling suggesting poor swallowing?

2.3 Specific Dental Tests

TestHow to Do ItInterpretation
Percussion testTap tooth with handle of dental mirrorPain = periapical periodontitis/abscess - infection has spread beyond root apex
Bite testPatient bites on tongue bladePain = cracked tooth syndrome or periapical pathology
Thermal sensitivityCold spray or warm water on toothBrief pain = reversible pulpitis. Lingering pain = irreversible pulpitis. No response = necrotic pulp
Palpation of alveolusPress finger along buccal sulcus opposite the root apexFluctuance = abscess ready for drainage. Hard swelling = cellulitis
ProbingPeriodontal probe measures pocket depth around each toothNormal < 3mm. Deep pocket > 4mm suggests periodontitis
Mobility testPush tooth bucco-lingually with two finger handlesGrade I: slight. Grade II: > 1mm. Grade III: moves in all directions including vertically

2.4 Red Flag Oral Lesions - Report to Senior Immediately

  • Any white patch (leukoplakia) or red patch (erythroplakia) that does NOT rub off
  • Any ulcer present > 3 weeks that has not healed
  • Unexplained paraesthesia/numbness of lip or chin
  • Unexplained tooth mobility without periodontal disease
  • Hard, fixed, non-tender lymphadenopathy
  • Any suspicious mass - refer for biopsy


SECTION 3: DIFFERENTIAL DIAGNOSIS OF DENTAL PAIN

Dental pain is most commonly odontogenic. Always exclude non-odontogenic causes when multiple teeth are involved or pain does not respond to treatment.
DiagnosisKey Clinical FeaturesManagement
Reversible PulpitisBrief pain to cold/sweet; stops when stimulus removed; no spontaneous painFilling/restoration
Irreversible PulpitisSpontaneous, throbbing, lingering pain; worse at night; hard to localizeRoot canal or extraction
Periapical AbscessSevere constant pain; exquisitely tender to percussion; possible swelling; feverDrainage + root canal or extraction + antibiotics
Periodontal AbscessPain in the gum, not the tooth; swelling near gum line; pus from pocketDrainage, scaling
PericoronitisPain around partially erupted wisdom tooth; swollen operculum; trismus; halitosisIrrigation, metronidazole; consider extraction
Cracked ToothSharp pain on biting then releasing; thermal sensitivity; hard to localizeCrown, root canal, or extraction
Dry Socket (Alveolar Osteitis)Severe throbbing pain 2-4 days after extraction; empty socket; foul smellAlvogyl dressing; analgesics
TMJ DisorderPre-auricular pain; jaw clicking; pain on chewing; tender masseter/temporalisSoft diet, splint, physiotherapy
Maxillary SinusitisUpper posterior tooth pain, bilateral; worse on bending forward; nasal congestionTreat sinusitis (not the teeth)
Trigeminal NeuralgiaElectric shock, lancinating pain; triggered by touch/eating; no dental cause foundCarbamazepine
Salivary Gland StoneSwelling under jaw at mealtimes; colicky pain; tender glandHydration, massage; surgery if persistent
From Harrison's Principles: Dental pathology can cause pain radiating to the ear (referred otalgia). Bruxism, malocclusion, and TMJ disorder may present with tenderness over the jaw muscle attachments.


SECTION 4: COMMON DENTAL CONDITIONS


4.1 Dental Caries (Tooth Decay)

The most common cause of odontogenic pain. Caused by acid from Streptococcus mutans fermenting dietary sugars.
StageFindingsSymptoms
Initial (White Spot)Chalky white area; no cavitationNone
Enamel cariesCavitation confined to enamelNone or very mild sensitivity
Dentine cariesCavity into dentine (yellowish)Sensitivity to sweet, hot, and cold
PulpitisCaries reaching the pulpSpontaneous throbbing pain, worse at night
Necrotic pulpPulp deadPain may subside, then returns with abscess
Periapical abscessInfection at root apexSevere pain, facial swelling, fever, lymphadenopathy
Risk factors: High sugar frequency, poor oral hygiene, xerostomia, Sjogren's, poorly controlled diabetes, fluoride deficiency

4.2 Periodontal Disease

Gingivitis = inflammation confined to the gingiva (reversible) Periodontitis = destroys the periodontal ligament and alveolar bone (irreversible, leads to tooth loss)
ConditionClinical FeaturesCauseTreatment
GingivitisBleeding on brushing; red, swollen gums; bad breathPlaque/calculus; smoking; hormonal (pregnancy)Scaling and oral hygiene instruction
Chronic PeriodontitisDeep pockets > 4mm; bone loss on X-ray; tooth mobility; recessionChronic bacterial biofilm; smoking; diabetes; geneticsDeep scaling (root planing), antibiotics if severe
ANUG (Trench Mouth)Punched-out ulcers of interdental papillae; intense pain; bleeding; characteristic foul halitosisFusobacterium and spirochetes; stress; smoking; immunosuppression (HIV)Metronidazole 200mg TDS; chlorhexidine rinse; debridement
PericoronitisPain and swelling around partially erupted tooth; operculum inflamed; trismus; halitosisImpacted/partially erupted 3rd molar (wisdom tooth)Irrigation under operculum with chlorhexidine; metronidazole if spreading

4.3 Oral Ulcers

TypeClinical FeaturesManagement
Aphthous Ulcer (most common)Shallow, round/oval; yellow/grey base with red halo; very painful; non-infective; heals in 7-14 daysSpontaneous remission; chlorhexidine mouthwash; topical triamcinolone
Herpetic Stomatitis (HSV-1)Multiple vesicles rupturing into ulcers; fever; lymphadenopathy; affects gingiva; children > adultsAciclovir (if early/severe); analgesics; topical lignocaine for pain
Traumatic UlcerSingle ulcer at site of trauma (denture, sharp tooth, biting cheek); irregular edgeRemove cause; reassess at 2 weeks if not healed
Oral Candidiasis (Thrush)White curdy plaques on erythematous base; rub off leaving bleeding surface; immunosuppressed/infants/denture wearersNystatin oral drops/lozenges; fluconazole if severe
Malignant UlcerIndurated (hard) rolled edges; painless initially; does NOT heal; > 3 weeks; lateral tongue or floor of mouth most commonURGENT referral for biopsy

4.4 Oro-Facial Swellings

TypeFeaturesManagement
Dental AbscessTender, fluctuant swelling in buccal sulcus opposite apex; fistula/sinus tract may be presentDrain abscess; extract/root canal; antibiotics if systemic features
PericoronitisSwelling behind last visible molar; trismusIrrigation; metronidazole; operculum removal or extraction
Cellulitis (spreading)Brawny, non-fluctuant, diffuse; erythema; feverIV antibiotics; hospital admission; surgical drainage
Ludwig's AnginaBilateral submandibular swelling; floor of mouth raised; drooling; stridor; cannot swallowAIRWAY EMERGENCY - Call senior IMMEDIATELY
Gingival HyperplasiaPainless overgrowth of gingiva; drug-induced (phenytoin, nifedipine, ciclosporin)Remove causative drug; gingivectomy
RanulaTranslucent bluish swelling in floor of mouth; pseudocyst of sublingual glandSurgical excision or marsupialisation
EpulisPedunculated growth from gingival margin; fibrous/vascular/giant cell typesExcision biopsy to rule out malignancy
LUDWIG'S ANGINA = EMERGENCY: Bilateral submandibular cellulitis causing floor of mouth elevation, drooling, inability to swallow, stridor. IV penicillin + metronidazole. Prepare for emergency airway management. Call your senior immediately.


SECTION 5: TOOTH NUMBERING (FDI System)

The FDI Two-Digit System is the standard in India and internationally.
  • First digit = Quadrant: 1 = Upper Right, 2 = Upper Left, 3 = Lower Left, 4 = Lower Right
  • Second digit = Tooth position: 1 = Central incisor β†’ 8 = Third molar (wisdom tooth)
        UPPER JAW
Right  18 17 16 15 14 13 12 11 | 21 22 23 24 25 26 27 28  Left
       48 47 46 45 44 43 42 41 | 31 32 33 34 35 36 37 38
        LOWER JAW

Examples:
  Tooth 11 = Upper right central incisor
  Tooth 36 = Lower left first molar
  Tooth 48 = Lower right wisdom tooth
Primary (milk) teeth: 51-55 (upper right), 61-65 (upper left), 71-75 (lower left), 81-85 (lower right)
Tooth TypePositionFunction
Incisors (1, 2)Front teethCutting; single-rooted
Canines (3)Corner teethTearing; single root, longest root
Premolars (4, 5)Behind caninesCrushing; 1-2 roots; absent in primary dentition
Molars (6, 7, 8)Back teethGrinding; upper = 3 roots, lower = 2 roots
Tooth Mobility Grading:
  • Grade I: Slightly more than normal; up to 1mm
  • Grade II: > 1mm bucco-lingually; no vertical displacement
  • Grade III: Mobile in ALL directions including vertically


SECTION 6: WARD ESSENTIALS - INVESTIGATIONS

InvestigationWhat It ShowsWhen to Order
IOPA X-ray (Intra-Oral Periapical)Single tooth and periapical region; caries, bone levels, root morphology, periapical pathologyRoutine for individual tooth assessment
OPG (Orthopantomogram/Panorex)Full mouth panoramic view; all teeth, TMJ, sinuses, mandible, cystsScreening, wisdom teeth, fractures, full mouth assessment
Bitewing X-rayInterproximal caries between posterior teeth; bone levelsDetecting early caries between teeth
CBCT (Cone Beam CT)3D imagingImplant planning, complex pathology, surgical cases
Blood TestsCBC, BT/CT, blood glucose, HIV, Hep B/C for surgical cases; INR if on warfarin; HbA1c for diabeticsAll patients undergoing surgery
Pus SwabCulture and sensitivityBefore starting antibiotics in abscess (if available)
BiopsyHistopathologyAny suspicious lesion - send in 10% formalin

Pre-Operative Considerations

ConsiderationKey Points
WarfarinCheck INR - safe for minor surgery if INR < 3.5. Do NOT routinely stop warfarin for simple extraction
Aspirin/ClopidogrelDo not stop for most minor procedures
DOACs (rivaroxaban, apixaban)Consult haematology for complex surgery
DiabetesSchedule early morning; check blood glucose; have glucose available
Penicillin allergyUse clindamycin or metronidazole as alternatives
IE ProphylaxisHigh-risk only (prosthetic valve, prior IE, congenital heart disease): Amoxicillin 2g orally 30-60 mins before procedure. If allergic to penicillin: Clindamycin 600mg
BisphosphonatesRisk of MRONJ (medication-related osteonecrosis of jaw); assess risk vs benefit before any extraction


SECTION 7: KEY DRUG DOSES

Analgesic Ladder for Dental Pain

Step 1 (Mild pain): Ibuprofen 400-600mg every 6-8 hrs with food, OR Paracetamol 1g every 6 hrs Step 2 (Moderate pain): Ibuprofen 400mg + Paracetamol 1g together at scheduled intervals (combination is very effective) Step 3 (Severe pain): Add Diclofenac 50mg TDS or short course of Tramadol 50mg TDS
Important: NSAIDs at scheduled intervals are MORE effective than opioids for dental pain. Do not prescribe opioids for chronic/long-standing dental problems. (Rosen's Emergency Medicine)

Common Drugs in Dental Practice

DrugDoseNotes
Amoxicillin500mg TDS x 5 daysFirst-line for dental infections - NOT for simple pulpitis alone
Co-Amoxiclav (Augmentin)625mg TDS x 5 daysMore severe or resistant infections
Metronidazole (Flagyl)200-400mg TDS x 5 daysAnaerobic infections; ANUG; pericoronitis; avoid alcohol during course
Clindamycin300mg TDS x 5 daysPenicillin allergy alternative; bone infections; watch for C. difficile
Chlorhexidine 0.2% mouthwash10ml twice dailyAntiseptic rinse; post-extraction; ANUG; gingivitis
Benzydamine (Difflam)15ml rinse every 3 hrsAnti-inflammatory mouthwash for post-op pain
Nystatin suspension100,000 units/ml, 1ml QIDOral candidiasis - hold in mouth then swallow
Fluconazole50mg daily x 7 daysModerate-severe oral candidiasis
Aciclovir200mg 5x/day x 5 daysPrimary herpetic gingivostomatitis - start within 72 hours
Lignocaine 2% + Adrenaline 1:80,000Local infiltrationMost common dental LA; max 4.4mg/kg lignocaine; avoid adrenaline in severe CV disease

Antibiotics in Dentistry - Critical Rule: Antibiotics are NOT indicated for reversible or irreversible pulpitis (dental pain without systemic signs). They are indicated ONLY when there is spreading infection, systemic features (fever, lymphadenopathy, trismus), or an immunocompromised patient. Definitive treatment is always surgical - drainage, extraction, or root canal.


SECTION 8: QUICK REFERENCE CHEAT SHEET


History Taking Checklist

  • Chief complaint in patient's own words
  • SOCRATES for pain
  • Duration and progression
  • Previous dental treatment for this problem
  • Past dental history (extractions, fillings, root canals, dentures)
  • Past medical history (diabetes, cardiac, bleeding disorders, Sjogren's)
  • Drug history - anticoagulants, bisphosphonates, amlodipine, phenytoin
  • Allergies (especially penicillin)
  • Social: smoking, alcohol, diet/sugar, stress/bruxism
  • Systemic review: dysphagia, trismus, lymphadenopathy, fever, weight loss

Examination Checklist

  • Extra-oral: General appearance, facial symmetry, swelling, lymph nodes
  • TMJ palpation (clicks, crepitus, pain, restricted opening)
  • Parotid and submandibular gland palpation
  • Intra-oral using SALTTS
  • Teeth: count, caries, fracture, mobility, discolouration
  • Gingiva: colour, bleeding, swelling, recession
  • Tongue lateral borders (any white/red patch = urgent biopsy)
  • Floor of mouth
  • Hard and soft palate
  • Special tests: percussion, bite, thermal, palpation

Key Mnemonics Summary

MnemonicStands For
SOCRATESSite, Onset, Character, Radiation, Associations, Time course, Exacerbating/Relieving, Severity
SALTTSSoft tissues, Alveolar/Gingiva, Lips, Tongue, Teeth, Saliva
3 P's of PulpitisPain to temperature (Pulpitis) - Pain on Percussion (Periapical) - Pus (Abscess)

Common Presentations at a Glance

PresentationLikely DiagnosisWhat to Do First
Toothache + cold sensitivity (brief)Reversible pulpitisRefer for filling
Toothache + spontaneous at nightIrreversible pulpitisRoot canal or extraction
Tender on tapping toothPeriapical periodontitisRoot canal or extraction
Swelling in gum + feverDental abscessDrain + antibiotics + RCT/extraction
Pain behind last tooth + can't open mouthPericoronitisIrrigation + metronidazole
Multiple oral ulcers + fever (child)Primary herpetic stomatitisAciclovir + supportive care
White non-scrapable oral patchLeukoplakia (potentially malignant)Urgent biopsy referral
Gum bleeding on brushingGingivitisScaling + oral hygiene instruction
Severe pain 2-4 days after extraction + empty socketDry socketAlvogyl dressing + analgesia
Floor of mouth raised + drooling + stridorLudwig's Angina - EMERGENCYCall senior NOW + IV antibiotics + airway

Remember for tomorrow: Always wash hands before and after examining a patient. Gloves, mask, and eyewear are mandatory. Never attempt any procedure without your senior's supervision. When in doubt - always ask. Good luck on your first day! 🦷

Content sourced from: Rosen's Emergency Medicine (9th Ed) | Scott-Brown's ORL Head & Neck Surgery | Cummings Otolaryngology | Robbins & Kumar Basic Pathology | Harrison's Principles of Internal Medicine 22E | K.J. Lee's Essential Otolaryngology

Create a printable one-page checklist for your first ward round

Reading File
Writing File

~/dental-guide/ward_round_checklist.py

#!/usr/bin/env python3
"""One-page printable dental ward round checklist for MBBS 3rd year students."""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable
from reportlab.pdfgen import canvas as pdfcanvas

OUTPUT = "/home/daytona/workspace/dental-guide/Dental_Ward_Round_Checklist.pdf"

PAGE_W, PAGE_H = A4
ML = 1.3 * cm   # left margin
MR = 1.3 * cm   # right margin
MT = 1.2 * cm
MB = 1.2 * cm
USABLE = PAGE_W - ML - MR

# ── COLORS ──────────────────────────────────────
NAVY        = colors.HexColor("#1B3A6B")
BLUE        = colors.HexColor("#2563EB")
LIGHT_BLUE  = colors.HexColor("#DBEAFE")
TEAL        = colors.HexColor("#0D9488")
LIGHT_TEAL  = colors.HexColor("#CCFBF1")
ORANGE      = colors.HexColor("#EA580C")
LIGHT_ORANGE= colors.HexColor("#FFF3E0")
RED         = colors.HexColor("#DC2626")
LIGHT_RED   = colors.HexColor("#FEE2E2")
GREEN       = colors.HexColor("#16A34A")
LIGHT_GREEN = colors.HexColor("#DCFCE7")
YELLOW      = colors.HexColor("#D97706")
LIGHT_YELLOW= colors.HexColor("#FFFBEB")
LGRAY       = colors.HexColor("#E5E7EB")
MGRAY       = colors.HexColor("#6B7280")
DGRAY       = colors.HexColor("#1F2937")
WHITE       = colors.white

# ── PARAGRAPH STYLES ────────────────────────────
def S(name, **kw):
    return ParagraphStyle(name, **kw)

HEAD_TITLE = S("ht", fontName="Helvetica-Bold", fontSize=15,
               textColor=WHITE, alignment=TA_CENTER, leading=18)
HEAD_SUB   = S("hs", fontName="Helvetica", fontSize=8,
               textColor=colors.HexColor("#BFDBFE"), alignment=TA_CENTER, leading=11)

SEC_TITLE  = S("st", fontName="Helvetica-Bold", fontSize=7.5,
               textColor=WHITE, alignment=TA_LEFT, leading=10)

ITEM       = S("it", fontName="Helvetica", fontSize=7.5,
               textColor=DGRAY, leading=10)
ITEM_BOLD  = S("ib", fontName="Helvetica-Bold", fontSize=7.5,
               textColor=NAVY, leading=10)
SMALL      = S("sm", fontName="Helvetica", fontSize=6.5,
               textColor=MGRAY, leading=9)
WARN       = S("wn", fontName="Helvetica-Bold", fontSize=7.5,
               textColor=RED, leading=10)
TIP        = S("tp", fontName="Helvetica-Oblique", fontSize=7,
               textColor=TEAL, leading=9)
FOOTER     = S("ft", fontName="Helvetica-Oblique", fontSize=6.5,
               textColor=MGRAY, alignment=TA_CENTER)

# ── HELPER: section header bar ───────────────────
def hdr(title_para, bg, w=None):
    t = Table([[title_para]], colWidths=[w or USABLE])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("TOPPADDING",    (0,0), (-1,-1), 3),
        ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("RIGHTPADDING",  (0,0), (-1,-1), 4),
    ]))
    return t

# ── HELPER: checkbox row ─────────────────────────
BOX = "☐"   # unicode checkbox

def row(text, bold=False, indent=0):
    style = ITEM_BOLD if bold else ITEM
    left  = 4 + indent
    p = Paragraph(f"{BOX}  {text}", S("r", fontName=style.fontName,
        fontSize=style.fontSize, textColor=style.textColor,
        leading=style.leading, leftIndent=left))
    return p

def rows_table(items, col_widths=None, bg_alt=None):
    """Pack a list of Paragraphs into a tight single-col table."""
    data = [[p] for p in items]
    w = col_widths or [USABLE]
    t = Table(data, colWidths=w)
    style_cmds = [
        ("TOPPADDING",    (0,0), (-1,-1), 1.5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 1.5),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 4),
    ]
    if bg_alt:
        for i in range(0, len(data), 2):
            style_cmds.append(("BACKGROUND", (0,i), (-1,i), bg_alt))
    t.setStyle(TableStyle(style_cmds))
    return t

def two_col_section(left_items, right_items, lw, rw, left_hdr, right_hdr,
                    lhdr_bg=NAVY, rhdr_bg=NAVY):
    """Two side-by-side boxed columns."""
    def col_block(hdr_para, hdr_bg, items, w):
        data = [[hdr(hdr_para, hdr_bg, w)]]
        for p in items:
            data.append([p])
        t = Table(data, colWidths=[w])
        t.setStyle(TableStyle([
            ("BOX",           (0,0), (-1,-1), 0.6, LGRAY),
            ("TOPPADDING",    (0,1), (-1,-1), 1.8),
            ("BOTTOMPADDING", (0,1), (-1,-1), 1.8),
            ("LEFTPADDING",   (0,1), (-1,-1), 5),
            ("RIGHTPADDING",  (0,1), (-1,-1), 4),
            ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, colors.HexColor("#F9FAFB")]),
        ]))
        return t
    lblock = col_block(left_hdr,  lhdr_bg, left_items,  lw)
    rblock = col_block(right_hdr, rhdr_bg, right_items, rw)
    outer  = Table([[lblock, rblock]], colWidths=[lw, rw])
    outer.setStyle(TableStyle([
        ("LEFTPADDING",  (0,0), (-1,-1), 0),
        ("RIGHTPADDING", (0,0), (-1,-1), 0),
        ("TOPPADDING",   (0,0), (-1,-1), 0),
        ("BOTTOMPADDING",(0,0), (-1,-1), 0),
        ("VALIGN",       (0,0), (-1,-1), "TOP"),
        ("INNERGRID",    (0,0), (-1,-1), 0, WHITE),
        ("ALIGN",        (1,0), (1,0), "RIGHT"),
        ("LEFTPADDING",  (1,0), (1,0), 3),
    ]))
    return outer

def page_num(c, doc):
    pass  # single page, no page numbers needed


# ════════════════════════════════════════════════
# BUILD
# ════════════════════════════════════════════════
def build():
    doc = SimpleDocTemplate(OUTPUT, pagesize=A4,
        leftMargin=ML, rightMargin=MR, topMargin=MT, bottomMargin=MB,
        title="Dental Ward Round Checklist", author="Orris")

    story = []
    GAP  = Spacer(1, 2.5)
    SGAP = Spacer(1, 1.5)

    # ── HEADER BANNER ────────────────────────────
    banner_data = [
        [Paragraph("🦷  DENTAL WARD ROUND CHECKLIST", HEAD_TITLE)],
        [Paragraph("MBBS 3rd Year  |  First Posting Edition  |  Print & Carry", HEAD_SUB)],
    ]
    banner = Table(banner_data, colWidths=[USABLE])
    banner.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), NAVY),
        ("TOPPADDING",    (0,0), (-1,-1), 5),
        ("BOTTOMPADDING", (0,0), (-1,-1), 5),
        ("LEFTPADDING",   (0,0), (-1,-1), 8),
    ]))
    story.append(banner)
    story.append(GAP)

    # ════════ ROW 1: HISTORY + EXAMINATION (side-by-side) ════════
    LW = USABLE * 0.495
    RW = USABLE - LW - 3

    # --- LEFT: History ---
    hist_items = [
        row("Chief complaint in patient's own words", bold=True),
        row("S – Site: which tooth? Upper/Lower? Right/Left?"),
        row("O – Onset: sudden or gradual? After procedure?"),
        row("C – Character: sharp / throbbing / dull / burning?"),
        row("R – Radiation: to ear, jaw, temple, neck?"),
        row("A – Associations: swelling? Fever? Pus? Taste?"),
        row("T – Time course: duration? Better or worse?"),
        row("E – Exacerbating/Relieving: hot/cold/biting?"),
        row("S – Severity: pain score 0–10? Wakes from sleep?"),
        Paragraph("─── Past Dental History ───", S("pd", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=BLUE, leading=9, leftIndent=4, spaceBefore=2)),
        row("Previous fillings, RCT, extractions, dentures?"),
        row("Last dental visit & reason"),
        row("Problems with local anaesthesia before?"),
        row("Oral hygiene: brushing frequency, flossing?"),
        Paragraph("─── Medical, Drug & Social ───", S("pd2", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=BLUE, leading=9, leftIndent=4, spaceBefore=2)),
        row("Diabetes / Cardiac / Bleeding disorder / HIV?"),
        row("Drugs: anticoagulants, bisphosphonates, phenytoin?"),
        row("ALLERGIES – especially PENICILLIN?", bold=True),
        row("Smoking / Alcohol / Sugar diet / Stress/Bruxism?"),
        row("FH: malocclusion, cleft palate, inherited disease?"),
        Paragraph("─── Systemic Review ───", S("sr", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=BLUE, leading=9, leftIndent=4, spaceBefore=2)),
        row("Dysphagia? Trismus? Lymphadenopathy? Fever?"),
        row("Weight loss? Otalgia? Lip/chin paraesthesia?"),
    ]

    # --- RIGHT: Examination ---
    exam_items = [
        Paragraph("── Extra-Oral ──", S("eo", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=TEAL, leading=9, leftIndent=4)),
        row("General appearance, nutritional status"),
        row("Facial symmetry, swelling, erythema, sinus tracts"),
        row("Lymph nodes: submental, submandibular, cervical"),
        row("Lips: angular cheilitis, herpes labialis, dryness"),
        row("TMJ: clicks, crepitus, restricted opening (35–50mm)"),
        row("Parotid & submandibular glands: size, tenderness"),
        row("Masseter/temporalis: tenderness (bruxism/TMJ)"),
        Paragraph("── Intra-Oral (SALTTS) ──", S("io", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=TEAL, leading=9, leftIndent=4, spaceBefore=2)),
        row("S – Soft tissues: buccal, palate, floor of mouth"),
        row("A – Alveolar/Gingiva: colour, bleeding, pockets"),
        row("L – Lips (inside): mucoceles, frenum attachment"),
        row("T – Tongue: dorsum coating, lateral borders ⚠"),
        row("T – Teeth: caries, fracture, mobility, count"),
        row("S – Saliva: present? Xerostomia?"),
        Paragraph("── Special Tests ──", S("sp", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=TEAL, leading=9, leftIndent=4, spaceBefore=2)),
        row("Percussion test (pain = periapical pathology)"),
        row("Thermal test (brief = reversible; lingering = irreversible)"),
        row("Palpate buccal sulcus (fluctuance = abscess)"),
        row("Bite test (pain on release = cracked tooth)"),
        row("Probing depth (>4mm = periodontitis)"),
        row("Mobility Grade I / II / III"),
    ]

    story.append(two_col_section(
        hist_items, exam_items, LW, RW,
        Paragraph("πŸ“‹  HISTORY TAKING", SEC_TITLE),
        Paragraph("πŸ”  CLINICAL EXAMINATION", SEC_TITLE),
        lhdr_bg=NAVY, rhdr_bg=TEAL
    ))
    story.append(GAP)

    # ════════ ROW 2: DIFFERENTIALS + DRUGS + RED FLAGS (3 cols) ════════
    CW1 = USABLE * 0.34
    CW2 = USABLE * 0.36
    CW3 = USABLE - CW1 - CW2 - 4

    # --- COL 1: Key Differentials ---
    diff_items = [
        row("Cold sensitivity (brief) β†’ Reversible pulpitis"),
        row("Spontaneous night pain β†’ Irreversible pulpitis"),
        row("Pain on percussion β†’ Periapical abscess"),
        row("Gum swelling + pus β†’ Periodontal abscess"),
        row("Wisdom tooth + trismus β†’ Pericoronitis"),
        row("Pain on bite/release β†’ Cracked tooth"),
        row("2–4d post-extraction β†’ Dry socket"),
        row("Jaw click + TMJ pain β†’ TMJ disorder"),
        row("Upper teeth + nasal β†’ Maxillary sinusitis"),
        row("Electric shock pain β†’ Trigeminal neuralgia"),
        Paragraph("Antibiotic rule: Give ONLY if fever /", TIP),
        Paragraph("spreading / trismus / immunosuppressed.", TIP),
        Paragraph("NOT for simple pulpitis!", WARN),
    ]

    # --- COL 2: Key Drug Doses ---
    drug_items = [
        Paragraph("ANALGESICS", S("dh", fontName="Helvetica-Bold", fontSize=7,
            textColor=ORANGE, leading=9, leftIndent=4)),
        row("Paracetamol 1g QID (scheduled)"),
        row("Ibuprofen 400mg TDS (with food)"),
        row("Combo: both together > opioids"),
        Paragraph("ANTIBIOTICS", S("dh2", fontName="Helvetica-Bold", fontSize=7,
            textColor=ORANGE, leading=9, leftIndent=4, spaceBefore=2)),
        row("Amoxicillin 500mg TDS Γ— 5 days"),
        row("Metronidazole 400mg TDS Γ— 5 days"),
        row("Co-Amoxiclav 625mg TDS Γ— 5 days"),
        row("Clindamycin 300mg TDS (pen. allergy)"),
        Paragraph("ANTISEPTIC / TOPICAL", S("dh3", fontName="Helvetica-Bold", fontSize=7,
            textColor=ORANGE, leading=9, leftIndent=4, spaceBefore=2)),
        row("Chlorhexidine 0.2% – 10ml BD rinse"),
        row("Nystatin 100k units/ml, 1ml QID"),
        row("Fluconazole 50mg OD Γ— 7 days"),
        row("Aciclovir 200mg 5Γ—/day Γ— 5 days"),
        Paragraph("IE PROPHYLAXIS (high-risk cardiac only)", S("dh4",
            fontName="Helvetica-Bold", fontSize=7,
            textColor=ORANGE, leading=9, leftIndent=4, spaceBefore=2)),
        row("Amoxicillin 2g PO 30–60 min before procedure"),
        row("If pen-allergic: Clindamycin 600mg PO"),
    ]

    # --- COL 3: Red Flags + Mnemonics ---
    flag_items = [
        Paragraph("⚠  RED FLAGS – CALL SENIOR", S("rf", fontName="Helvetica-Bold",
            fontSize=7, textColor=RED, leading=9, leftIndent=4)),
        row("Ulcer > 3 weeks not healing"),
        row("White/red patch NOT rubbing off"),
        row("Hard fixed lymph node"),
        row("Lip/chin numbness (paraesthesia)"),
        row("Unexplained tooth mobility"),
        row("Suspicious mass β†’ biopsy"),
        Paragraph("", SMALL),
        Paragraph("🚨  LUDWIG'S ANGINA = EMERGENCY", S("lud", fontName="Helvetica-Bold",
            fontSize=7, textColor=RED, leading=10, leftIndent=4)),
        Paragraph("Floor of mouth raised + drooling +", WARN),
        Paragraph("stridor = AIRWAY EMERGENCY", WARN),
        Paragraph("β†’ Call senior + IV Pen + Metronidazole", ITEM),
        Paragraph("", SMALL),
        Paragraph("KEY MNEMONICS", S("mn", fontName="Helvetica-Bold",
            fontSize=7, textColor=NAVY, leading=9, leftIndent=4)),
        Paragraph("SOCRATES  Site / Onset / Character /", TIP),
        Paragraph("Radiation / Associations / Time /", TIP),
        Paragraph("Exacerbating-Relieving / Severity", TIP),
        Paragraph("", SMALL),
        Paragraph("SALTTS  Soft tissues / Alveolar /", TIP),
        Paragraph("Lips / Tongue / Teeth / Saliva", TIP),
        Paragraph("", SMALL),
        Paragraph("3 P's  Temperature Pain (Pulpitis) /", TIP),
        Paragraph("Percussion Pain (Periapical) / Pus", TIP),
    ]

    def single_col_block(hdr_para, hdr_bg, items, w):
        data = [[hdr(hdr_para, hdr_bg, w)]]
        for p in items:
            data.append([p])
        t = Table(data, colWidths=[w])
        t.setStyle(TableStyle([
            ("BOX",           (0,0), (-1,-1), 0.6, LGRAY),
            ("TOPPADDING",    (0,1), (-1,-1), 1.4),
            ("BOTTOMPADDING", (0,1), (-1,-1), 1.4),
            ("LEFTPADDING",   (0,1), (-1,-1), 4),
            ("RIGHTPADDING",  (0,1), (-1,-1), 3),
            ("ROWBACKGROUNDS",(0,1), (-1,-1), [WHITE, colors.HexColor("#F9FAFB")]),
        ]))
        return t

    b1 = single_col_block(Paragraph("⚑  QUICK DIFFERENTIALS", SEC_TITLE), RED,    diff_items, CW1)
    b2 = single_col_block(Paragraph("πŸ’Š  KEY DRUG DOSES",       SEC_TITLE), ORANGE, drug_items, CW2)
    b3 = single_col_block(Paragraph("🚩  RED FLAGS & MNEMONICS", SEC_TITLE), colors.HexColor("#7C3AED"), flag_items, CW3)

    row3 = Table([[b1, b2, b3]], colWidths=[CW1, CW2, CW3])
    row3.setStyle(TableStyle([
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("LEFTPADDING",   (0,0), (-1,-1), 0),
        ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ("TOPPADDING",    (0,0), (-1,-1), 0),
        ("BOTTOMPADDING", (0,0), (-1,-1), 0),
        ("LEFTPADDING",   (1,0), (1,0), 3),
        ("LEFTPADDING",   (2,0), (2,0), 3),
    ]))
    story.append(row3)
    story.append(GAP)

    # ════════ ROW 3: PRE-OP + INVESTIGATIONS + BEFORE YOU START ════════
    PW1 = USABLE * 0.33
    PW2 = USABLE * 0.33
    PW3 = USABLE - PW1 - PW2 - 4

    preop_items = [
        row("INR checked if on warfarin (target < 3.5)", bold=True),
        row("Blood glucose checked for diabetics"),
        row("Allergy status confirmed & documented"),
        row("Consent obtained before procedure"),
        row("Patient fasted if GA/sedation planned"),
        row("Bisphosphonate: risk discussion done"),
        row("IE prophylaxis given if high-risk cardiac"),
        row("Pre-op bloods: CBC, BT/CT, Blood glucose"),
        row("HIV/Hep B/C serology for surgical patients"),
    ]

    inv_items = [
        row("IOPA X-ray: periapical pathology, bone levels"),
        row("OPG: wisdom teeth, fractures, cysts, full mouth"),
        row("Bitewing: interproximal caries detection"),
        row("CBCT: implants, complex surgery (3D)"),
        Paragraph("── Biopsy ──", S("bx", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=TEAL, leading=9, leftIndent=4, spaceBefore=2)),
        row("Send in 10% formalin"),
        row("For any suspicious oral lesion"),
        Paragraph("── Swab ──", S("sw", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=TEAL, leading=9, leftIndent=4, spaceBefore=2)),
        row("Pus swab before antibiotics if possible"),
        row("C&S to guide antibiotic choice"),
    ]

    basics_items = [
        row("Wash hands before & after every patient", bold=True),
        row("Gloves + Mask + Eye protection = MANDATORY"),
        row("Introduce yourself to patient & senior"),
        row("Carry: pen, notepad, gloves, pocket guide"),
        row("Never attempt procedure without supervision"),
        Paragraph("── On seeing each patient ──", S("op", fontName="Helvetica-Bold",
            fontSize=6.8, textColor=GREEN, leading=9, leftIndent=4, spaceBefore=2)),
        row("Read the case notes before entering"),
        row("Check vitals from nursing chart"),
        row("Note chief complaint & current status"),
        row("Check drug chart: any new prescriptions?"),
        row("Ask senior before writing any prescription"),
        row("Document your findings in the case notes"),
        row("Attend any procedure - observe first"),
    ]

    b4 = single_col_block(Paragraph("βœ…  PRE-OPERATIVE CHECKS", SEC_TITLE),   GREEN,  preop_items,  PW1)
    b5 = single_col_block(Paragraph("πŸ”¬  INVESTIGATIONS",        SEC_TITLE),   BLUE,   inv_items,    PW2)
    b6 = single_col_block(Paragraph("🩺  WARD ROUND BASICS",     SEC_TITLE),   TEAL,   basics_items, PW3)

    row4 = Table([[b4, b5, b6]], colWidths=[PW1, PW2, PW3])
    row4.setStyle(TableStyle([
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("LEFTPADDING",   (0,0), (-1,-1), 0),
        ("RIGHTPADDING",  (0,0), (-1,-1), 0),
        ("TOPPADDING",    (0,0), (-1,-1), 0),
        ("BOTTOMPADDING", (0,0), (-1,-1), 0),
        ("LEFTPADDING",   (1,0), (1,0), 3),
        ("LEFTPADDING",   (2,0), (2,0), 3),
    ]))
    story.append(row4)
    story.append(GAP)

    # ════════ FOOTER STRIP ════════
    footer_data = [[
        Paragraph("Sources: Rosen's Emergency Medicine | Scott-Brown's ORL | "
                  "Harrison's Principles 22E | Robbins Pathology  |  "
                  "Compiled by Orris AI  |  July 2026", FOOTER)
    ]]
    footer_t = Table(footer_data, colWidths=[USABLE])
    footer_t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), colors.HexColor("#F1F5F9")),
        ("TOPPADDING",    (0,0), (-1,-1), 3),
        ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ("BOX",           (0,0), (-1,-1), 0.5, LGRAY),
    ]))
    story.append(footer_t)

    doc.build(story)
    print(f"Done: {OUTPUT}")

if __name__ == "__main__":
    build()
Running Command

cd /home/daytona/workspace/dental-guide && python3 ward_round_checklist.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.
This is a shared conversation. Sign in to Orris to start your own chat.