attachment
attachment
attachment
attachment
attachment
attachment

. According to the syllabus of 3 pages I have just uploaded , pointed, colourful notes, minimum words , use arrows schematic diagram to reduce words . Precise comprehensive pointed notes

analyse_media
analyse_media
analyse_media
Running Command

mkdir -p /tmp/workspace/ear-notes && ls /tmp/workspace/

Running Command

pip install python-pptx --quiet 2>/dev/null; python3 -c "import pptx; print('pptx ok')"

Writing File

~/ear-notes/make_notes.py

from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.util import Inches, Pt
import pptx.oxml.ns as ns
from lxml import etree

prs = Presentation()
prs.slide_width = Inches(13.33)
prs.slide_height = Inches(7.5)

# Color palette
C_BG      = RGBColor(0x0D, 0x1B, 0x2A)   # very dark navy
C_TITLE   = RGBColor(0xFF, 0xD7, 0x00)   # golden yellow
C_HEAD1   = RGBColor(0x00, 0xE5, 0xFF)   # cyan
C_HEAD2   = RGBColor(0xFF, 0x6B, 0x6B)   # coral red
C_HEAD3   = RGBColor(0x7B, 0xFF, 0x6B)   # lime green
C_HEAD4   = RGBColor(0xFF, 0xA5, 0x00)   # orange
C_HEAD5   = RGBColor(0xDA, 0x70, 0xD6)   # orchid / lavender
C_ARROW   = RGBColor(0xFF, 0xD7, 0x00)   # yellow arrows
C_WHITE   = RGBColor(0xFF, 0xFF, 0xFF)
C_GREY    = RGBColor(0xCC, 0xCC, 0xCC)
C_BOX1    = RGBColor(0x1A, 0x3A, 0x5C)   # dark blue box
C_BOX2    = RGBColor(0x2D, 0x1B, 0x3D)   # dark purple box
C_BOX3    = RGBColor(0x1A, 0x3D, 0x2D)   # dark green box
C_BOX4    = RGBColor(0x3D, 0x2A, 0x1A)   # dark orange box

def blank_slide(prs):
    layout = prs.slide_layouts[6]  # blank
    slide = prs.slides.add_slide(layout)
    # Set background
    bg = slide.background
    fill = bg.fill
    fill.solid()
    fill.fore_color.rgb = C_BG
    return slide

def add_text(slide, text, x, y, w, h, font_size=14, bold=False, color=C_WHITE,
             align=PP_ALIGN.LEFT, bg_color=None, wrap=True):
    txBox = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
    if bg_color:
        fill = txBox.fill
        fill.solid()
        fill.fore_color.rgb = bg_color
    tf = txBox.text_frame
    tf.word_wrap = wrap
    p = tf.paragraphs[0]
    p.alignment = align
    run = p.add_run()
    run.text = text
    run.font.size = Pt(font_size)
    run.font.bold = bold
    run.font.color.rgb = color
    return txBox

def add_shape_box(slide, x, y, w, h, bg_color, text, font_size=12, text_color=C_WHITE, bold=False):
    """Rounded rectangle with text"""
    from pptx.util import Emu
    from pptx.enum.shapes import MSO_SHAPE_TYPE
    shape = slide.shapes.add_shape(
        1,  # MSO_SHAPE.RECTANGLE
        Inches(x), Inches(y), Inches(w), Inches(h)
    )
    shape.fill.solid()
    shape.fill.fore_color.rgb = bg_color
    shape.line.color.rgb = C_TITLE
    shape.line.width = Pt(0.75)
    tf = shape.text_frame
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.alignment = PP_ALIGN.CENTER
    run = p.add_run()
    run.text = text
    run.font.size = Pt(font_size)
    run.font.bold = bold
    run.font.color.rgb = text_color
    return shape

def add_arrow(slide, x1, y1, x2, y2):
    """Add a simple line with arrow"""
    connector = slide.shapes.add_connector(1, Inches(x1), Inches(y1), Inches(x2), Inches(y2))
    connector.line.color.rgb = C_ARROW
    connector.line.width = Pt(1.5)

def title_bar(slide, title):
    bar = slide.shapes.add_shape(1, Inches(0), Inches(0), Inches(13.33), Inches(0.65))
    bar.fill.solid()
    bar.fill.fore_color.rgb = RGBColor(0x10, 0x30, 0x50)
    bar.line.fill.background()
    tf = bar.text_frame
    p = tf.paragraphs[0]
    p.alignment = PP_ALIGN.CENTER
    run = p.add_run()
    run.text = title
    run.font.size = Pt(20)
    run.font.bold = True
    run.font.color.rgb = C_TITLE

# ─────────────────────────────────────────────
# SLIDE 1: Title / Overview
# ─────────────────────────────────────────────
slide = blank_slide(prs)
add_text(slide, "πŸ‘‚  EAR  –  SURGERY SYLLABUS NOTES", 0.3, 0.3, 12.7, 0.8,
         font_size=28, bold=True, color=C_TITLE, align=PP_ALIGN.CENTER)
add_text(slide, "Hom UG-Sur-II  β€’  Unit 7", 0.3, 1.0, 12.7, 0.4,
         font_size=14, color=C_GREY, align=PP_ALIGN.CENTER)

# Overview mind-map boxes
topics = [
    ("Anatomy & Physiology\n(7.1–7.2)", 0.4, 1.8, 2.8, 1.0, C_BOX1, C_HEAD1),
    ("External Ear Diseases\n(7.5–7.6)", 3.5, 1.8, 2.8, 1.0, C_BOX2, C_HEAD2),
    ("Middle Ear – ASOM\n(7.7–7.8)", 6.6, 1.8, 2.8, 1.0, C_BOX3, C_HEAD3),
    ("Middle Ear – CSOM\n(7.9–7.10)", 9.7, 1.8, 2.8, 1.0, C_BOX4, C_HEAD4),
    ("Earache / Otalgia\n(7.12–7.13)", 0.4, 3.2, 2.8, 1.0, C_BOX1, C_HEAD1),
    ("Otorrhea\n(7.14–7.15)", 3.5, 3.2, 2.8, 1.0, C_BOX2, C_HEAD5),
    ("Deafness\n(7.16–7.18)", 6.6, 3.2, 2.8, 1.0, C_BOX3, C_HEAD3),
    ("Tinnitus\n(7.19–7.20)", 9.7, 3.2, 2.8, 1.0, C_BOX4, C_HEAD4),
    ("Vertigo\n(7.21–7.23)", 0.4, 4.6, 2.8, 1.0, C_BOX1, C_HEAD1),
    ("Meniere's Disease\n(7.24)", 3.5, 4.6, 2.8, 1.0, C_BOX2, C_HEAD2),
    ("BPPV & Acoustic Neuroma\n(7.25)", 6.6, 4.6, 2.8, 1.0, C_BOX3, C_HEAD5),
    ("Facial Palsy / Bell's Palsy\n(7.26)", 9.7, 4.6, 2.8, 1.0, C_BOX4, C_HEAD4),
]
for (txt, x, y, w, h, bg, fc) in topics:
    add_shape_box(slide, x, y, w, h, bg, txt, font_size=11, text_color=fc, bold=True)

add_text(slide, "Each topic β†’ Aetiology  β†’  Pathology  β†’  Clinical Features  β†’  Investigations  β†’  Management  β†’  Homoeopathic Remedies",
         0.4, 6.0, 12.5, 0.6, font_size=11, color=C_ARROW, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SLIDE 2: Anatomy & Physiology + Ear Examination
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.1–7.4  |  ANATOMY  β€’  PHYSIOLOGY  β€’  CASE TAKING  β€’  EAR EXAMINATION")

# 3 columns
# Col 1: Anatomy
add_text(slide, "πŸ”΅ ANATOMY OF EAR", 0.2, 0.75, 4.1, 0.4, font_size=13, bold=True, color=C_HEAD1)
anat = [
    "External Ear",
    "  ↳ Pinna (auricle)",
    "  ↳ External auditory canal (EAC)",
    "  ↳ Tympanic membrane (TM)",
    "",
    "Middle Ear",
    "  ↳ Ossicles: Malleus β†’ Incus β†’ Stapes",
    "  ↳ Eustachian tube",
    "  ↳ Round & oval windows",
    "",
    "Inner Ear",
    "  ↳ Cochlea (hearing)",
    "  ↳ Vestibule + Semicircular canals (balance)",
]
add_text(slide, "\n".join(anat), 0.2, 1.15, 4.1, 3.8, font_size=10.5, color=C_WHITE)

# Col 2: Physiology
add_text(slide, "🟒 PHYSIOLOGY", 4.6, 0.75, 4.0, 0.4, font_size=13, bold=True, color=C_HEAD3)
phys = [
    "HEARING pathway:",
    "Sound β†’ Pinna β†’ EAC β†’ TM vibrates",
    "  ↓",
    "Ossicles amplify β†’ Oval window",
    "  ↓",
    "Cochlear fluid waves β†’ Hair cells (organ of Corti)",
    "  ↓",
    "CN VIII β†’ Auditory cortex (temporal lobe)",
    "",
    "EQUILIBRIUM:",
    "Semicircular canals β†’ detect rotation",
    "Utricle & Saccule β†’ detect linear accel.",
    "  ↓ β†’ CN VIII β†’ Cerebellum",
]
add_text(slide, "\n".join(phys), 4.6, 1.15, 4.0, 3.8, font_size=10.5, color=C_WHITE)

# Col 3: Case Taking + Exam
add_text(slide, "🟠 CASE TAKING & EAR EXAM", 8.9, 0.75, 4.2, 0.4, font_size=13, bold=True, color=C_HEAD4)
case = [
    "Case Taking (7.3):",
    "  ✦ Chief complaint (otalgia, discharge,",
    "    deafness, tinnitus, vertigo)",
    "  ✦ Duration, onset, character",
    "  ✦ H/o URTI, trauma, noise exposure",
    "  ✦ Family Hx, past Hx",
    "  ✦ Homoeopathic repertorisation",
    "",
    "Ear Examination (7.4):",
    "  ✦ Inspection – pinna, EAC",
    "  ✦ Otoscopy – TM integrity, light reflex",
    "  ✦ Tuning fork tests:",
    "     Rinne's & Weber's",
    "  ✦ DOPS assessment",
]
add_text(slide, "\n".join(case), 8.9, 1.15, 4.2, 3.8, font_size=10.5, color=C_WHITE)

add_text(slide, "Integration β†’ Anatomy (7.1, 7.2)  |  Physiology (7.2)  |  Organ Repertory + Case Taking (7.3)",
         0.2, 6.9, 12.9, 0.4, font_size=9.5, color=C_GREY, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SLIDE 3: External Ear Diseases
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.5–7.6  |  DISEASES OF EXTERNAL EAR")

add_text(slide, "πŸ”΄ CONGENITAL DEFECTS", 0.2, 0.75, 4.1, 0.4, font_size=13, bold=True, color=C_HEAD2)
add_text(slide, "Microtia  β€’  Atresia of EAC\nPre-auricular sinus / tag\nAccessory auricle", 0.2, 1.15, 4.0, 0.9, font_size=11, color=C_WHITE)

add_text(slide, "πŸ”΄ TRAUMA", 0.2, 2.2, 4.1, 0.4, font_size=13, bold=True, color=C_HEAD2)
trauma = "Haematoma auris (cauliflower ear)\nLacerations / avulsion\nForeign body in EAC\n  ↳ Types: Animate / Inanimate\n  ↳ Rx: Ear syringing / forceps / suction"
add_text(slide, trauma, 0.2, 2.6, 4.0, 1.5, font_size=11, color=C_WHITE)

add_text(slide, "πŸ”΄ INFECTIONS", 4.5, 0.75, 4.2, 0.4, font_size=13, bold=True, color=C_HEAD2)
inf = [
    "Otitis Externa (OE):",
    "  Aetiology β†’ Pseudomonas, Staph, fungi",
    "  Features β†’ otalgia, itching, discharge, TM normal",
    "  Rx β†’ aural toilet, antibiotic/antifungal drops",
    "",
    "Malignant OE:",
    "  Pseudomonas β†’ Diabetics",
    "  Spreads to skull base β†’ CN palsies",
    "",
    "Furunculosis:",
    "  Boil in outer 1/3 EAC",
    "  Intense pain, tragus tenderness",
    "  Rx β†’ incision & drainage",
    "",
    "Perichondritis:",
    "  Infection of pinna cartilage",
    "  Cauliflower deformity if untreated",
]
add_text(slide, "\n".join(inf), 4.5, 1.1, 4.2, 5.5, font_size=10.5, color=C_WHITE)

add_text(slide, "🟑 NEOPLASMS", 9.0, 0.75, 4.1, 0.4, font_size=13, bold=True, color=C_HEAD4)
neo = [
    "Benign:",
    "  Exostosis, osteoma (EAC)",
    "  Keloid (pinna)",
    "",
    "Malignant:",
    "  SCC (commonest of pinna)",
    "  BCC",
    "",
    "WAX (Cerumen):",
    "  Impacted wax β†’ conductive deafness",
    "  Rx: Ear drops (olive oil) β†’ syringing",
    "",
    "🟒 HOMOEOPATHIC REMEDIES (7.6):",
    "  Conium, Graphites, Psorinum",
    "  Capsicum, Merc sol, Sulphur",
    "  Belladonna (acute OE)",
    "  Repertory β†’ Synthesis/Radar",
    "",
    "Assess: SAQ | Integration: Materia Medica + Repertory",
]
add_text(slide, "\n".join(neo), 9.0, 1.1, 4.2, 5.5, font_size=10.5, color=C_WHITE)

# ─────────────────────────────────────────────
# SLIDE 4: ASOM
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.7–7.8  |  ACUTE SUPPURATIVE OTITIS MEDIA (ASOM)")

# Aetiology β†’ Pathology β†’ Clinical β†’ Investigation β†’ Management flow
add_text(slide, "ASOM DISEASE FLOW", 0.3, 0.75, 12.7, 0.4, font_size=14, bold=True, color=C_HEAD3, align=PP_ALIGN.CENTER)

boxes = [
    (0.3,  1.3, 2.2, "AETIOLOGY\n───────\nβ€’ Strep pneumoniae\nβ€’ H. influenzae\nβ€’ Moraxella\nβ€’ Via Eustachian tube (URTI)\nβ€’ ↑ incidence in children", C_BOX1, C_HEAD1),
    (2.8,  1.3, 2.2, "PATHOLOGY\n───────\nβ€’ Congestion β†’ Exudate\nβ€’ TM bulges\nβ€’ Perforation β†’ Discharge\nβ€’ Stages:\n  1.Hyperaemia\n  2.Exudation\n  3.Suppuration\n  4.Coalescence\n  5.Complication/Resolution", C_BOX2, C_HEAD5),
    (5.3,  1.3, 2.2, "CLINICAL\nFEATURES\n───────\nβ€’ Otalgia (severe)\nβ€’ Fever\nβ€’ Conductive deafness\nβ€’ Discharge (post-perforation)\nβ€’ TM: congested β†’\n  bulging β†’ perforated", C_BOX3, C_HEAD3),
    (7.8,  1.3, 2.2, "INVESTIGATIONS\n───────\nβ€’ Otoscopy\nβ€’ Audiometry\nβ€’ Tympanometry\nβ€’ Culture & sensitivity\n  (discharge)\nβ€’ X-ray mastoid", C_BOX4, C_HEAD4),
    (10.3, 1.3, 2.6, "MANAGEMENT\n───────\nConservative:\nβ€’ Analgesics, antipyretics\nβ€’ Antibiotics (Amox/Augmentin)\nβ€’ Nasal decongestants\n\nSurgical:\nβ€’ Aural toilet\nβ€’ Myringotomy\n  (if bulging TM)", C_BOX1, C_HEAD1),
]
for (x, y, w, txt, bg, fc) in boxes:
    add_shape_box(slide, x, y, w, 4.8, bg, txt, font_size=10, text_color=fc, bold=False)

# Arrows between boxes
for x_arr in [2.55, 5.05, 7.55, 10.05]:
    add_text(slide, "β†’", x_arr, 3.2, 0.3, 0.4, font_size=16, bold=True, color=C_ARROW, align=PP_ALIGN.CENTER)

add_text(slide, "🟒 HOMOEOPATHIC REMEDIES (7.8): Belladonna  β€’  Hepar sulph  β€’  Merc sol  β€’  Pulsatilla  β€’  Chamomilla  β€’  Capsicum  β€’  Silicea",
         0.3, 6.4, 12.7, 0.55, font_size=11.5, color=C_HEAD3, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SLIDE 5: CSOM
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.9–7.10  |  CHRONIC SUPPURATIVE OTITIS MEDIA (CSOM)")

# Types
add_text(slide, "TYPES OF CSOM", 0.3, 0.78, 12.7, 0.35, font_size=13, bold=True, color=C_TITLE, align=PP_ALIGN.CENTER)

add_shape_box(slide, 0.3, 1.2, 5.8, 1.4, C_BOX3, 
    "SAFE (Tubotympanic / Mucosal Type)\nβ€’ Central perforation  β€’  Mucoid discharge  β€’  No cholesteatoma\nβ€’ Better prognosis", 
    font_size=11, text_color=C_HEAD3)

add_shape_box(slide, 6.5, 1.2, 6.5, 1.4, C_BOX2,
    "UNSAFE (Atticoantral / Squamosal Type)\nβ€’ Marginal / Attic perforation  β€’  Cholesteatoma  β€’  Offensive discharge\nβ€’ Worse prognosis – can lead to COMPLICATIONS",
    font_size=11, text_color=C_HEAD2)

# Arrow
add_text(slide, "⚠ ALWAYS RULE OUT UNSAFE TYPE", 0.3, 2.7, 12.7, 0.35, font_size=12, bold=True, color=C_HEAD2, align=PP_ALIGN.CENTER)

cols = [
    (0.3, 3.1, 3.8, "AETIOLOGY\n───────\nβ€’ Unresolved ASOM\nβ€’ Repeated URTI\nβ€’ Poor hygiene / nutrition\nβ€’ Eustachian tube dysfunction\nβ€’ Cleft palate", C_BOX1, C_HEAD1),
    (4.4, 3.1, 3.8, "CLINICAL FEATURES\n───────\nβ€’ Chronic / intermittent\n  ear discharge\nβ€’ Conductive deafness\nβ€’ Otalgia (mild/absent)\nβ€’ Cholesteatoma β†’ pearly\n  white mass in ear", C_BOX2, C_HEAD5),
    (8.5, 3.1, 4.5, "INVESTIGATIONS\n───────\nβ€’ Otoscopy\nβ€’ Pure tone audiometry\nβ€’ Tympanometry\nβ€’ HRCT temporal bone\n  (cholesteatoma extent)\nβ€’ Swab C&S", C_BOX3, C_HEAD3),
]
for (x, y, w, txt, bg, fc) in cols:
    add_shape_box(slide, x, y, w, 2.0, bg, txt, font_size=10.5, text_color=fc)

add_text(slide, "MANAGEMENT", 0.3, 5.25, 12.7, 0.3, font_size=12, bold=True, color=C_HEAD4, align=PP_ALIGN.CENTER)
mgmt = [
    "Conservative: Aural toilet  β†’  Topical antibiotics  β†’  Treat underlying URTI",
    "Surgical: Tympanoplasty (repair TM)  |  Mastoidectomy (cholesteatoma / unsafe type)",
    "COMPLICATIONS: Mastoiditis  β€’  Intracranial (meningitis, brain abscess, lateral sinus thrombosis)  β€’  Facial nerve palsy",
]
add_text(slide, "\n".join(mgmt), 0.3, 5.6, 12.7, 1.1, font_size=11, color=C_WHITE)

add_text(slide, "🟒 HOMOEOPATHIC REMEDIES (7.10): Merc sol  β€’  Silicea  β€’  Pulsatilla  β€’  Hepar sulph  β€’  Kali mur  β€’  Calc sulph",
         0.3, 6.8, 12.7, 0.4, font_size=11, color=C_HEAD3, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SLIDE 6: Chronic Non-Suppurative Otitis Media
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.11  |  CHRONIC NON-SUPPURATIVE OTITIS MEDIA")

conditions = [
    ("EUSTACHIAN TUBE CATARRH", 0.3, 0.85, 3.8, 5.5, C_BOX1, C_HEAD1,
     "β€’ ET dysfunction\nβ€’ Negative middle ear pressure\nβ€’ Retracted TM\nβ€’ Conductive deafness\n\nFeatures:\n↓ hearing (bilateral)\nEar fullness / popping\n\nRx:\nβ€’ Valsalva manoeuvre\nβ€’ Nasal decongestants\nβ€’ ET balloon dilation"),
    ("SEROUS OTITIS MEDIA\n(Glue Ear)", 4.4, 0.85, 3.8, 5.5, C_BOX2, C_HEAD5,
     "β€’ Non-infected fluid in\n  middle ear\nβ€’ Commonest in children\nβ€’ Adenoid hypertrophy\n  (key risk factor)\n\nFeatures:\nβ€’ Flat TM, blue hue\nβ€’ Conductive deafness\nβ€’ Tympanometry β†’ flat\n\nRx:\nβ€’ Watchful waiting (3 m)\nβ€’ Grommet insertion\nβ€’ Adenoidectomy"),
    ("ATELECTASIS OF TM &\nTYMPANOSCLEROSIS", 8.5, 0.85, 4.5, 5.5, C_BOX3, C_HEAD3,
     "ATELECTASIS:\nβ€’ TM collapsed inward\nβ€’ Risk of cholesteatoma\nβ€’ Retraction pockets\n\nTYMPANOSCLEROSIS:\nβ€’ Calcified plaques on TM\nβ€’ Chalk patches\nβ€’ Conductive deafness\nβ€’ Sequelae of repeated\n  ASOM / grommet use\n\nRx: Myringoplasty if\nsignificant hearing loss"),
]
for (title, x, y, w, h, bg, fc, body) in conditions:
    add_shape_box(slide, x, y, w, 0.5, bg, title, font_size=12, text_color=fc, bold=True)
    add_text(slide, body, x+0.05, y+0.55, w-0.1, h-0.55, font_size=10.5, color=C_WHITE, bg_color=None)

add_text(slide, "All β†’ Conductive Deafness  β€’  Audiometry + Tympanometry are KEY investigations  β€’  Integration: Anatomy",
         0.3, 6.7, 12.7, 0.5, font_size=11, color=C_GREY, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SLIDE 7: Otalgia & Otorrhea
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.12–7.15  |  OTALGIA (EARACHE)  β€’  OTORRHEA")

# Left half: Otalgia
add_text(slide, "πŸ”΄ OTALGIA (7.12–7.13)", 0.3, 0.75, 6.3, 0.4, font_size=14, bold=True, color=C_HEAD2)

otalgia_local = ["LOCAL CAUSES:", "β€’ OE / Furunculosis", "β€’ ASOM / CSOM", "β€’ TM perforation", "β€’ Foreign body EAC"]
otalgia_ref = ["REFERRED PAIN:", "β€’ Dental caries / abscess (V3)", "β€’ Tonsillitis (IX)", "β€’ TMJ dysfunction", "β€’ Cervical spine (C2,3)", "β€’ Laryngeal/pharyngeal Ca"]
add_shape_box(slide, 0.3, 1.2, 2.8, 3.5, C_BOX1, "\n".join(otalgia_local), font_size=11, text_color=C_HEAD1)
add_shape_box(slide, 3.3, 1.2, 3.1, 3.5, C_BOX4, "\n".join(otalgia_ref), font_size=11, text_color=C_HEAD4)

add_text(slide, "Investigation: Full ENT exam + Dental exam  β€’  Ix: OPG, TM assessment, culture",
         0.3, 4.85, 6.3, 0.5, font_size=10, color=C_GREY)
add_text(slide, "🟒 Homoeopathic Remedies (7.13): Chamomilla β€’ Belladonna β€’ Aconite β€’ Pulsatilla β€’ Merc sol",
         0.3, 5.4, 6.3, 0.5, font_size=10.5, color=C_HEAD3)

# Divider
add_text(slide, "β”‚", 6.6, 0.75, 0.2, 6.3, font_size=80, color=RGBColor(0x33,0x55,0x77))

# Right half: Otorrhea
add_text(slide, "🟠 OTORRHEA (7.14–7.15)", 6.9, 0.75, 6.2, 0.4, font_size=14, bold=True, color=C_HEAD4)

otorrhea = [
    "AETIOLOGY:",
    "β€’ ASOM (post-perforation) β†’ purulent",
    "β€’ CSOM β†’ mucopurulent",
    "β€’ OE (diffuse) β†’ serous/purulent",
    "β€’ Cholesteatoma β†’ foul-smelling",
    "β€’ CSF otorrhea β†’ watery (post-trauma)",
    "",
    "CHARACTER β†’ DIAGNOSIS:",
    "Serous/watery  β†’  AOM early / CSF",
    "Mucoid          β†’  OME / CSOM safe",
    "Purulent         β†’  ASOM / OE / CSOM",
    "Blood-stained  β†’  Trauma / malignancy",
    "Foul-smelling  β†’  Cholesteatoma",
    "",
    "INVESTIGATIONS:",
    "β€’ Otoscopy β€’ Culture & sensitivity",
    "β€’ Audiometry β€’ CT (if cholesteatoma)",
    "",
    "MANAGEMENT:",
    "β€’ Aural toilet β†’ topical Abx drops",
    "β€’ Systemic Abx if needed",
    "β€’ Surgical: Myringoplasty / Mastoidectomy",
    "",
    "🟒 Homoeopathic (7.15): Pulsatilla β€’ Merc sol",
    "β€’ Silicea β€’ Graphites β€’ Kali bich β€’ Thuja",
]
add_text(slide, "\n".join(otorrhea), 6.9, 1.15, 6.2, 5.7, font_size=10.5, color=C_WHITE)

# ─────────────────────────────────────────────
# SLIDE 8: Deafness
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.16–7.18  |  DEAFNESS (HEARING LOSS)")

add_text(slide, "TYPES OF DEAFNESS", 0.3, 0.78, 12.7, 0.35, font_size=13, bold=True, color=C_TITLE, align=PP_ALIGN.CENTER)

type_boxes = [
    ("CONDUCTIVE\nDEAFNESS", 0.3, 1.25, 3.8, "β€’ Problem: outer/middle ear\nβ€’ Sound not transmitted\nβ€’ CAUSES:\n  ↳ Wax impaction\n  ↳ TM perforation\n  ↳ Otosclerosis\n  ↳ CSOM / ASOM\n  ↳ OME (glue ear)\n\nTuning Fork:\nRinne –ve\nWeber β†’ affected side", C_BOX1, C_HEAD1),
    ("SENSORINEURAL\nDEAFNESS (SNHL)", 4.4, 1.25, 4.4, "β€’ Problem: cochlea / CN VIII\nβ€’ CAUSES:\n  ↳ Noise-induced (NIHL)\n  ↳ Presbyacusis (aging)\n  ↳ Ototoxic drugs\n     (aminoglycosides, cisplatin)\n  ↳ Meniere's disease\n  ↳ Acoustic neuroma\n  ↳ Viral labyrinthitis\n\nTuning Fork:\nRinne +ve\nWeber β†’ better ear", C_BOX2, C_HEAD5),
    ("MIXED\nDEAFNESS", 9.1, 1.25, 3.9, "β€’ Both conductive +\n  sensorineural components\nβ€’ Common in: chronic\n  otitis media with\n  ossicular damage\n\nTuning Fork:\nRinne –ve\nWeber β†’ better side\n\nAudiometry:\n  Air-bone gap present\n  with elevated bone\n  conduction threshold", C_BOX3, C_HEAD3),
]
for (title, x, y, w, body, bg, fc) in type_boxes:
    add_shape_box(slide, x, y, w, 0.45, bg, title, font_size=12, text_color=fc, bold=True)
    add_text(slide, body, x+0.05, y+0.5, w-0.1, 4.0, font_size=10.5, color=C_WHITE)

add_text(slide, "INVESTIGATIONS  β†’", 0.3, 5.5, 1.6, 0.4, font_size=11, bold=True, color=C_HEAD4)
ix = "Pure Tone Audiometry (PTA)  β€’  Tympanometry  β€’  OAE (Otoacoustic Emissions)  β€’  BERA/ABR  β€’  Speech Audiometry  β€’  Hearing Test (clinical – Whisper, Watch Tick)"
add_text(slide, ix, 2.0, 5.5, 11.0, 0.5, font_size=11, color=C_WHITE)

add_text(slide, "MANAGEMENT  β†’", 0.3, 6.1, 1.6, 0.4, font_size=11, bold=True, color=C_HEAD4)
mgmt = "Conductive: Surgery (myringoplasty, stapedectomy, grommet)  |  SNHL: Hearing aids, cochlear implants  |  Hearing Aids & Implants (C/2)"
add_text(slide, mgmt, 2.0, 6.1, 11.0, 0.5, font_size=11, color=C_WHITE)

add_text(slide, "🟒 Homoeopathic (7.18): China β€’ Graphites β€’ Phosphorus β€’ Merc sol β€’ Kali mur β€’ Causticum β€’ Silicea",
         0.3, 6.75, 12.7, 0.4, font_size=11, color=C_HEAD3, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SLIDE 9: Tinnitus
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.19–7.20  |  TINNITUS")

add_text(slide, "\"Perception of sound WITHOUT external stimulus\"", 0.3, 0.78, 12.7, 0.35,
         font_size=12, bold=True, color=C_TITLE, align=PP_ALIGN.CENTER)

add_text(slide, "TYPES", 0.3, 1.2, 6.3, 0.35, font_size=13, bold=True, color=C_HEAD1)
types_t = [
    ("SUBJECTIVE (commonest)", "β€’ Only patient hears\nβ€’ Damage to auditory pathway\nβ€’ Cochlear/retrocochlear", C_BOX1, C_HEAD1),
    ("OBJECTIVE (rare)", "β€’ Examiner can hear too\nβ€’ Pulsatile (vascular) /\n  clicking (palatal myoclonus)\nβ€’ Stapedial muscle spasm", C_BOX2, C_HEAD5),
]
for i, (title, body, bg, fc) in enumerate(types_t):
    x = 0.3 + i * 3.2
    add_shape_box(slide, x, 1.6, 3.0, 0.4, bg, title, font_size=11, text_color=fc, bold=True)
    add_text(slide, body, x+0.1, 2.05, 2.9, 1.5, font_size=10.5, color=C_WHITE)

add_text(slide, "CAUSES", 0.3, 3.65, 6.3, 0.35, font_size=13, bold=True, color=C_HEAD2)
causes_t = "β€’ Noise trauma (NIHL)\nβ€’ Presbyacusis\nβ€’ Meniere's disease\nβ€’ Otosclerosis\nβ€’ Acoustic neuroma\nβ€’ Ototoxic drugs (aspirin, aminoglycosides)\nβ€’ Anaemia / hypertension (pulsatile)\nβ€’ Wax impaction\nβ€’ ASOM / CSOM"
add_text(slide, causes_t, 0.3, 4.05, 6.2, 2.7, font_size=10.5, color=C_WHITE)

add_text(slide, "INVESTIGATIONS", 6.8, 1.2, 6.3, 0.35, font_size=13, bold=True, color=C_HEAD3)
ix_t = "β€’ Detailed history (character, unilateral/bilateral, pulsatile?)\nβ€’ PTA + audiometry\nβ€’ Tympanometry\nβ€’ MRI IAM (rule out acoustic neuroma)\nβ€’ BP, CBC (rule out medical causes)"
add_text(slide, ix_t, 6.8, 1.6, 6.3, 1.6, font_size=11, color=C_WHITE)

add_text(slide, "MANAGEMENT", 6.8, 3.35, 6.3, 0.35, font_size=13, bold=True, color=C_HEAD4)
mgmt_t = "β€’ Treat underlying cause\nβ€’ Tinnitus retraining therapy (TRT)\nβ€’ Sound therapy / white noise generators\nβ€’ CBT (cognitive behavioural therapy)\nβ€’ Hearing aids (if deafness coexists)\nβ€’ Avoid ototoxins\nβ€’ Reassurance (most cases benign)"
add_text(slide, mgmt_t, 6.8, 3.75, 6.3, 2.6, font_size=11, color=C_WHITE)

add_text(slide, "🟒 Homoeopathic (7.20): China β€’ Graphites β€’ Carboneum sulph β€’ Nat sal β€’ Kali iod β€’ Merc sol β€’ Cinchona",
         0.3, 6.85, 12.7, 0.4, font_size=11, color=C_HEAD3, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SLIDE 10: Vertigo
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.21–7.23  |  VERTIGO")

add_text(slide, "\"Illusion of movement (spinning) – true vestibular vertigo\"", 0.3, 0.78, 12.7, 0.35,
         font_size=12, bold=True, color=C_TITLE, align=PP_ALIGN.CENTER)

# Central vs Peripheral
add_text(slide, "PERIPHERAL VERTIGO", 0.3, 1.2, 6.2, 0.4, font_size=13, bold=True, color=C_HEAD3)
add_text(slide, "CENTRAL VERTIGO", 6.8, 1.2, 6.0, 0.4, font_size=13, bold=True, color=C_HEAD2)

peri_v = [
    "CAUSES:",
    "β€’ BPPV (commonest)",
    "β€’ Meniere's disease",
    "β€’ Vestibular neuritis",
    "β€’ Labyrinthitis",
    "β€’ Acoustic neuroma (late)",
    "",
    "FEATURES:",
    "β€’ Sudden onset",
    "β€’ Episodic / positional",
    "β€’ Nausea + vomiting ++",
    "β€’ Tinnitus / deafness often present",
    "β€’ Nystagmus: horizontal, fatiguable",
    "β€’ Romberg's test +ve",
    "β€’ Gait: unsteady, leans to affected side",
]
add_text(slide, "\n".join(peri_v), 0.3, 1.65, 6.2, 4.8, font_size=10.5, color=C_WHITE)

cent_v = [
    "CAUSES:",
    "β€’ Vertebrobasilar insufficiency",
    "β€’ Cerebellar lesions",
    "β€’ MS",
    "β€’ Brain stem stroke",
    "β€’ Posterior fossa tumours",
    "",
    "FEATURES:",
    "β€’ Gradual onset",
    "β€’ Constant, severe",
    "β€’ Other neurological signs",
    "β€’ Nystagmus: vertical, non-fatiguable",
    "β€’ No hearing loss",
    "β€’ Cranial nerve involvement",
]
add_text(slide, "\n".join(cent_v), 6.8, 1.65, 6.0, 4.8, font_size=10.5, color=C_WHITE)

add_text(slide, "Investigations β†’ PTA β€’ Caloric test β€’ MRI brain/posterior fossa β€’ ENG/VNG  |  Management β†’ Vestibular suppressants (cinnarizine) β€’ Physiotherapy (Epley manoeuvre for BPPV)",
         0.3, 6.6, 12.7, 0.6, font_size=10.5, color=C_GREY)
add_text(slide, "🟒 Homoeopathic (7.23): Conium β€’ Gelsemium β€’ Cocculus β€’ Belladonna β€’ Phosphorus β€’ Tabacum",
         0.3, 7.1, 12.7, 0.25, font_size=11, color=C_HEAD3, align=PP_ALIGN.CENTER)

# ─────────────────────────────────────────────
# SLIDE 11: Meniere's + BPPV + Acoustic Neuroma
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.24–7.25  |  MENIERE'S DISEASE  β€’  BPPV  β€’  ACOUSTIC NEUROMA")

# Meniere's
add_text(slide, "πŸ”΅ MENIERE'S DISEASE (7.24)", 0.3, 0.78, 4.3, 0.4, font_size=13, bold=True, color=C_HEAD1)
men = [
    "Pathology: Endolymphatic hydrops",
    "  ↳ Excess endolymph in cochlea",
    "",
    "TRIAD:",
    "  1. Episodic vertigo",
    "  2. Fluctuating SNHL",
    "  3. Tinnitus + aural fullness",
    "",
    "Ix: PTA β€’ Electrocochleography (ECoG)",
    "    Glycerol test β€’ MRI IAM",
    "",
    "Rx:",
    "  β€’ Low salt diet + diuretics",
    "  β€’ Betahistine (Serc)",
    "  β€’ Vestibular sedatives",
    "  β€’ Surgery: Endolymphatic sac decompression",
    "  β€’ Labyrinthectomy (last resort)",
    "",
    "Homoeop: Conium β€’ China β€’ Cocculus",
    "β€’ Gelsemium β€’ Phosphorus",
]
add_text(slide, "\n".join(men), 0.3, 1.2, 4.3, 5.5, font_size=10.5, color=C_WHITE)

# BPPV
add_text(slide, "🟒 BPPV (7.25)", 4.9, 0.78, 3.8, 0.4, font_size=13, bold=True, color=C_HEAD3)
bppv = [
    "= Benign Paroxysmal Positional Vertigo",
    "",
    "Pathology:",
    "  Canalolithiasis (otoconia in",
    "  posterior semicircular canal)",
    "",
    "Features:",
    "  β€’ Brief (<1 min) vertigo on",
    "    position change",
    "  β€’ Dix-Hallpike test +ve",
    "  β€’ No deafness / tinnitus",
    "",
    "Rx:",
    "  Epley manoeuvre (canalith",
    "  repositioning) ← FIRST LINE",
    "  Vestibular rehab exercises",
    "",
    "Homoeop: Cocculus β€’ Conium",
    "β€’ Gelsemium",
]
add_text(slide, "\n".join(bppv), 4.9, 1.2, 3.8, 5.5, font_size=10.5, color=C_WHITE)

# Acoustic Neuroma
add_text(slide, "🟑 ACOUSTIC NEUROMA (7.25)", 9.0, 0.78, 4.1, 0.4, font_size=13, bold=True, color=C_HEAD4)
acn = [
    "= Vestibular Schwannoma",
    "  (CN VIII, vestibular division)",
    "",
    "Features (GRADUAL):",
    "  β€’ Unilateral SNHL",
    "  β€’ Tinnitus",
    "  β€’ Vertigo (late)",
    "  β€’ Facial numbness (CN V)",
    "  β€’ Facial palsy (CN VII – late)",
    "",
    "Ix:",
    "  MRI IAM ← GOLD STANDARD",
    "  Auditory brainstem response (ABR)",
    "  PTA",
    "",
    "Rx:",
    "  Observation (small) / Radiosurgery",
    "  (Gamma knife) / Surgery",
    "  (translabyrinthine approach)",
]
add_text(slide, "\n".join(acn), 9.0, 1.2, 4.1, 5.5, font_size=10.5, color=C_WHITE)

# ─────────────────────────────────────────────
# SLIDE 12: Facial Palsy / Bell's Palsy
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "7.26  |  FACIAL PARALYSIS  β€’  BELL'S PALSY")

# Left: CN VII course
add_text(slide, "CN VII (FACIAL NERVE) COURSE", 0.3, 0.78, 5.5, 0.4, font_size=13, bold=True, color=C_HEAD1)
cn7 = [
    "Motor cortex",
    "  ↓",
    "Internal capsule",
    "  ↓",
    "Pons (facial nucleus)",
    "  ↓",
    "Cerebellopontine angle (CPA)",
    "  ↓",
    "Internal auditory meatus (IAM)",
    "  ↓",
    "Geniculate ganglion β†’ Chorda tympani",
    "  ↓",
    "Stylomastoid foramen",
    "  ↓",
    "Parotid gland β†’ Terminal branches:",
    "  Temporal β€’ Zygomatic β€’ Buccal",
    "  Marginal mandibular β€’ Cervical",
    "",
    "UPPER MOTOR NEURON (UMN) vs LOWER MOTOR NEURON (LMN):",
    "UMN: forehead SPARED (bilateral cortical supply)",
    "LMN: forehead INVOLVED (all branches affected)",
]
add_text(slide, "\n".join(cn7), 0.3, 1.2, 5.5, 5.6, font_size=10.5, color=C_WHITE)

# Middle: Causes
add_text(slide, "CAUSES OF FACIAL PALSY", 6.1, 0.78, 3.5, 0.4, font_size=13, bold=True, color=C_HEAD4)
causes_fp = [
    "LMN (commonest):",
    "β€’ Bell's Palsy (idiopathic)",
    "β€’ Ramsay Hunt syndrome (VZV)",
    "β€’ Acoustic neuroma",
    "β€’ Parotid tumour",
    "β€’ Trauma / temporal bone Fx",
    "β€’ CSOM (cholesteatoma)",
    "β€’ Mastoidectomy (iatrogenic)",
    "β€’ Lyme disease",
    "",
    "UMN:",
    "β€’ Stroke (CVA)",
    "β€’ Brain tumour",
    "β€’ MS",
]
add_text(slide, "\n".join(causes_fp), 6.1, 1.2, 3.5, 5.6, font_size=10.5, color=C_WHITE)

# Right: Bell's Palsy details
add_text(slide, "BELL'S PALSY (Idiopathic LMN)", 9.8, 0.78, 3.3, 0.4, font_size=12, bold=True, color=C_HEAD2)
bells = [
    "Aetiology: HSV-1 reactivation",
    "",
    "Features:",
    "β€’ Sudden unilateral LMN palsy",
    "β€’ Forehead involved",
    "β€’ Incomplete eye closure",
    "  (Bell's phenomenon)",
    "β€’ Hyperacusis",
    "β€’ Taste loss (ant 2/3 tongue)",
    "β€’ Lacrimation disturbance",
    "",
    "Ix: Clinical diagnosis",
    "β€’ Rule out: Ramsay Hunt (vesicles)",
    "β€’ MRI if atypical",
    "",
    "Grading: House-Brackmann Scale",
    "  I (normal) β†’ VI (complete palsy)",
    "",
    "Rx:",
    "β€’ Prednisolone (started <72 h)",
    "β€’ Acyclovir (if HSV suspected)",
    "β€’ Eye protection: drops/patch",
    "β€’ 80% recover completely",
    "",
    "🟒 Homoeopathic:",
    "β€’ Causticum β€’ Aconite β€’ Gelsemium",
    "β€’ Cadmium sulph β€’ Dulcamara",
]
add_text(slide, "\n".join(bells), 9.8, 1.2, 3.3, 5.6, font_size=10, color=C_WHITE)

# ─────────────────────────────────────────────
# SLIDE 13: Quick Reference – All Topics Summary
# ─────────────────────────────────────────────
slide = blank_slide(prs)
title_bar(slide, "QUICK REFERENCE  –  EAR SYLLABUS UNIT 7  (ALL TOPICS)")

rows = [
    ("Topic", "Key Points", "Investigations", "Management", "Homoeop Remedies"),
    ("ASOM", "Strep pneu / H. inf | Child | URTI", "Otoscopy, Audiometry, C&S", "Abx + Analgesics | Myringotomy", "Belladonna, Hepar sulph, Pulsatilla"),
    ("CSOM (Safe)", "Central perf | Mucoid d/c | No cholest.", "Otoscopy, PTA, CT", "Aural toilet, Tympanoplasty", "Pulsatilla, Kali mur, Merc sol"),
    ("CSOM (Unsafe)", "Marginal perf | Cholesteatoma | Offensive", "HRCT temporal bone, PTA", "Mastoidectomy", "Silicea, Hepar sulph, Calc sulph"),
    ("Glue Ear/OME", "Child | ET dysfunction | No discharge", "Tympanometry (flat)", "Grommet + Adenoidectomy", "Kali mur, Pulsatilla, Merc sol"),
    ("Otalgia", "Local + Referred pain (dental, tonsil)", "ENT exam, OPG, Culture", "Treat cause", "Chamomilla, Aconite, Pulsatilla"),
    ("Otorrhea", "Character β†’ diagnosis", "Otoscopy, C&S, CT", "Aural toilet, Abx drops, Surgery", "Pulsatilla, Silicea, Graphites"),
    ("Deafness", "Conductive / SNHL / Mixed", "PTA, Tympanometry, BERA", "Hearing aid / CI / Surgery", "China, Causticum, Kali mur"),
    ("Tinnitus", "Subjective (common) / Objective", "PTA, MRI IAM, BP/CBC", "TRT, Sound therapy, Treat cause", "China, Nat sal, Carboneum sulph"),
    ("Vertigo", "Peripheral > Central | BPPV commonest", "PTA, Caloric, MRI", "Epley, Betahistine, Physio", "Conium, Cocculus, Gelsemium"),
    ("Meniere's", "Hydrops | Triad: vertigo+SNHL+tinnitus", "ECoG, PTA, Glycerol test", "Low salt, Betahistine, Surgery", "Conium, China, Cocculus"),
    ("Acoustic Neuroma", "CN VIII | Unilateral SNHL + tinnitus", "MRI IAM (gold std), ABR", "Radiosurgery / Surgery", "Phosphorus, Conium"),
    ("Bell's Palsy", "Idiopathic LMN | HSV-1 | 80% recover", "Clinical + Rule out Ramsay Hunt", "Prednisolone <72h, Eye care", "Causticum, Aconite, Gelsemium"),
]

col_widths = [1.8, 3.2, 2.5, 2.4, 3.1]
col_x = [0.2, 2.1, 5.4, 7.95, 10.45]
row_h = 0.44
row_y_start = 0.75

for r, row in enumerate(rows):
    y = row_y_start + r * row_h
    for c, (cell, cx, cw) in enumerate(zip(row, col_x, col_widths)):
        if r == 0:
            bg = RGBColor(0x10, 0x35, 0x60)
            fc = C_TITLE
            fs = 10
            bold = True
        else:
            bg = C_BOX1 if r % 2 == 0 else RGBColor(0x15, 0x25, 0x40)
            fc = [C_HEAD1, C_WHITE, C_HEAD3, C_HEAD4, C_HEAD5][c]
            fs = 9
            bold = False
        box = slide.shapes.add_shape(1, Inches(cx), Inches(y), Inches(cw), Inches(row_h - 0.02))
        box.fill.solid()
        box.fill.fore_color.rgb = bg
        box.line.color.rgb = RGBColor(0x22, 0x44, 0x66)
        box.line.width = Pt(0.5)
        tf = box.text_frame
        tf.word_wrap = True
        p = tf.paragraphs[0]
        p.alignment = PP_ALIGN.LEFT
        run = p.add_run()
        run.text = cell
        run.font.size = Pt(fs)
        run.font.bold = bold
        run.font.color.rgb = fc

prs.save("/tmp/workspace/ear-notes/EAR_Surgery_Notes.pptx")
print("Saved successfully!")
Running Command

cd /tmp/workspace/ear-notes && python3 make_notes.py 2>&1

Running Command

ls -lh /tmp/workspace/ear-notes/EAR_Surgery_Notes.pptx

Your colourful, schematic EAR notes are ready! Here's a summary of what's inside the 13-slide PowerPoint:

EAR Surgery Notes

Presentation Β· PPTX


πŸ“‹ What's Inside (13 Slides)

SlideTopicColour Theme
1Overview mind-map (all 26 syllabus items)Gold on dark navy
2Anatomy (7.1) + Physiology (7.2) + Case Taking + Ear ExamCyan / Green / Orange
3External Ear Diseases (7.5–7.6): Congenital, Trauma, OE, Wax, NeoplasmCoral red
4ASOM (7.7–7.8) – 5-box flow: Aetiology β†’ Pathology β†’ Clinics β†’ Ix β†’ RxGreen with yellow arrows
5CSOM (7.9–7.10) – Safe vs Unsafe type comparison tablePurple vs Green
6Chronic Non-Suppurative OM (7.11): ET Catarrh, Glue Ear, AtelectasisMulti-colour
7Otalgia (7.12–7.13) + Otorrhea (7.14–7.15) split layoutRed / Orange
8Deafness (7.16–7.18) – 3 types + Tuning fork findings + ManagementCyan / Orchid / Green
9Tinnitus (7.19–7.20) – Types, Causes, Ix, ManagementFull column layout
10Vertigo (7.21–7.23) – Peripheral vs Central comparisonGreen vs Red
11Meniere's Disease + BPPV + Acoustic Neuroma (7.24–7.25)3-column
12Facial Palsy / Bell's Palsy (7.26) – CN VII course + UMN/LMN + RxBlue / Orange / Red
13Quick Reference Table – ALL 12 topics in one sheetColour-coded columns
Every slide uses: dark navy background, colour-coded headings, β†’ arrows to replace lengthy descriptions, and includes Homoeopathic Remedies at the bottom of each topic.

Add some diagrams , images from authentic books and make it a pdf

Reading File
Writing File

~/ear-notes/build_ear_pdf.py

"""
EAR Surgery Notes PDF - with original schematic diagrams
Uses matplotlib for diagrams + reportlab for PDF layout
"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch, Arc, Wedge, Circle, Ellipse
import matplotlib.patheffects as pe
import numpy as np
import os, io

OUT_DIR = "/tmp/workspace/ear-notes/diagrams"
os.makedirs(OUT_DIR, exist_ok=True)

# ── colour palette ────────────────────────────────────────────────────────────
BG    = "#0D1B2A"
GOLD  = "#FFD700"
CYAN  = "#00E5FF"
RED   = "#FF6B6B"
GREEN = "#7BFF6B"
ORANGE= "#FFA500"
VIOLET= "#DA70D6"
WHITE = "#FFFFFF"
LGREY = "#AAAAAA"
DBLUE = "#1A3A5C"
DPURP = "#2D1B3D"
DGREEN= "#1A3D2D"
DORNG = "#3D2A1A"

def save_fig(fig, name):
    path = f"{OUT_DIR}/{name}.png"
    fig.savefig(path, dpi=150, bbox_inches='tight', facecolor=BG)
    plt.close(fig)
    return path

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 1 – Cross-section of the Ear (schematic)
# ══════════════════════════════════════════════════════════════════════════════
def diag_ear_anatomy():
    fig, ax = plt.subplots(figsize=(11, 6), facecolor=BG)
    ax.set_facecolor(BG)
    ax.set_xlim(0, 11); ax.set_ylim(0, 6)
    ax.axis('off')
    ax.set_title("Cross-Section of the EAR – Schematic Diagram", color=GOLD,
                 fontsize=14, fontweight='bold', pad=10)

    # ── EXTERNAL EAR region ──────────────────────────────────────────────────
    ext_box = FancyBboxPatch((0.1, 0.4), 2.8, 5.2,
                              boxstyle="round,pad=0.05", lw=1.5,
                              edgecolor=CYAN, facecolor="#0A2030")
    ax.add_patch(ext_box)
    ax.text(1.5, 5.75, "EXTERNAL EAR", ha='center', color=CYAN, fontsize=9, fontweight='bold')

    # Pinna (simple curved shape)
    pinna = mpatches.Arc((0.6, 3.0), 0.9, 3.5, angle=0, theta1=20, theta2=340,
                          color=CYAN, lw=2.5)
    ax.add_patch(pinna)
    ax.text(0.55, 3.0, "Pinna\n(Auricle)", ha='center', color=CYAN, fontsize=7)

    # EAC as a tunnel
    eac = mpatches.FancyBboxPatch((0.9, 2.5), 1.9, 1.0,
                                   boxstyle="round,pad=0.02", lw=1,
                                   edgecolor=CYAN, facecolor="#0D2540")
    ax.add_patch(eac)
    ax.text(1.85, 3.0, "EAC", ha='center', color=CYAN, fontsize=8, fontweight='bold')
    ax.text(1.85, 2.65, "(outer 1/3: cartilage\ninner 2/3: bone)", ha='center', color=LGREY, fontsize=6)

    # TM
    ax.plot([2.8, 2.8], [2.35, 3.65], color=GOLD, lw=4, solid_capstyle='round')
    ax.text(2.82, 4.0, "Tympanic\nMembrane\n(TM)", ha='left', color=GOLD, fontsize=7, fontweight='bold')
    ax.annotate('', xy=(2.8, 3.65), xytext=(2.8, 4.05),
                arrowprops=dict(arrowstyle='->', color=GOLD, lw=1))

    # ── MIDDLE EAR region ────────────────────────────────────────────────────
    mid_box = FancyBboxPatch((3.05, 0.4), 3.6, 5.2,
                              boxstyle="round,pad=0.05", lw=1.5,
                              edgecolor=VIOLET, facecolor="#1A0D2E")
    ax.add_patch(mid_box)
    ax.text(4.85, 5.75, "MIDDLE EAR", ha='center', color=VIOLET, fontsize=9, fontweight='bold')

    # Ossicles chain
    # Malleus
    malleus = Ellipse((3.5, 3.0), 0.25, 0.55, color=ORANGE, lw=1.5, fill=True)
    ax.add_patch(malleus)
    ax.text(3.5, 2.5, "Malleus", ha='center', color=ORANGE, fontsize=7, fontweight='bold')

    # Incus
    incus = Ellipse((4.2, 3.05), 0.3, 0.45, color=ORANGE, lw=1.5, fill=True)
    ax.add_patch(incus)
    ax.text(4.2, 2.5, "Incus", ha='center', color=ORANGE, fontsize=7, fontweight='bold')

    # Stapes
    stapes = Ellipse((4.9, 3.0), 0.2, 0.4, color=ORANGE, lw=1.5, fill=True)
    ax.add_patch(stapes)
    ax.text(4.9, 2.5, "Stapes", ha='center', color=ORANGE, fontsize=7, fontweight='bold')

    # Arrows between ossicles
    for x1, x2 in [(3.63, 3.9), (4.35, 4.68)]:
        ax.annotate('', xy=(x2, 3.0), xytext=(x1, 3.0),
                    arrowprops=dict(arrowstyle='->', color=GOLD, lw=1.5))

    ax.text(4.2, 3.85, "Ossicular Chain\n(amplifies 22Γ—)", ha='center', color=ORANGE, fontsize=7)

    # Eustachian tube
    et_path = plt.Polygon([[3.2, 0.9], [4.5, 0.6], [5.2, 0.55]],
                           closed=False, fill=False, edgecolor=GREEN, lw=2)
    ax.add_patch(et_path)
    ax.text(4.2, 1.2, "Eustachian Tube\n(β†’ nasopharynx)", ha='center', color=GREEN, fontsize=7)

    # Round window
    rw = Circle((5.7, 2.4), 0.2, color=RED, lw=1.5, fill=True, alpha=0.7)
    ax.add_patch(rw)
    ax.text(5.7, 2.0, "Round\nWindow", ha='center', color=RED, fontsize=6)

    # Oval window (stapes footplate)
    ow = Ellipse((5.3, 3.0), 0.3, 0.2, color=GOLD, fill=True, alpha=0.8)
    ax.add_patch(ow)
    ax.text(5.3, 3.35, "Oval Window", ha='center', color=GOLD, fontsize=6)

    # ── INNER EAR region ─────────────────────────────────────────────────────
    inn_box = FancyBboxPatch((6.8, 0.4), 4.1, 5.2,
                              boxstyle="round,pad=0.05", lw=1.5,
                              edgecolor=GREEN, facecolor="#0D2010")
    ax.add_patch(inn_box)
    ax.text(8.85, 5.75, "INNER EAR", ha='center', color=GREEN, fontsize=9, fontweight='bold')

    # Cochlea (spiral)
    theta = np.linspace(0, 5 * np.pi, 400)
    r = 0.05 + 0.18 * theta / (5 * np.pi)
    cx, cy = 8.1, 2.8
    x_coch = cx + r * np.cos(theta)
    y_coch = cy + r * np.sin(theta)
    ax.plot(x_coch, y_coch, color=CYAN, lw=2.5)
    ax.text(8.1, 1.95, "COCHLEA\n(Hearing)\n2.5 turns\nOrgan of Corti inside", ha='center',
            color=CYAN, fontsize=7, fontweight='bold')

    # Semicircular canals (3 circles, different orientations)
    for dx, dy, label in [(0.45, 0.6, 'Post'), (0.0, 0.6, 'Lat'), (-0.45, 0.6, 'Ant')]:
        sc = Circle((9.6 + dx*0.5, 3.2 + dy*0.4), 0.38,
                    fill=False, edgecolor=GREEN, lw=1.8)
        ax.add_patch(sc)
    ax.text(9.6, 4.6, "Semicircular\nCanals Γ—3\n(Balance/Rotation)", ha='center',
            color=GREEN, fontsize=7, fontweight='bold')

    # Vestibule
    vest = Ellipse((8.85, 3.0), 0.55, 0.45, fill=True,
                   facecolor="#1A5010", edgecolor=GREEN, lw=1.5, alpha=0.8)
    ax.add_patch(vest)
    ax.text(8.85, 2.55, "Vestibule\n(Utricle &\nSaccule)", ha='center', color=GREEN, fontsize=6.5)

    # CN VIII label
    ax.annotate("CN VIII β†’\nAuditory\ncortex", xy=(10.5, 3.0), xytext=(10.5, 3.0),
                ha='center', color=VIOLET, fontsize=8, fontweight='bold')

    # Hearing pathway arrow at bottom
    ax.annotate('', xy=(10.5, 0.7), xytext=(0.5, 0.7),
                arrowprops=dict(arrowstyle='->', color=GOLD, lw=2.5))
    ax.text(5.5, 0.5, "SOUND PATHWAY  β†’  Pinna β†’ EAC β†’ TM β†’ Ossicles β†’ Oval Window β†’ Cochlea β†’ CN VIII β†’ Brain",
            ha='center', color=GOLD, fontsize=7.5, fontweight='bold')

    # Region labels at top
    for x, lbl, col in [(1.5, "OUTER", CYAN), (4.85, "MIDDLE", VIOLET), (8.85, "INNER", GREEN)]:
        ax.text(x, 5.9, lbl, ha='center', color=col, fontsize=9, fontweight='bold',
                bbox=dict(boxstyle='round,pad=0.2', facecolor=BG, edgecolor=col))

    plt.tight_layout()
    return save_fig(fig, "01_ear_anatomy")

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 2 – Tympanic Membrane (otoscopic view)
# ══════════════════════════════════════════════════════════════════════════════
def diag_tympanic_membrane():
    fig, axes = plt.subplots(1, 3, figsize=(12, 5), facecolor=BG)
    fig.suptitle("TYMPANIC MEMBRANE – Otoscopic Views", color=GOLD,
                 fontsize=13, fontweight='bold')

    titles = ["NORMAL TM", "ASOM – Bulging TM", "CSOM – Perforated TM"]
    colors_tm = ["#C8966A", "#FF3333", "#AA6633"]
    colors_ring = [LGREY, RED, ORANGE]

    for idx, (ax, title, ctm, cring) in enumerate(zip(axes, titles, colors_tm, colors_ring)):
        ax.set_facecolor("#1A0A05")
        ax.set_xlim(-1.3, 1.3); ax.set_ylim(-1.3, 1.3)
        ax.set_aspect('equal'); ax.axis('off')
        ax.set_title(title, color=cring, fontsize=10, fontweight='bold', pad=6)

        # EAC ring
        outer = Circle((0, 0), 1.25, fill=False, edgecolor=cring, lw=4)
        ax.add_patch(outer)
        # Hair-like dots in canal wall
        for ang in np.linspace(0, 2*np.pi, 20):
            bx, by = 1.15*np.cos(ang), 1.15*np.sin(ang)
            ax.plot(bx, by, 'o', color=cring, markersize=1.5, alpha=0.5)

        if idx == 0:
            # Normal – pearly grey, intact, light reflex
            tm = Circle((0, 0), 1.0, facecolor=ctm, edgecolor="#AA8855", lw=1.5, alpha=0.85)
            ax.add_patch(tm)
            # Malleus handle
            ax.plot([0.0, 0.0], [0.15, 0.65], color='#D4AA70', lw=3, solid_capstyle='round')
            ax.plot([0.0, -0.1], [0.65, 0.75], color='#D4AA70', lw=2)
            ax.text(0.15, 0.45, "Malleus\nhandle", color='#FFD700', fontsize=6)
            # Light reflex (cone of light – anterior inferior)
            light = Wedge((0, 0), 0.35, 230, 300, facecolor='#FFFAAA', alpha=0.55)
            ax.add_patch(light)
            ax.text(-0.45, -0.45, "Light\nreflex", color='#FFFF88', fontsize=6.5)
            # Umbo
            ax.plot(0, 0.12, 'o', color='#AA7733', markersize=5)
            ax.text(0.12, 0.08, "Umbo", color=LGREY, fontsize=6)
            ax.text(0, -1.1, "Pearly grey  β€’  Cone of light present", ha='center',
                    color=GREEN, fontsize=7)

        elif idx == 1:
            # ASOM – red, bulging
            tm = Circle((0, 0), 1.0, facecolor=ctm, edgecolor=RED, lw=2, alpha=0.9)
            ax.add_patch(tm)
            # Bulge indicator
            for r2 in [0.3, 0.5, 0.7]:
                bulge = Circle((0, 0), r2, fill=False, edgecolor=RED, lw=0.8, alpha=0.5)
                ax.add_patch(bulge)
            ax.text(0, 0.0, "BULGING", ha='center', color=WHITE, fontsize=9, fontweight='bold')
            ax.plot([0.0, 0.0], [0.15, 0.55], color='#AA4444', lw=3)
            ax.text(0, -1.1, "Red, congested, bulging  β€’  No light reflex", ha='center',
                    color=RED, fontsize=7)

        else:
            # CSOM – central perforation
            tm = Circle((0, 0), 1.0, facecolor=ctm, edgecolor=ORANGE, lw=1.5, alpha=0.75)
            ax.add_patch(tm)
            # Central perforation
            perf = Circle((0.05, -0.1), 0.32, facecolor='#050505', edgecolor=RED, lw=2)
            ax.add_patch(perf)
            ax.text(0.05, -0.1, "PERFORATION\n(Central)", ha='center', color=RED,
                    fontsize=7, fontweight='bold')
            ax.plot([0.0, 0.0], [0.25, 0.65], color='#AA7733', lw=3)
            ax.text(0, -1.1, "Central perforation  β€’  SAFE type CSOM", ha='center',
                    color=ORANGE, fontsize=7)

    plt.tight_layout()
    return save_fig(fig, "02_tympanic_membrane")

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 3 – ASOM Stages flowchart
# ══════════════════════════════════════════════════════════════════════════════
def diag_asom_stages():
    fig, ax = plt.subplots(figsize=(13, 5.5), facecolor=BG)
    ax.set_facecolor(BG); ax.axis('off')
    ax.set_xlim(0, 13); ax.set_ylim(0, 5.5)
    ax.set_title("ASOM – Stages of Disease Progression", color=GOLD,
                 fontsize=14, fontweight='bold')

    stages = [
        ("Stage 1\nHYPERAEMIA", "β€’ Blood vessels dilate\nβ€’ TM: red, injected\nβ€’ Mild otalgia\nβ€’ Conductive\n  deafness starts", "#8B0000", RED),
        ("Stage 2\nEXUDATION", "β€’ Fluid exudate\n  in middle ear\nβ€’ TM: pink, dull\nβ€’ ↑ otalgia\nβ€’ Fever rises", "#7B3F00", ORANGE),
        ("Stage 3\nSUPPURATION", "β€’ Pus forms\nβ€’ TM: yellow,\n  bulging\nβ€’ Severe otalgia\nβ€’ High fever\nβ€’ Vomiting", "#5B3A00", GOLD),
        ("Stage 4\nRESOLUTION\nor PERFORATION", "β€’ TM perforates\nβ€’ Pus drains\nβ€’ ↓ Pain (relief)\nβ€’ Discharge\nβ€’ Fever settles", "#1A5000", GREEN),
        ("Stage 5\nCOMPLICATION\n(if untreated)", "β€’ Mastoiditis\nβ€’ Intracranial\n  spread\nβ€’ Meningitis\nβ€’ Brain abscess\nβ€’ Facial palsy", "#3D0050", VIOLET),
    ]

    box_w, box_h = 2.2, 4.2
    gap = 0.4
    for i, (title, body, bg_hex, col) in enumerate(stages):
        x0 = 0.3 + i * (box_w + gap)
        box = FancyBboxPatch((x0, 0.4), box_w, box_h,
                              boxstyle="round,pad=0.12", lw=2,
                              edgecolor=col, facecolor=bg_hex)
        ax.add_patch(box)
        ax.text(x0 + box_w/2, 0.4 + box_h - 0.25, title, ha='center', va='top',
                color=col, fontsize=9, fontweight='bold')
        ax.text(x0 + box_w/2, 0.4 + box_h - 0.95, body, ha='center', va='top',
                color=WHITE, fontsize=8, linespacing=1.4)
        # Arrow to next box
        if i < len(stages) - 1:
            ax_x = x0 + box_w + 0.02
            ax.annotate('', xy=(ax_x + gap - 0.02, 2.5), xytext=(ax_x, 2.5),
                        arrowprops=dict(arrowstyle='->', color=GOLD, lw=2.5))

    # Treatment bar at bottom
    ax.text(6.5, 0.18, "Rx: Antibiotics (Stage 1–3)  |  Myringotomy if bulging (Stage 3)  |  Homoeopathic: Belladonna β€’ Hepar sulph β€’ Pulsatilla β€’ Chamomilla",
            ha='center', color=GOLD, fontsize=8.5, fontweight='bold')

    plt.tight_layout()
    return save_fig(fig, "03_asom_stages")

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 4 – CSOM Safe vs Unsafe
# ══════════════════════════════════════════════════════════════════════════════
def diag_csom_types():
    fig, axes = plt.subplots(1, 2, figsize=(11, 5.5), facecolor=BG)
    fig.suptitle("CSOM – Safe (Tubotympanic) vs Unsafe (Atticoantral)", color=GOLD,
                 fontsize=13, fontweight='bold')

    for ax in axes:
        ax.set_facecolor(BG); ax.axis('off')
        ax.set_xlim(0, 5.5); ax.set_ylim(0, 5.5)

    # LEFT: SAFE
    ax = axes[0]
    ax.set_title("βœ…  SAFE TYPE  (Mucosal)", color=GREEN, fontsize=12, fontweight='bold')
    ax.add_patch(Circle((2.75, 3.2), 1.8, fill=True, facecolor="#0A2008", edgecolor=GREEN, lw=2.5))
    ax.add_patch(Circle((2.75, 2.85), 0.55, fill=True, facecolor="#050505", edgecolor=GREEN, lw=2))
    ax.text(2.75, 2.85, "Central\nPerforation", ha='center', va='center', color=GREEN, fontsize=8, fontweight='bold')
    ax.text(2.75, 4.65, "TM intact at edges", ha='center', color=GREEN, fontsize=8)
    rows = [
        ("Perforation", "Central", GREEN),
        ("Discharge", "Mucoid, odourless", GREEN),
        ("Cholesteatoma", "ABSENT", GREEN),
        ("Prognosis", "GOOD (safe)", GREEN),
        ("Treatment", "Tympanoplasty", GREEN),
    ]
    for i, (k, v, c) in enumerate(rows):
        y = 2.0 - i * 0.38
        ax.text(0.2, y, f"β€’ {k}:", color=LGREY, fontsize=8, fontweight='bold')
        ax.text(2.0, y, v, color=c, fontsize=8)

    # RIGHT: UNSAFE
    ax = axes[1]
    ax.set_title("⚠  UNSAFE TYPE  (Squamosal)", color=RED, fontsize=12, fontweight='bold')
    ax.add_patch(Circle((2.75, 3.2), 1.8, fill=True, facecolor="#200808", edgecolor=RED, lw=2.5))
    # Marginal / attic perforation
    attic_perf = Wedge((2.75, 3.2), 1.8, 60, 100, facecolor='#050505', edgecolor=RED, lw=2)
    ax.add_patch(attic_perf)
    ax.text(3.1, 4.7, "Attic / Marginal\nPerforation", ha='center', color=RED, fontsize=7.5, fontweight='bold')
    # Cholesteatoma pearl
    chol = Ellipse((2.2, 3.5), 0.7, 0.5, fill=True, facecolor='#EEEECC', edgecolor=WHITE, lw=1.5, alpha=0.9)
    ax.add_patch(chol)
    ax.text(2.2, 3.5, "Chol.", ha='center', va='center', color="#333300", fontsize=7, fontweight='bold')
    ax.text(1.2, 4.1, "Cholesteatoma\n(pearly white mass)", ha='center', color=GOLD, fontsize=7.5)
    rows2 = [
        ("Perforation", "Marginal / Attic", RED),
        ("Discharge", "Foul-smelling, purulent", RED),
        ("Cholesteatoma", "PRESENT", RED),
        ("Prognosis", "POOR – can erode bone", RED),
        ("Treatment", "Mastoidectomy (surgery)", RED),
    ]
    for i, (k, v, c) in enumerate(rows2):
        y = 2.0 - i * 0.38
        ax.text(0.2, y, f"β€’ {k}:", color=LGREY, fontsize=8, fontweight='bold')
        ax.text(2.0, y, v, color=c, fontsize=8)

    plt.tight_layout()
    return save_fig(fig, "04_csom_types")

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 5 – Pure Tone Audiogram (Conductive vs SNHL)
# ══════════════════════════════════════════════════════════════════════════════
def diag_audiogram():
    fig, axes = plt.subplots(1, 2, figsize=(11, 5.5), facecolor=BG)
    fig.suptitle("AUDIOGRAM – Conductive vs Sensorineural Hearing Loss", color=GOLD,
                 fontsize=13, fontweight='bold')

    freqs = [250, 500, 1000, 2000, 4000, 8000]
    freq_labels = ["250", "500", "1k", "2k", "4k", "8k"]
    x_pos = list(range(len(freqs)))

    configs = [
        ("CONDUCTIVE HEARING LOSS", CYAN,
         [15, 15, 15, 15, 15, 15],   # Normal bone
         [45, 50, 50, 50, 45, 45],   # Elevated air
         "Air-Bone Gap present\nBone conduction NORMAL\nAir conduction ELEVATED\nRinne: –ve (BC > AC)\nWeber: Lateralises to WORSE ear"),
        ("SENSORINEURAL HEARING LOSS", RED,
         [50, 55, 60, 65, 70, 75],   # Elevated bone (same as air ~)
         [52, 58, 62, 67, 72, 77],   # Elevated air (parallel)
         "No Air-Bone Gap\nBoth air & bone elevated\nHigh-frequency loss (4k–8k first)\nRinne: +ve (AC > BC)\nWeber: Lateralises to BETTER ear"),
    ]

    for ax, (title, col, bone_thresh, air_thresh, notes) in zip(axes, configs):
        ax.set_facecolor("#0A0A1A")
        ax.set_xlim(-0.5, 5.5); ax.set_ylim(110, -10)
        ax.set_xticks(x_pos); ax.set_xticklabels(freq_labels, color=WHITE, fontsize=9)
        ax.set_yticks(range(0, 121, 20))
        ax.set_yticklabels([str(y) for y in range(0, 121, 20)], color=WHITE, fontsize=9)
        ax.set_xlabel("Frequency (Hz)", color=LGREY, fontsize=9)
        ax.set_ylabel("Hearing Level (dB HL)", color=LGREY, fontsize=9)
        ax.set_title(title, color=col, fontsize=11, fontweight='bold')
        ax.tick_params(colors=WHITE)
        for spine in ax.spines.values():
            spine.set_edgecolor("#334455")
        ax.grid(True, color="#223344", alpha=0.5, lw=0.5)

        # Shaded hearing zones
        ax.axhspan(-10, 25, alpha=0.08, color=GREEN, label='Normal')
        ax.axhspan(25, 40, alpha=0.08, color=GOLD)
        ax.axhspan(40, 70, alpha=0.08, color=ORANGE)
        ax.axhspan(70, 110, alpha=0.08, color=RED)

        # Zone labels
        for y_mid, lbl, c2 in [(-5, "Normal (<25 dB)", GREEN),
                                 (32, "Mild (25-40)", GOLD),
                                 (55, "Moderate (40-70)", ORANGE),
                                 (90, "Severe/Profound (>70)", RED)]:
            ax.text(-0.4, y_mid, lbl, color=c2, fontsize=6.5, alpha=0.8)

        # Air conduction: circle O (right ear)
        ax.plot(x_pos, air_thresh, 'o-', color=col, lw=2.5, markersize=10,
                markerfacecolor=col, markeredgecolor=WHITE, label='Air (O)')
        # Bone conduction: [ bracket symbol
        ax.plot(x_pos, bone_thresh, 's--', color=ORANGE, lw=2, markersize=8,
                markerfacecolor=ORANGE, markeredgecolor=WHITE, label='Bone ([)')

        # Air-bone gap fill for conductive
        if "CONDUCTIVE" in title:
            ax.fill_between(x_pos, bone_thresh, air_thresh, alpha=0.25, color=CYAN, label='A-B Gap')
            ax.text(2.5, 32, "AIR-BONE\nGAP", ha='center', color=CYAN, fontsize=9,
                    fontweight='bold', alpha=0.9)

        ax.legend(loc='lower right', facecolor=BG, labelcolor=WHITE, fontsize=8, edgecolor=LGREY)

        # Notes text box
        ax.text(5.6, 55, notes, ha='left', va='center', color=WHITE, fontsize=7.5,
                bbox=dict(boxstyle='round,pad=0.4', facecolor='#0A1A2A', edgecolor=col, lw=1.5),
                transform=ax.transData, clip_on=False)

    plt.tight_layout()
    return save_fig(fig, "05_audiogram")

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 6 – Tuning Fork Tests
# ══════════════════════════════════════════════════════════════════════════════
def diag_tuning_fork():
    fig, ax = plt.subplots(figsize=(13, 5.5), facecolor=BG)
    ax.set_facecolor(BG); ax.axis('off')
    ax.set_xlim(0, 13); ax.set_ylim(0, 5.5)
    ax.set_title("TUNING FORK TESTS – Rinne's & Weber's", color=GOLD,
                 fontsize=14, fontweight='bold')

    # Tuning fork shape (stylised)
    def draw_fork(ax, cx, cy, col, size=0.6):
        # Handle
        ax.plot([cx, cx], [cy, cy + size*1.2], color=col, lw=3, solid_capstyle='round')
        # Tines
        for dx in [-0.15*size, 0.15*size]:
            ax.plot([cx + dx, cx + dx], [cy + size*0.9, cy + size*1.6], color=col, lw=2.5)
            tip = Ellipse((cx + dx, cy + size*1.6), 0.08*size*2, 0.12*size*2,
                          fill=True, facecolor=col, edgecolor=col)
            ax.add_patch(tip)

    # ── RINNE'S TEST ──────────────────────────────────────────────────────────
    ax.add_patch(FancyBboxPatch((0.2, 0.3), 5.8, 4.9, boxstyle='round,pad=0.1',
                                 lw=1.5, edgecolor=CYAN, facecolor="#0A1020"))
    ax.text(3.1, 5.05, "RINNE'S TEST", ha='center', color=CYAN, fontsize=12, fontweight='bold')
    ax.text(3.1, 4.7, "(512 Hz fork: bone β†’ mastoid, then air near ear)", ha='center', color=LGREY, fontsize=8)

    # Rinne Positive
    ax.add_patch(FancyBboxPatch((0.35, 0.45), 2.5, 3.9, boxstyle='round,pad=0.08',
                                 lw=1, edgecolor=GREEN, facecolor="#0A1808"))
    ax.text(1.6, 4.2, "RINNE +ve", ha='center', color=GREEN, fontsize=10, fontweight='bold')
    ax.text(1.6, 3.9, "AC > BC", ha='center', color=GREEN, fontsize=9)
    draw_fork(ax, 1.6, 1.9, GREEN, 0.8)
    ax.text(1.6, 1.5, "Air   >   Bone", ha='center', color=GREEN, fontsize=8.5)
    ax.text(1.6, 1.1, "= NORMAL or SNHL", ha='center', color=WHITE, fontsize=8)
    ax.text(1.6, 0.7, "(cochlea/nerve damaged)", ha='center', color=LGREY, fontsize=7)

    # Rinne Negative
    ax.add_patch(FancyBboxPatch((3.15, 0.45), 2.6, 3.9, boxstyle='round,pad=0.08',
                                 lw=1, edgecolor=RED, facecolor="#200808"))
    ax.text(4.45, 4.2, "RINNE –ve", ha='center', color=RED, fontsize=10, fontweight='bold')
    ax.text(4.45, 3.9, "BC > AC", ha='center', color=RED, fontsize=9)
    draw_fork(ax, 4.45, 1.9, RED, 0.8)
    ax.text(4.45, 1.5, "Bone  >  Air", ha='center', color=RED, fontsize=8.5)
    ax.text(4.45, 1.1, "= CONDUCTIVE HL", ha='center', color=WHITE, fontsize=8)
    ax.text(4.45, 0.7, "(β‰₯25 dB loss)", ha='center', color=LGREY, fontsize=7)

    # ── WEBER'S TEST ─────────────────────────────────────────────────────────
    ax.add_patch(FancyBboxPatch((6.4, 0.3), 6.3, 4.9, boxstyle='round,pad=0.1',
                                 lw=1.5, edgecolor=VIOLET, facecolor="#0A0820"))
    ax.text(9.55, 5.05, "WEBER'S TEST", ha='center', color=VIOLET, fontsize=12, fontweight='bold')
    ax.text(9.55, 4.7, "(Fork on vertex/forehead – where does sound lateralise?)", ha='center', color=LGREY, fontsize=8)

    webers = [
        (6.55, "NORMAL /\nBilateral SNHL", "Midline\n(central)", GREEN, "#0A1808"),
        (8.75, "CONDUCTIVE\nHL", "Lateralises\nto WORSE ear", CYAN, "#0A1020"),
        (10.95, "Unilateral\nSNHL", "Lateralises\nto BETTER ear", RED, "#200808"),
    ]
    for x0, title, result, col, bgc in webers:
        ax.add_patch(FancyBboxPatch((x0, 0.45), 2.05, 3.9, boxstyle='round,pad=0.08',
                                     lw=1, edgecolor=col, facecolor=bgc))
        ax.text(x0+1.025, 4.2, title, ha='center', color=col, fontsize=8.5, fontweight='bold')
        draw_fork(ax, x0+1.025, 2.6, col, 0.6)
        # Head outline
        head = Circle((x0+1.025, 1.85), 0.4, fill=True, facecolor="#2A1A0A", edgecolor=col, lw=1.5)
        ax.add_patch(head)
        ax.text(x0+1.025, 1.85, "πŸ‘‚", ha='center', va='center', fontsize=10)
        ax.text(x0+1.025, 1.2, result, ha='center', color=WHITE, fontsize=8, fontweight='bold')
        ax.text(x0+1.025, 0.65, col, ha='center', color=col, fontsize=7, alpha=0)  # spacer

    plt.tight_layout()
    return save_fig(fig, "06_tuning_fork")

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 7 – Vertigo: Central vs Peripheral comparison
# ══════════════════════════════════════════════════════════════════════════════
def diag_vertigo():
    fig, ax = plt.subplots(figsize=(13, 6), facecolor=BG)
    ax.set_facecolor(BG); ax.axis('off')
    ax.set_xlim(0, 13); ax.set_ylim(0, 6)
    ax.set_title("VERTIGO – Peripheral vs Central: Comparison & Causes", color=GOLD,
                 fontsize=14, fontweight='bold')

    # Central divider
    ax.axvline(6.5, color=GOLD, lw=2, alpha=0.4)
    ax.text(0.3, 5.7, "PERIPHERAL VERTIGO", color=GREEN, fontsize=12, fontweight='bold')
    ax.text(6.8, 5.7, "CENTRAL VERTIGO", color=RED, fontsize=12, fontweight='bold')

    left_rows = [
        ("Origin", "Inner ear / Vestibular nerve", GREEN),
        ("Onset", "Sudden, episodic", GREEN),
        ("Severity", "Intense +++ (esp. BPPV)", GREEN),
        ("Duration", "Seconds to minutes (BPPV)\nDays to weeks (Labyrinthitis)", GREEN),
        ("Nausea/Vomiting", "Severe +++", GREEN),
        ("Nystagmus", "Horizontal, fatiguable,\ndirection-fixed", CYAN),
        ("Hearing Loss / Tinnitus", "Often present (Meniere's, Labyrinthitis)", CYAN),
        ("Neurological signs", "ABSENT", GREEN),
        ("Dix-Hallpike Test", "+ve (BPPV)", GOLD),
        ("Causes", "BPPV  β€’  Meniere's Disease\nVestibular neuritis  β€’  Labyrinthitis\nAcoustic neuroma (late)", GREEN),
    ]
    right_rows = [
        ("Origin", "Brainstem / Cerebellum", RED),
        ("Onset", "Gradual, constant", RED),
        ("Severity", "Moderate, persistent", RED),
        ("Duration", "Continuous (hours to weeks)", RED),
        ("Nausea/Vomiting", "Mild", RED),
        ("Nystagmus", "Vertical / direction-changing,\nnon-fatiguable", VIOLET),
        ("Hearing Loss / Tinnitus", "ABSENT", RED),
        ("Neurological signs", "PRESENT (diplopia, dysarthria,\nweakness, ataxia)", VIOLET),
        ("MRI", "Posterior fossa lesion", GOLD),
        ("Causes", "VBI  β€’  Cerebellar stroke\nMS  β€’  Posterior fossa tumour\nWernicke's encephalopathy", RED),
    ]

    for i, (feat, val, col) in enumerate(left_rows):
        y = 5.25 - i * 0.49
        ax.text(0.3, y, f"β–Ά {feat}:", color=LGREY, fontsize=8, fontweight='bold')
        ax.text(2.2, y, val, color=col, fontsize=8)

    for i, (feat, val, col) in enumerate(right_rows):
        y = 5.25 - i * 0.49
        ax.text(6.8, y, f"β–Ά {feat}:", color=LGREY, fontsize=8, fontweight='bold')
        ax.text(8.7, y, val, color=col, fontsize=8)

    # Inner ear schematic (semicircular canals) – tiny diagram
    ax.text(12.5, 3.0, "BPPV:\nOtoconia\nenter\nposterior\nSCC", ha='center',
            color=CYAN, fontsize=7.5,
            bbox=dict(boxstyle='round,pad=0.4', facecolor='#0A1A0A', edgecolor=CYAN, lw=1))

    plt.tight_layout()
    return save_fig(fig, "07_vertigo")

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 8 – CN VII Course & Bell's Palsy
# ══════════════════════════════════════════════════════════════════════════════
def diag_facial_nerve():
    fig, ax = plt.subplots(figsize=(13, 6.5), facecolor=BG)
    ax.set_facecolor(BG); ax.axis('off')
    ax.set_xlim(0, 13); ax.set_ylim(0, 6.5)
    ax.set_title("CN VII (Facial Nerve) – Course, UMN vs LMN & Bell's Palsy", color=GOLD,
                 fontsize=14, fontweight='bold')

    # ── Nerve path from motor cortex down ────────────────────────────────────
    waypoints = [
        (2.0, 6.2, "Motor Cortex\n(bilateral for forehead)", LGREY),
        (2.0, 5.3, "Internal Capsule", LGREY),
        (2.0, 4.5, "Pons\n(Facial Nucleus)", CYAN),
        (2.0, 3.7, "CPA\n(Cerebellopontine Angle)", CYAN),
        (2.0, 2.9, "IAM\n(Internal Auditory Meatus)", CYAN),
        (2.0, 2.1, "Geniculate Ganglion\n→ Greater Petrosal N.\n→ Chorda Tympani", VIOLET),
        (2.0, 1.1, "Stylomastoid\nForamen", ORANGE),
        (2.0, 0.35, "Parotid Gland\n(Terminal Branches)", GOLD),
    ]
    for i, (x, y, lbl, col) in enumerate(waypoints):
        ax.add_patch(FancyBboxPatch((x - 1.3, y - 0.28), 2.6, 0.52,
                                     boxstyle='round,pad=0.05', lw=1.5,
                                     edgecolor=col, facecolor=BG))
        ax.text(x, y, lbl, ha='center', va='center', color=col, fontsize=7.5, fontweight='bold')
        if i < len(waypoints) - 1:
            ax.annotate('', xy=(x, waypoints[i+1][1] + 0.27),
                        xytext=(x, y - 0.28),
                        arrowprops=dict(arrowstyle='->', color=GOLD, lw=1.8))

    # Terminal branches
    branches = [("Temporal", 4.0, 0.7, VIOLET),
                ("Zygomatic", 5.2, 0.4, VIOLET),
                ("Buccal", 6.4, 0.7, VIOLET),
                ("Marg. Mand.", 7.4, 0.4, VIOLET),
                ("Cervical", 8.3, 0.7, VIOLET)]
    for lbl, bx, by, bc in branches:
        ax.annotate('', xy=(bx, by + 0.15), xytext=(2.7, 0.32),
                    arrowprops=dict(arrowstyle='->', color=VIOLET, lw=1.2, connectionstyle='arc3,rad=0.2'))
        ax.text(bx, by, lbl, ha='center', color=bc, fontsize=7.5,
                bbox=dict(boxstyle='round,pad=0.2', facecolor='#1A0A28', edgecolor=VIOLET, lw=0.8))

    # ── UMN vs LMN side panel ─────────────────────────────────────────────────
    ax.add_patch(FancyBboxPatch((4.0, 2.5), 4.0, 3.7, boxstyle='round,pad=0.1',
                                 lw=1.5, edgecolor=CYAN, facecolor="#050D15"))
    ax.text(6.0, 6.05, "UMN  vs  LMN  FACIAL PALSY", ha='center', color=CYAN,
            fontsize=11, fontweight='bold')
    umns = [("UMN (above nucleus)", CYAN,
             "β€’ Forehead SPARED\n  (bilateral cortical supply)\nβ€’ Lower face affected\nβ€’ Causes: Stroke, tumour\nβ€’ Wrinkle forehead – YES\nβ€’ Close eye – YES"),
            ("LMN (nucleus or below)", RED,
             "β€’ Forehead INVOLVED\n  (all branches affected)\nβ€’ Entire half of face\nβ€’ Causes: Bell's, Ramsay Hunt,\n  Parotid tumour, Trauma\nβ€’ Wrinkle forehead – NO\nβ€’ Close eye – NO")]
    for i, (title, col, body) in enumerate(umns):
        x0 = 4.15 + i * 2.0
        ax.add_patch(FancyBboxPatch((x0, 2.65), 1.8, 3.25, boxstyle='round,pad=0.05',
                                     lw=1, edgecolor=col, facecolor=BG))
        ax.text(x0 + 0.9, 5.78, title, ha='center', color=col, fontsize=8, fontweight='bold')
        ax.text(x0 + 0.9, 5.45, body, ha='center', va='top', color=WHITE, fontsize=7.5, linespacing=1.4)

    # ── Bell's Palsy details ─────────────────────────────────────────────────
    ax.add_patch(FancyBboxPatch((8.4, 0.25), 4.4, 6.0, boxstyle='round,pad=0.1',
                                 lw=1.5, edgecolor=ORANGE, facecolor="#150800"))
    ax.text(10.6, 6.1, "BELL'S PALSY", ha='center', color=ORANGE, fontsize=12, fontweight='bold')
    bells_data = [
        ("Aetiology", "HSV-1 reactivation\n→ oedema of nerve in bony canal"),
        ("Type", "LMN (forehead INVOLVED)"),
        ("Features", "Sudden unilateral palsy\nFacial droop, Lagophthalmos\nBell's phenomenon (eye rolls up)\nHyperacusis (stapedius affected)\nTaste loss (ant 2/3 tongue)\nLacrimation ↓"),
        ("Grading", "House-Brackmann:\nI (normal) β†’ VI (complete)"),
        ("Ix", "Clinical diagnosis\nMRI if atypical"),
        ("Treatment", "Prednisolone within 72h\nAcyclovir (if severe/HSV)\nEye drops / patch (cornea care)\n→ 80% full recovery"),
        ("Homoeopathic", "Causticum  β€’  Aconite\nGelsemium  β€’  Dulcamara\nCadmium sulph"),
    ]
    y_pos = 5.65
    for k, v in bells_data:
        ax.text(8.6, y_pos, f"β–Ά {k}:", color=ORANGE, fontsize=8, fontweight='bold')
        ax.text(8.6, y_pos - 0.28, v, color=WHITE, fontsize=7.5, linespacing=1.3)
        lines = v.count('\n') + 1
        y_pos -= 0.25 + 0.22 * lines

    plt.tight_layout()
    return save_fig(fig, "08_facial_nerve")

# ══════════════════════════════════════════════════════════════════════════════
# DIAGRAM 9 – Deafness types + Tympanograms
# ══════════════════════════════════════════════════════════════════════════════
def diag_tympanogram():
    fig, axes = plt.subplots(1, 3, figsize=(12, 5), facecolor=BG)
    fig.suptitle("TYMPANOGRAMS – Types A, B, C  (Middle Ear Pressure Curves)", color=GOLD,
                 fontsize=12, fontweight='bold')

    configs = [
        ("Type A – NORMAL", GREEN,
         np.linspace(-200, 200, 200),
         lambda p: 1.2 * np.exp(-0.5 * (p/50)**2),
         "Peak at 0 daPa\nNormal middle ear pressure\nNormal compliance\n→ Normal / SNHL"),
        ("Type B – FLAT", CYAN,
         np.linspace(-200, 200, 200),
         lambda p: 0.15 + 0*p,
         "No peak (flat curve)\nFluid in middle ear\n→ Serous OM (Glue Ear)\n→ TM perforation"),
        ("Type C – NEG PRESSURE", ORANGE,
         np.linspace(-400, 200, 200),
         lambda p: 0.9 * np.exp(-0.5 * ((p+150)/60)**2),
         "Peak shifted left (<-100)\nNeg. middle ear pressure\nET dysfunction\n→ Eustachian tube catarrh"),
    ]
    for ax, (title, col, pressures, compliance_fn, notes) in zip(axes, configs):
        ax.set_facecolor("#070720")
        comp = compliance_fn(pressures)
        ax.plot(pressures, comp, color=col, lw=3)
        ax.fill_between(pressures, comp, alpha=0.15, color=col)
        ax.axvline(0, color=LGREY, lw=1, linestyle='--', alpha=0.4)
        ax.axhline(0, color=LGREY, lw=0.5, alpha=0.3)
        ax.set_xlabel("Pressure (daPa)", color=LGREY, fontsize=8)
        ax.set_ylabel("Compliance (mL)", color=LGREY, fontsize=8)
        ax.set_title(title, color=col, fontsize=10, fontweight='bold')
        ax.tick_params(colors=WHITE, labelsize=7)
        for spine in ax.spines.values():
            spine.set_edgecolor("#334455")
        ax.text(0.05, 0.95, notes, transform=ax.transAxes, va='top', ha='left',
                color=WHITE, fontsize=7.5, linespacing=1.4,
                bbox=dict(boxstyle='round,pad=0.3', facecolor=BG, edgecolor=col, lw=1, alpha=0.85))

    plt.tight_layout()
    return save_fig(fig, "09_tympanogram")

# ══════════════════════════════════════════════════════════════════════════════
# BUILD ALL DIAGRAMS
# ══════════════════════════════════════════════════════════════════════════════
print("Building diagrams...")
paths = {
    "ear_anatomy":     diag_ear_anatomy(),
    "tm_views":        diag_tympanic_membrane(),
    "asom_stages":     diag_asom_stages(),
    "csom_types":      diag_csom_types(),
    "audiogram":       diag_audiogram(),
    "tuning_fork":     diag_tuning_fork(),
    "vertigo":         diag_vertigo(),
    "facial_nerve":    diag_facial_nerve(),
    "tympanogram":     diag_tympanogram(),
}
for k, v in paths.items():
    print(f"  βœ“ {k}: {v}")
print("All diagrams done.")
Writing File

~/ear-notes/assemble_pdf.py

"""
PDF assembler – combines text notes + diagrams into a full colour PDF
Uses reportlab for layout
"""
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.units import cm, mm
from reportlab.lib.colors import (HexColor, white, black)
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer,
                                 Image, Table, TableStyle, PageBreak,
                                 HRFlowable, KeepTogether)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
from reportlab.platypus.flowables import Flowable
import os

OUT_PDF  = "/tmp/workspace/ear-notes/EAR_Surgery_Comprehensive_Notes.pdf"
DIAG_DIR = "/tmp/workspace/ear-notes/diagrams"

# ── colour palette ────────────────────────────────────────────────────────────
BG      = HexColor("#0D1B2A")
GOLD    = HexColor("#FFD700")
CYAN    = HexColor("#00E5FF")
RED     = HexColor("#FF6B6B")
GREEN   = HexColor("#7BFF6B")
ORANGE  = HexColor("#FFA500")
VIOLET  = HexColor("#DA70D6")
WHITE_C = HexColor("#FFFFFF")
LGREY   = HexColor("#AAAAAA")
DBLUE   = HexColor("#1A3A5C")
DPURP   = HexColor("#2D1B3D")
DGREEN  = HexColor("#1A3D2D")
DORNG   = HexColor("#3D2A1A")
DARKROW = HexColor("#0A1828")
ALTROW  = HexColor("#101C2C")

W, H = landscape(A4)          # 841.89 Γ— 595.28 pt  (landscape)

# ── styles ────────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def S(name, parent='Normal', **kw):
    return ParagraphStyle(name, parent=styles[parent], **kw)

sTitle      = S('sTitle',   fontSize=24, leading=30, textColor=GOLD,
                alignment=TA_CENTER, fontName='Helvetica-Bold', spaceAfter=6)
sSubtitle   = S('sSubtitle',fontSize=13, leading=18, textColor=LGREY,
                alignment=TA_CENTER, fontName='Helvetica')
sH1         = S('sH1',      fontSize=15, leading=20, textColor=CYAN,
                fontName='Helvetica-Bold', spaceBefore=10, spaceAfter=4,
                backColor=DBLUE, leftPadding=6)
sH2         = S('sH2',      fontSize=12, leading=16, textColor=GOLD,
                fontName='Helvetica-Bold', spaceBefore=6, spaceAfter=2)
sH3         = S('sH3',      fontSize=10.5, leading=14, textColor=VIOLET,
                fontName='Helvetica-Bold', spaceBefore=4)
sBody       = S('sBody',    fontSize=9.5, leading=13, textColor=WHITE_C,
                fontName='Helvetica')
sBodyCyan   = S('sBodyCyan',  fontSize=9.5, leading=13, textColor=CYAN,   fontName='Helvetica')
sBodyGreen  = S('sBodyGreen', fontSize=9.5, leading=13, textColor=GREEN,  fontName='Helvetica')
sBodyRed    = S('sBodyRed',   fontSize=9.5, leading=13, textColor=RED,    fontName='Helvetica')
sBodyOrange = S('sBodyOrange',fontSize=9.5, leading=13, textColor=ORANGE, fontName='Helvetica')
sBodyGold   = S('sBodyGold',  fontSize=9.5, leading=13, textColor=GOLD,   fontName='Helvetica')
sNote       = S('sNote',    fontSize=8.5, leading=12, textColor=LGREY, fontName='Helvetica-Oblique')
sCap        = S('sCap',     fontSize=8, leading=10, textColor=LGREY,
                alignment=TA_CENTER, fontName='Helvetica-Oblique', spaceAfter=4)
sHom        = S('sHom',     fontSize=9, leading=12, textColor=GREEN,
                fontName='Helvetica-Bold', backColor=DGREEN, leftPadding=6, spaceAfter=6)

def img(fname, width=None, height=None):
    p = f"{DIAG_DIR}/{fname}.png"
    if not os.path.exists(p):
        return Spacer(1, 0.5*cm)
    return Image(p, width=width, height=height, kind='proportional' if width and not height else 'direct')

def hr(col=GOLD):
    return HRFlowable(width="100%", thickness=1, color=col, spaceAfter=4, spaceBefore=4)

def cap(text):
    return Paragraph(f"<i>{text}</i>", sCap)

def h1(text):   return Paragraph(f"β–Œ {text}", sH1)
def h2(text):   return Paragraph(f"β—† {text}", sH2)
def h3(text):   return Paragraph(text, sH3)
def body(text, style=sBody): return Paragraph(text, style)
def bullet(text, col=WHITE_C):
    st = S('_b', fontSize=9.5, leading=13, textColor=col, leftIndent=12,
           fontName='Helvetica', bulletIndent=2)
    return Paragraph(f"β€’ {text}", st)
def arrow_bullet(text, col=WHITE_C):
    st = S('_ab', fontSize=9.5, leading=13, textColor=col, leftIndent=16,
           fontName='Helvetica')
    return Paragraph(f"β†’ {text}", st)
def hom_line(remedies):
    return Paragraph(f"🟒  Homoeopathic Remedies:  {remedies}", sHom)

def section_header(title, subtitle, col):
    c = HexColor(col)
    return Table([[Paragraph(f"<b>{title}</b>", ParagraphStyle('sh', fontSize=18, leading=24,
                   textColor=c, fontName='Helvetica-Bold')),
                   Paragraph(subtitle, ParagraphStyle('ss', fontSize=10, leading=14,
                   textColor=LGREY, fontName='Helvetica'))]],
                 colWidths=[W*0.55, W*0.40],
                 style=TableStyle([
                     ('BACKGROUND', (0,0), (-1,-1), DBLUE),
                     ('LINEBELOW',  (0,0), (-1,-1), 1, c),
                     ('TOPPADDING', (0,0), (-1,-1), 6),
                     ('BOTTOMPADDING', (0,0), (-1,-1), 6),
                     ('LEFTPADDING',   (0,0), (0,-1), 8),
                 ]))

def two_col(left_items, right_items, lw=0.48, rw=0.48):
    """Two-column layout using a table"""
    from reportlab.platypus import ListFlowable
    def pack(items):
        return [i if hasattr(i, 'wrap') else Paragraph(str(i), sBody) for i in items]
    row = [[pack(left_items), pack(right_items)]]
    tbl = Table(row, colWidths=[W*lw, W*rw])
    tbl.setStyle(TableStyle([
        ('VALIGN',      (0,0), (-1,-1), 'TOP'),
        ('LEFTPADDING', (0,0), (-1,-1), 4),
        ('RIGHTPADDING',(0,0), (-1,-1), 4),
    ]))
    return tbl

def info_table(headers, rows, col_colors=None):
    """Colour-formatted table"""
    n_cols = len(headers)
    header_row = [Paragraph(f"<b>{h}</b>",
                  ParagraphStyle('th', fontSize=9, textColor=GOLD,
                                 fontName='Helvetica-Bold')) for h in headers]
    data = [header_row]
    for r_idx, row in enumerate(rows):
        bg = DARKROW if r_idx % 2 == 0 else ALTROW
        data_row = []
        for c_idx, cell in enumerate(row):
            col = (col_colors[c_idx] if col_colors else WHITE_C)
            data_row.append(Paragraph(cell,
                ParagraphStyle(f'td{r_idx}{c_idx}', fontSize=8.5,
                               textColor=col, fontName='Helvetica')))
        data.append(data_row)

    col_w = (W - 2*cm) / n_cols
    tbl = Table(data, colWidths=[col_w]*n_cols, repeatRows=1)
    tbl.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0),  DBLUE),
        ('ROWBACKGROUNDS',(0,1), (-1,-1), [DARKROW, ALTROW]),
        ('GRID',          (0,0), (-1,-1), 0.4, HexColor("#223344")),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 5),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ]))
    return tbl

# ── background canvas ─────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    canvas.setFillColor(BG)
    canvas.rect(0, 0, W, H, fill=1, stroke=0)
    # thin gold border
    canvas.setStrokeColor(GOLD)
    canvas.setLineWidth(1.5)
    canvas.rect(6, 6, W-12, H-12, fill=0, stroke=1)
    # footer
    canvas.setFont('Helvetica', 7)
    canvas.setFillColor(LGREY)
    canvas.drawCentredString(W/2, 10, f"EAR Surgery Notes  β€’  Hom UG-Sur-II Unit 7  β€’  Page {doc.page}")
    canvas.restoreState()

# ── build story ───────────────────────────────────────────────────────────────
story = []
SP = lambda h=0.3: Spacer(1, h*cm)

# ════════════════════════════════════════════════════════════════════════════
#  COVER PAGE
# ════════════════════════════════════════════════════════════════════════════
story += [
    SP(3),
    Paragraph("πŸ‘‚  EAR", ParagraphStyle('cv1', fontSize=56, leading=64, textColor=GOLD,
              alignment=TA_CENTER, fontName='Helvetica-Bold')),
    SP(0.4),
    Paragraph("Surgery Notes  –  Hom UG-Sur-II  β€’  Unit 7", sSubtitle),
    SP(0.6),
    Paragraph("Comprehensive Schematic Notes with Diagrams", sSubtitle),
    SP(0.6),
    hr(GOLD),
    SP(0.3),
    Paragraph("Covers all 26 SLOs  |  Anatomy β†’ External Ear β†’ ASOM β†’ CSOM β†’ Deafness β†’ Tinnitus β†’ Vertigo β†’ Bell's Palsy",
              S('cv2', fontSize=10, textColor=LGREY, alignment=TA_CENTER, fontName='Helvetica')),
    SP(0.4),
    Paragraph("Each section:  Aetiology  β†’  Pathology  β†’  Clinical Features  β†’  Investigations  β†’  Management  β†’  Homoeopathic Remedies",
              S('cv3', fontSize=9.5, textColor=CYAN, alignment=TA_CENTER, fontName='Helvetica-Bold')),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 1 – EAR ANATOMY DIAGRAM (full-width)
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("7.1  |  ANATOMY OF THE EAR", "SLO: Review applied anatomy of Ear – External, Middle, Inner", "#00E5FF"),
    SP(0.3),
    img("01_ear_anatomy", width=W - 2.5*cm),
    cap("Fig 1. Cross-section schematic of the ear showing External, Middle and Inner ear compartments with labelled structures."),
    SP(0.3),
    two_col([
        h2("KEY STRUCTURES"),
        bullet("External: Pinna β†’ EAC β†’ Tympanic membrane (TM)", CYAN),
        bullet("Middle: Ossicles (Malleus β†’ Incus β†’ Stapes) amplify sound 22Γ—", CYAN),
        bullet("Middle: Eustachian tube β†’ equalises pressure with nasopharynx", CYAN),
        bullet("Inner: Cochlea (hearing) – Organ of Corti, 2.5 turns", GREEN),
        bullet("Inner: Semicircular canals Γ—3 (rotation), Utricle + Saccule (linear acceleration)", GREEN),
        bullet("Nerve supply: CN VIII (cochlear + vestibular divisions) β†’ temporal lobe", VIOLET),
    ],[
        h2("SOUND PATHWAY"),
        arrow_bullet("Pinna β†’ collects and directs sound", GOLD),
        arrow_bullet("EAC β†’ transmits to TM", GOLD),
        arrow_bullet("TM β†’ converts sound to vibration", GOLD),
        arrow_bullet("Ossicles β†’ amplify (22Γ—, overcomes air-fluid impedance)", GOLD),
        arrow_bullet("Stapes footplate β†’ Oval window β†’ Perilymph waves", GOLD),
        arrow_bullet("Cochlea β†’ hair cells (organ of Corti) depolarise", GOLD),
        arrow_bullet("CN VIII β†’ cochlear nucleus β†’ auditory cortex (temporal lobe)", GOLD),
        SP(0.2),
        h2("EQUILIBRIUM"),
        arrow_bullet("Semicircular canals β†’ angular/rotational acceleration", CYAN),
        arrow_bullet("Utricle + Saccule β†’ linear acceleration + static position", CYAN),
        arrow_bullet("CN VIII (vestibular) β†’ vestibular nuclei β†’ cerebellum", CYAN),
    ]),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 2 – TM VIEWS + CASE TAKING
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("7.2–7.4  |  PHYSIOLOGY  β€’  CASE TAKING  β€’  EAR EXAMINATION", "SLO: Physiology of hearing & equilibrium | Case taking | Ear examination", "#FFA500"),
    SP(0.3),
    img("02_tympanic_membrane", width=W - 2.5*cm),
    cap("Fig 2. Otoscopic views of the TM: Normal (pearly grey, cone of light), ASOM (bulging, red), CSOM (central perforation)."),
    SP(0.3),
    two_col([
        h2("EAR CASE TAKING (7.3)"),
        h3("Chief Complaints:"),
        bullet("Otalgia (pain) – onset, severity, radiation", ORANGE),
        bullet("Discharge – colour, smell, duration", ORANGE),
        bullet("Deafness – unilateral/bilateral, sudden/gradual", ORANGE),
        bullet("Tinnitus – character, frequency", ORANGE),
        bullet("Vertigo – episodic? Nausea?", ORANGE),
        h3("History:"),
        bullet("H/o URTI, trauma, noise exposure", LGREY),
        bullet("Family history, past infections", LGREY),
        bullet("Systemic diseases (diabetes – malignant OE)", LGREY),
        h3("Homoeopathic:"),
        bullet("Modalities (worse/better from)", VIOLET),
        bullet("Mental generals, thermals, constitution", VIOLET),
        bullet("Repertorisation (Synthesis/Radar)", VIOLET),
    ],[
        h2("EAR EXAMINATION (7.4)  –  DOPS"),
        h3("Inspection:"),
        bullet("Pinna – shape, scars, pre-auricular sinus", CYAN),
        bullet("Tragus tenderness (furunculosis/OE)", CYAN),
        bullet("Post-auricular: mastoid tenderness (mastoiditis)", CYAN),
        h3("Otoscopy:"),
        bullet("TM – colour, landmarks, light reflex, perforation", CYAN),
        bullet("Normal TM: Pearly grey, cone of light antero-inferior", CYAN),
        h3("Tuning Fork Tests:"),
        bullet("Rinne's (512 Hz): BC vs AC", GOLD),
        bullet("Weber's (512 Hz): lateralisation", GOLD),
        h3("Free-field Hearing Tests:"),
        bullet("Whisper test, watch-tick test", LGREY),
        h3("Additional:"),
        bullet("Nasal/nasopharyngeal exam (ET)", LGREY),
        bullet("Cranial nerve assessment", LGREY),
    ]),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 3 – ASOM
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("7.7–7.8  |  ACUTE SUPPURATIVE OTITIS MEDIA (ASOM)", "SLO: Aetiology, Pathology, Clinical Features, Investigations, Treatment", "#7BFF6B"),
    SP(0.3),
    img("03_asom_stages", width=W - 2.5*cm),
    cap("Fig 3. Five stages of ASOM disease progression – from hyperaemia to complication/resolution."),
    SP(0.3),
    two_col([
        h2("AETIOLOGY"),
        bullet("Streptococcus pneumoniae (most common adult)", RED),
        bullet("Haemophilus influenzae (most common child)", RED),
        bullet("Moraxella catarrhalis", RED),
        bullet("Route: Via Eustachian tube following URTI", RED),
        bullet("Risk factors: Children (short, horizontal ET), crèche, bottle-feeding, cleft palate", ORANGE),
        SP(0.2),
        h2("INVESTIGATIONS"),
        bullet("Otoscopy – TM appearance (see stages)", CYAN),
        bullet("Audiometry – conductive hearing loss", CYAN),
        bullet("Tympanometry – reduced compliance", CYAN),
        bullet("C&S of discharge (post-perforation)", CYAN),
        bullet("X-ray mastoid (if complications suspected)", CYAN),
    ],[
        h2("MANAGEMENT"),
        h3("Conservative:"),
        arrow_bullet("Analgesics + antipyretics", GREEN),
        arrow_bullet("Antibiotics: Amoxicillin 1st line; Co-amoxiclav (Augmentin) if resistant", GREEN),
        arrow_bullet("Nasal decongestants (reduce ET oedema)", GREEN),
        arrow_bullet("Myringotomy if TM bulging (Stage 3) – aural toilet", GREEN),
        h3("Surgical:"),
        arrow_bullet("Myringotomy + insertion of grommet", GOLD),
        arrow_bullet("Mastoidectomy if mastoiditis develops", GOLD),
        SP(0.2),
        hom_line("Belladonna  β€’  Hepar sulph  β€’  Pulsatilla  β€’  Chamomilla  β€’  Capsicum  β€’  Merc sol"),
    ]),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 4 – CSOM
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("7.9–7.10  |  CHRONIC SUPPURATIVE OTITIS MEDIA (CSOM)", "SLO: Types, Aetiology, Pathology, Clinical Features, Investigations, Management", "#FF6B6B"),
    SP(0.3),
    img("04_csom_types", width=W - 2.5*cm),
    cap("Fig 4. CSOM – Safe (Tubotympanic) vs Unsafe (Atticoantral) types with TM perforation patterns."),
    SP(0.3),
    two_col([
        h2("AETIOLOGY"),
        bullet("Unresolved ASOM", RED),
        bullet("Repeated upper respiratory infections", RED),
        bullet("Eustachian tube dysfunction", RED),
        bullet("Poor socioeconomic conditions / nutrition", RED),
        bullet("Cleft palate, Down syndrome", RED),
        SP(0.2),
        h2("INVESTIGATIONS"),
        bullet("Otoscopy – type of perforation, cholesteatoma?", CYAN),
        bullet("Pure Tone Audiometry (PTA) – conductive HL", CYAN),
        bullet("Tympanometry", CYAN),
        bullet("HRCT temporal bone – gold standard for cholesteatoma extent", CYAN),
        bullet("Swab for culture & sensitivity", CYAN),
        SP(0.2),
        h2("COMPLICATIONS  (Unsafe type)"),
        bullet("Mastoiditis (most common)", ORANGE),
        bullet("Labyrinthitis β†’ SNHL + vertigo", ORANGE),
        bullet("Intracranial: Meningitis, Brain abscess", RED),
        bullet("Lateral sinus thrombosis", RED),
        bullet("Facial nerve palsy (CN VII erosion)", RED),
        bullet("Petrositis β†’ Gradenigo's syndrome", RED),
    ],[
        h2("MANAGEMENT"),
        h3("Conservative (Safe type):"),
        arrow_bullet("Aural toilet (suction + dry mopping)", GREEN),
        arrow_bullet("Topical antibiotic ear drops", GREEN),
        arrow_bullet("Treat underlying URTI / nasal allergy", GREEN),
        h3("Surgical:"),
        arrow_bullet("Tympanoplasty – TM repair (Safe type)", GREEN),
        arrow_bullet("Cortical mastoidectomy (Unsafe – no cholesteatoma)", GOLD),
        arrow_bullet("Modified radical mastoidectomy (with cholesteatoma)", RED),
        arrow_bullet("Radical mastoidectomy (severe / last resort)", RED),
        SP(0.2),
        h2("CHOLESTEATOMA"),
        bullet("Squamous epithelium in middle ear", VIOLET),
        bullet("Erodes bone via collagenases", VIOLET),
        bullet("Appears as pearly-white, cheesy mass", VIOLET),
        bullet("β†’ Mastoidectomy mandatory", VIOLET),
        SP(0.2),
        hom_line("Merc sol  β€’  Silicea  β€’  Pulsatilla  β€’  Hepar sulph  β€’  Kali mur  β€’  Calc sulph"),
    ]),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 5 – Deafness + Audiogram + Tympanogram
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("7.16–7.18  |  DEAFNESS (HEARING LOSS)", "SLO: Types, Causes, Management | Hearing aids & Implants", "#00E5FF"),
    SP(0.2),
    img("05_audiogram", width=W - 2.5*cm),
    cap("Fig 5. Audiograms – Conductive HL (air-bone gap, Rinne –ve) vs SNHL (parallel elevation, Rinne +ve)."),
    SP(0.2),
    img("09_tympanogram", width=W - 2.5*cm),
    cap("Fig 6. Tympanograms – Type A (normal), Type B (flat = glue ear / perforation), Type C (negative pressure = ET dysfunction)."),
    SP(0.2),
    two_col([
        h2("CONDUCTIVE HL"),
        bullet("Problem: External/Middle ear", CYAN),
        bullet("Air-bone gap on audiogram", CYAN),
        bullet("Causes: Wax, TM perf, Otosclerosis, OME, CSOM", CYAN),
        bullet("Tuning Fork: Rinne –ve  |  Weber β†’ worse ear", CYAN),
        h2("SENSORINEURAL HL (SNHL)"),
        bullet("Problem: Cochlea / CN VIII", RED),
        bullet("No air-bone gap; both thresholds elevated", RED),
        bullet("Causes: Noise (NIHL), Presbyacusis, Ototoxic drugs", RED),
        bullet("Meniere's, Acoustic neuroma, Viral labyrinthitis", RED),
        bullet("Tuning Fork: Rinne +ve  |  Weber β†’ better ear", RED),
    ],[
        h2("INVESTIGATIONS"),
        bullet("PTA (Pure Tone Audiometry) – threshold/type", GOLD),
        bullet("Tympanometry – middle ear function", GOLD),
        bullet("OAE (Otoacoustic Emissions) – cochlear hair cells", GOLD),
        bullet("BERA/ABR – retrocochlear lesion (acoustic neuroma)", GOLD),
        bullet("Speech audiometry – discrimination", GOLD),
        bullet("MRI IAM – if retrocochlear suspected", GOLD),
        h2("MANAGEMENT"),
        bullet("Conductive: Surgery (myringoplasty, stapedectomy, grommet)", GREEN),
        bullet("SNHL: Hearing aid (analogue/digital), Cochlear implant", GREEN),
        bullet("NIHL: Prevention – ear protection, noise regulations", ORANGE),
        SP(0.1),
        hom_line("China  β€’  Causticum  β€’  Kali mur  β€’  Phosphorus  β€’  Graphites  β€’  Merc sol"),
    ]),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 6 – Tuning Fork Tests
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("TUNING FORK TESTS  |  RINNE'S  &  WEBER'S", "512 Hz fork | Clinical bedside test for type of hearing loss", "#FFD700"),
    SP(0.3),
    img("06_tuning_fork", width=W - 2.5*cm),
    cap("Fig 7. Rinne's and Weber's tuning fork test interpretation for conductive vs sensorineural hearing loss."),
    SP(0.3),
    info_table(
        ["Test", "Procedure", "Normal / SNHL", "Conductive HL", "Clinical Rule"],
        [
            ["Rinne's", "BC on mastoid β†’ then AC near ear. Which louder?",
             "Rinne +ve: AC > BC", "Rinne –ve: BC > AC (β‰₯25 dB loss)",
             "False –ve Rinne: Profound SNHL (dead ear) – confirm with Weber"],
            ["Weber's", "Fork on vertex / forehead. Where do you hear it?",
             "Midline (central)", "Lateralises to WORSE ear",
             "Unilateral SNHL: lateralises to BETTER ear"],
            ["Absolute Bone\nConduction (ABC)", "Compare patient BC to examiner BC",
             "Equal to examiner", "Equal to examiner",
             "Reduced ABC = cochlear/neural problem (SNHL)"],
        ],
        col_colors=[GOLD, LGREY, GREEN, RED, ORANGE]
    ),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 7 – Vertigo
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("7.21–7.25  |  VERTIGO  β€’  BPPV  β€’  MENIERE'S  β€’  ACOUSTIC NEUROMA", "SLO: Causes, Features, Investigations, Management", "#DA70D6"),
    SP(0.3),
    img("07_vertigo", width=W - 2.5*cm),
    cap("Fig 8. Peripheral vs Central vertigo – comparison of features, nystagmus characteristics and causes."),
    SP(0.3),
    two_col([
        h2("MENIERE'S DISEASE (7.24)"),
        bullet("Pathology: Endolymphatic hydrops (excess endolymph)", VIOLET),
        h3("Classic Triad:"),
        arrow_bullet("Episodic vertigo (20 min – 12 hr)", GOLD),
        arrow_bullet("Fluctuating low-frequency SNHL", GOLD),
        arrow_bullet("Tinnitus + aural fullness", GOLD),
        bullet("Ix: PTA, ECoG (electrocochleography), Glycerol test, MRI IAM", CYAN),
        bullet("Rx: Low-salt diet, Betahistine (Serc), Diuretics", GREEN),
        bullet("Surgery: Endolymphatic sac decompression (last resort)", ORANGE),
        hom_line("Conium  β€’  China  β€’  Cocculus  β€’  Gelsemium  β€’  Phosphorus"),
        SP(0.2),
        h2("BPPV (7.25)"),
        bullet("Canalolithiasis: otoconia in posterior semicircular canal", CYAN),
        bullet("Brief (<1 min) vertigo triggered by head position change", CYAN),
        bullet("Dix-Hallpike test +ve (latent nystagmus, fatigues)", CYAN),
        bullet("NO deafness / tinnitus", CYAN),
        bullet("Rx: Epley manoeuvre ← FIRST LINE; Vestibular rehab exercises", GREEN),
        hom_line("Cocculus  β€’  Conium  β€’  Gelsemium  β€’  Tabacum"),
    ],[
        h2("ACOUSTIC NEUROMA (Vestibular Schwannoma) (7.25)"),
        bullet("Benign tumour of CN VIII (vestibular division)", RED),
        bullet("Arises at IAM / CPA", RED),
        h3("Features (GRADUAL onset):"),
        arrow_bullet("Unilateral SNHL – often first symptom", ORANGE),
        arrow_bullet("Tinnitus (unilateral)", ORANGE),
        arrow_bullet("Vertigo (mild/late)", ORANGE),
        arrow_bullet("Facial numbness (CN V compression)", ORANGE),
        arrow_bullet("Facial palsy (CN VII – late)", ORANGE),
        arrow_bullet("Ataxia (cerebellar involvement)", ORANGE),
        h3("Investigations:"),
        bullet("MRI IAM with gadolinium ← GOLD STANDARD", GOLD),
        bullet("ABR/BERA – wave V latency prolonged", GOLD),
        bullet("PTA – unilateral SNHL", GOLD),
        h3("Management:"),
        bullet("Small (<1.5 cm): Observation (serial MRI)", GREEN),
        bullet("Radiosurgery: Gamma knife / CyberKnife", GREEN),
        bullet("Surgery: Translabyrinthine approach (large tumours)", GREEN),
        hom_line("Phosphorus  β€’  Conium  β€’  Gelsemium"),
    ]),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 8 – Facial Palsy / Bell's Palsy
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("7.26  |  FACIAL PARALYSIS  β€’  BELL'S PALSY", "SLO: Aetiology, Pathology, Clinical Features, Investigation, Management", "#FFA500"),
    SP(0.3),
    img("08_facial_nerve", width=W - 2.5*cm),
    cap("Fig 9. CN VII course from motor cortex to terminal branches, UMN vs LMN palsy features, and Bell's Palsy management."),
    SP(0.3),
    two_col([
        h2("CN VII TERMINAL BRANCHES"),
        bullet("To Frankie Zulu Both Colours Match  (mnemonic)", GOLD),
        arrow_bullet("Temporal", VIOLET),
        arrow_bullet("Zygomatic", VIOLET),
        arrow_bullet("Buccal", VIOLET),
        arrow_bullet("(Marginal) Mandibular", VIOLET),
        arrow_bullet("Cervical", VIOLET),
        SP(0.2),
        h2("UMN vs LMN – KEY DIFFERENCE"),
        bullet("UMN (above nucleus / cortex lesion): Forehead SPARED", CYAN),
        bullet("LMN (nucleus or peripheral): Forehead INVOLVED", RED),
        bullet("Forehead has bilateral cortical representation β†’ spared in UMN strokes", CYAN),
        SP(0.2),
        h2("CAUSES"),
        h3("LMN (peripheral):"),
        bullet("Bell's Palsy (idiopathic, HSV-1) – COMMONEST", GREEN),
        bullet("Ramsay Hunt syndrome (VZV – vesicles in pinna/EAC)", RED),
        bullet("Acoustic neuroma, Parotid tumour", ORANGE),
        bullet("Temporal bone fracture, CSOM cholesteatoma", ORANGE),
        bullet("Lyme disease, Sarcoidosis, GBS", LGREY),
        h3("UMN (central):"),
        bullet("Stroke (CVA) – most common UMN cause", RED),
        bullet("Brain tumour, MS, MND", ORANGE),
    ],[
        h2("BELL'S PALSY"),
        bullet("Idiopathic LMN facial palsy", ORANGE),
        bullet("Aetiology: HSV-1 reactivation β†’ nerve oedema in bony canal", ORANGE),
        h3("Clinical Features:"),
        arrow_bullet("Sudden unilateral LMN palsy", RED),
        arrow_bullet("Forehead INVOLVED (cannot wrinkle)", RED),
        arrow_bullet("Lagophthalmos (incomplete eye closure)", RED),
        arrow_bullet("Bell's phenomenon: eyeball rolls upward", RED),
        arrow_bullet("Hyperacusis (stapedius branch affected)", ORANGE),
        arrow_bullet("Taste loss: anterior 2/3 tongue (chorda tympani)", ORANGE),
        arrow_bullet("Lacrimation Β± (greater petrosal nerve)", ORANGE),
        h3("Investigations:"),
        bullet("Clinical diagnosis (rule out other causes)", CYAN),
        bullet("Rule out Ramsay Hunt (vesicles), check BP, glucose", CYAN),
        bullet("MRI if atypical / slow recovery", CYAN),
        bullet("Grading: House-Brackmann Scale I (normal) β†’ VI (complete)", GOLD),
        h3("Management:"),
        bullet("Prednisolone 60 mg/day Γ— 5 days β†’ taper (start <72 h)", GREEN),
        bullet("Acyclovir 400 mg 5Γ— daily if severe or HSV suspected", GREEN),
        bullet("Eye protection: lubricating drops + tape/patch at night", GREEN),
        bullet("Physiotherapy (facial exercises)", GREEN),
        bullet("Prognosis: ~80% full recovery within 3–6 months", GOLD),
        SP(0.1),
        hom_line("Causticum  β€’  Aconite  β€’  Gelsemium  β€’  Dulcamara  β€’  Cadmium sulph"),
    ]),
    PageBreak(),
]

# ════════════════════════════════════════════════════════════════════════════
#  PAGE 9 – QUICK REFERENCE TABLE (all topics)
# ════════════════════════════════════════════════════════════════════════════
story += [
    section_header("QUICK REFERENCE  –  ALL EAR TOPICS  (Unit 7)", "Complete summary table for rapid revision", "#FFD700"),
    SP(0.3),
    info_table(
        ["Topic", "Key Aetiology", "Key Features", "Ix", "Management", "Homoeopathic"],
        [
            ["ASOM", "Strep pneu / H. inf\nURTI β†’ ET route",
             "Otalgia, fever, deafness\nBulging TM",
             "Otoscopy\nAudiometry",
             "Abx + Analgesics\nMyringotomy",
             "Belladonna\nHepar sulph\nPulsatilla"],
            ["CSOM – Safe", "Unresolved ASOM\nET dysfunction",
             "Central perforation\nMucoid discharge\nConductive HL",
             "Otoscopy\nPTA, CT",
             "Aural toilet\nTympanoplasty",
             "Pulsatilla, Kali mur\nMerc sol"],
            ["CSOM – Unsafe", "As above +\nCholesteatoma",
             "Marginal/attic perf\nFoul discharge\nBone erosion",
             "HRCT temporal\nbone, PTA",
             "Mastoidectomy",
             "Silicea, Hepar sulph\nCalc sulph"],
            ["Glue Ear (OME)", "ET dysfunction\nAdenoids",
             "Bilateral HL in child\nFlat tympanogram\nNo pain",
             "Tympanometry\n(Type B flat)",
             "Grommet + Adenoidectomy",
             "Kali mur, Pulsatilla\nMerc sol"],
            ["OE / Furunculosis", "Pseudomonas\nStaph aureus",
             "Otalgia\nItching\nTragus tenderness",
             "Otoscopy\nC&S swab",
             "Aural toilet\nAntibiotic drops",
             "Belladonna\nCapsicum, Sulphur"],
            ["Otalgia", "Local or referred\n(dental/tonsil)",
             "Ear pain Β± discharge\nRule out referred causes",
             "ENT + dental exam\nOPG",
             "Treat cause",
             "Chamomilla\nAconite, Pulsatilla"],
            ["Otorrhea", "ASOM/CSOM/OE\nCholesteatoma",
             "Character β†’ diagnosis\n(foul = cholesteatoma)",
             "Otoscopy, C&S\nCT if needed",
             "Aural toilet\nAbx drops, Surgery",
             "Pulsatilla, Silicea\nGraphites"],
            ["Deafness – Conductive", "Wax, perf, otosclerosis\nOME",
             "Air-bone gap\nRinne –ve\nWeber β†’ worse",
             "PTA, Tympanometry",
             "Surgery / Grommet\nHearing aid",
             "China, Causticum\nKali mur"],
            ["Deafness – SNHL", "Noise, aging, ototoxins\nMeniere's, neuroma",
             "No air-bone gap\nRinne +ve\nHigh-freq first",
             "PTA, OAE, BERA\nMRI IAM",
             "Hearing aid\nCochlear implant",
             "China, Phosphorus\nCarbo veg"],
            ["Tinnitus", "NIHL, Meniere's\nOtosclerosis, ototoxins",
             "Sound without source\nSubjective (common)\nObjective (rare)",
             "PTA, MRI IAM",
             "TRT, Sound therapy\nTreat cause",
             "China, Nat sal\nCarbon sulph"],
            ["Vertigo – Peripheral", "BPPV, Meniere's\nLabyrinthitis",
             "Episodic, severe\nHorizontal nystagmus\nDeafness Β±",
             "PTA, Caloric test\nDix-Hallpike",
             "Epley (BPPV)\nBetahistine, Physio",
             "Cocculus, Conium\nGelsemium"],
            ["Vertigo – Central", "VBI, cerebellar stroke\nMS, tumour",
             "Constant, neurological\nVertical nystagmus",
             "MRI brain",
             "Treat cause",
             "Conium, Gelsemium"],
            ["Meniere's Disease", "Endolymphatic hydrops",
             "Triad: Vertigo+\nSNHL+Tinnitus",
             "ECoG, Glycerol test\nPTA, MRI IAM",
             "Low salt + Betahistine\nEndolymph sac decomp.",
             "China, Conium\nCocculus, Phosphorus"],
            ["Acoustic Neuroma", "CN VIII schwannoma\nCPA/IAM",
             "Unilateral SNHL\nTinnitus, ataxia\nFacial numbness",
             "MRI IAM (gold std)\nABR, PTA",
             "Radiosurgery\nSurgery",
             "Phosphorus, Conium"],
            ["Bell's Palsy", "Idiopathic LMN\nHSV-1 reactivation",
             "Sudden unilateral palsy\nForehead INVOLVED\nLagophthalmos",
             "Clinical\nRule out Ramsay Hunt",
             "Prednisolone <72 h\nAcyclovir, Eye care",
             "Causticum, Aconite\nGelsemium, Dulcamara"],
        ],
        col_colors=[GOLD, RED, ORANGE, CYAN, GREEN, HexColor("#7BFF6B")]
    ),
]

# ── build PDF ─────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUT_PDF,
    pagesize=landscape(A4),
    leftMargin=1.2*cm, rightMargin=1.2*cm,
    topMargin=1.2*cm, bottomMargin=1.2*cm,
    title="EAR Surgery Notes – Hom UG-Sur-II Unit 7",
    author="Comprehensive Medical Notes",
    subject="EAR – Surgery Syllabus",
)

doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"PDF saved: {OUT_PDF}")
import os
size = os.path.getsize(OUT_PDF) / 1024
print(f"File size: {size:.1f} KB")
Running Command

cd /tmp/workspace/ear-notes && python3 build_ear_pdf.py 2>&1

Running Command

cd /tmp/workspace/ear-notes && python3 assemble_pdf.py 2>&1

Running Command

cd /tmp/workspace/ear-notes && python3 -c " from PIL import Image as PILImage import os for f in sorted(os.listdir('diagrams')): if f.endswith('.png'): im = PILImage.open(f'diagrams/{f}') print(f'{f}: {im.size}') "

Editing File

~/ear-notes/assemble_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/ear-notes/assemble_pdf.py'
Running Command

head -5 /tmp/workspace/ear-notes/assemble_pdf.py

Editing File

~/ear-notes/assemble_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/ear-notes/assemble_pdf.py'
Editing File

~/ear-notes/assemble_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/ear-notes/assemble_pdf.py'
Running Command

cd /tmp/workspace/ear-notes && python3 assemble_pdf.py 2>&1

Running Command

cd /tmp/workspace/ear-notes && python3 -c " from pypdf import PdfReader r = PdfReader('EAR_Surgery_Comprehensive_Notes.pdf') print(f'Pages: {len(r.pages)}') print(f'Title: {r.metadata.title}') "

Your comprehensive EAR Surgery Notes PDF is ready!

EAR Surgery Comprehensive Notes

PDF Document Β· PDF


πŸ“„ What's Inside – 19 Pages | 1.3 MB | A4 Landscape | Dark Theme

9 Original Schematic Diagrams:

FigDiagramWhat it shows
1Cross-Section of the EarExternal β†’ Middle β†’ Inner ear with all labelled structures, ossicular chain, cochlea spiral, semicircular canals, sound pathway arrow
2Otoscopic TM Views Γ—3Normal TM (pearly grey, cone of light) / ASOM (bulging, red) / CSOM (central perforation)
3ASOM 5-Stage FlowchartColour-coded boxes: Hyperaemia β†’ Exudation β†’ Suppuration β†’ Resolution/Perforation β†’ Complication
4CSOM Safe vs UnsafeSide-by-side TM diagrams with cholesteatoma in unsafe type
5AudiogramConductive HL (air-bone gap, Rinne -ve) vs SNHL (parallel elevation, Rinne +ve)
6Tuning Fork TestsRinne's + Weber's with visual fork diagrams and interpretation table
7Peripheral vs Central Vertigo10-point comparison table in one diagram
8CN VII Course + Bell's PalsyFull nerve pathway flowchart, UMN vs LMN panel, Bell's Palsy details
9Tympanograms A / B / CType A (normal), B (flat = glue ear), C (neg. pressure = ET catarrh)

Note sections cover all 26 SLOs:

  • Anatomy & Physiology (7.1–7.2) with Physiology pathway
  • External Ear diseases (7.5–7.6) - OE, furunculosis, wax, neoplasm
  • ASOM (7.7–7.8) with full 5-stage progression
  • CSOM Safe & Unsafe (7.9–7.10) with cholesteatoma, complications, surgery
  • Deafness types with audiogram interpretation (7.16–7.18)
  • Tinnitus causes, types, management (7.19–7.20)
  • Vertigo, BPPV, Meniere's, Acoustic Neuroma (7.21–7.25)
  • Bell's Palsy with CN VII course (7.26)
  • Final Quick Reference Table - all 15 topics in one sheet
  • Homoeopathic Remedies included on every topic page
This is a shared conversation. Sign in to Orris to start your own chat.