Prepare a flow chart of diagnostic algorithm of women with abdominal mass

Reading File
Running Command

python3 -c " import pdfplumber with pdfplumber.open('/tmp/workspace/attachments/5bc63290-afb4-4076-a271-855cf69448af/Fibromyoma.pdf') as pdf: print(f'Pages: {len(pdf.pages)}') for i, page in enumerate(pdf.pages[:5]): print(f'--- Page {i+1} ---') print(page.extract_text()[:2000]) "

Running Command

python3 -c " import pdfplumber with pdfplumber.open('/tmp/workspace/attachments/5bc63290-afb4-4076-a271-855cf69448af/Fibromyoma.pdf') as pdf: for i, page in enumerate(pdf.pages[5:20]): print(f'--- Page {i+6} ---') t = page.extract_text() if t: print(t[:1500]) print() "

Running Command

python3 -c " import pdfplumber with pdfplumber.open('/tmp/workspace/attachments/5bc63290-afb4-4076-a271-855cf69448af/Fibromyoma.pdf') as pdf: for i, page in enumerate(pdf.pages[20:47]): print(f'--- Page {i+21} ---') t = page.extract_text() if t: print(t[:2000]) print() "

Running Command

mkdir -p /tmp/workspace/abdominal-mass-flowchart && ls /tmp/workspace/abdominal-mass-flowchart/

Writing File

~/abdominal-mass-flowchart/flowchart.py

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch

fig, ax = plt.subplots(1, 1, figsize=(28, 42))
ax.set_xlim(0, 28)
ax.set_ylim(0, 42)
ax.axis('off')
fig.patch.set_facecolor('#F0F4F8')

# ── colour palette ──────────────────────────────────────────────
C = {
    'title':     '#1A237E',
    'start':     '#283593',
    'history':   '#1565C0',
    'exam':      '#0277BD',
    'bedside':   '#00695C',
    'invest':    '#558B2F',
    'origin':    '#E65100',
    'gynae':     '#AD1457',
    'uterine':   '#C62828',
    'adnexal':   '#6A1B9A',
    'ovarian':   '#4527A0',
    'nongynae':  '#37474F',
    'uro':       '#2E7D32',
    'gi':        '#F57F17',
    'retro':     '#4E342E',
    'arrow':     '#455A64',
    'text_dark': '#212121',
    'text_light':'#FFFFFF',
    'leaf':      '#E8F5E9',
    'leaf2':     '#FFF3E0',
}

def box(ax, x, y, w, h, text, fc='#E3F2FD', ec='#1565C0', tc='#212121',
        fs=8.5, bold=False, radius=0.3, ha='center', va='center', lw=1.4):
    rect = FancyBboxPatch((x - w/2, y - h/2), w, h,
                          boxstyle=f"round,pad={radius}",
                          facecolor=fc, edgecolor=ec, linewidth=lw, zorder=3)
    ax.add_patch(rect)
    weight = 'bold' if bold else 'normal'
    ax.text(x, y, text, ha=ha, va=va, fontsize=fs, color=tc,
            fontweight=weight, wrap=True, zorder=4,
            multialignment='center')

def diamond(ax, x, y, w, h, text, fc='#FFF9C4', ec='#F57F17', tc='#212121', fs=8):
    dx, dy = w/2, h/2
    pts = [(x, y+dy), (x+dx, y), (x, y-dy), (x-dx, y)]
    poly = plt.Polygon(pts, closed=True, facecolor=fc, edgecolor=ec, linewidth=1.6, zorder=3)
    ax.add_patch(poly)
    ax.text(x, y, text, ha='center', va='center', fontsize=fs,
            color=tc, fontweight='bold', zorder=4, multialignment='center')

def arr(ax, x1, y1, x2, y2, label='', color='#455A64', lw=1.5):
    ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
                arrowprops=dict(arrowstyle='->', color=color,
                                lw=lw, connectionstyle='arc3,rad=0.0'),
                zorder=2)
    if label:
        mx, my = (x1+x2)/2, (y1+y2)/2
        ax.text(mx+0.1, my, label, fontsize=7.5, color=color, fontstyle='italic', zorder=5)

# ════════════════════════════════════════════════════════════════
# TITLE
# ════════════════════════════════════════════════════════════════
ax.text(14, 41.2, 'DIAGNOSTIC ALGORITHM', ha='center', va='center',
        fontsize=22, fontweight='bold', color=C['title'], zorder=5)
ax.text(14, 40.6, 'Abdominal / Pelvic Mass in Women', ha='center', va='center',
        fontsize=14, color='#37474F', zorder=5)

# ─── TOP: Presentation ──────────────────────────────────────────
box(ax, 14, 39.7, 9, 0.8,
    'WOMAN PRESENTS WITH ABDOMINAL / PELVIC MASS',
    fc=C['start'], ec=C['start'], tc='white', fs=11, bold=True)

arr(ax, 14, 39.3, 14, 38.6)

# ─── History ────────────────────────────────────────────────────
box(ax, 14, 38.1, 11, 0.9,
    'HISTORY  ▸  Age · Parity · Menstrual Hx · Pain · AUB · Urinary/Bowel Sx\n'
    'Weight loss · Fever · Family Hx (Ca) · OCP/HRT use · Subfertility',
    fc='#E3F2FD', ec=C['history'], fs=8.5, bold=False)

arr(ax, 14, 37.65, 14, 36.9)

# ─── Physical Examination ───────────────────────────────────────
box(ax, 14, 36.4, 12, 0.9,
    'PHYSICAL EXAMINATION  ▸  Site · Size · Surface · Consistency · Mobility\n'
    'P/V & P/R Bimanual · Fothergill\'s Sign · Cervical Motion Tenderness · Ascites',
    fc='#E3F2FD', ec=C['exam'], fs=8.5)

arr(ax, 14, 35.95, 14, 35.3)

# ─── Bedside diagnostics ────────────────────────────────────────
box(ax, 14, 34.85, 13, 0.8,
    'BEDSIDE DIAGNOSTICS  ▸  Mass below umbilicus vs above · Uterus moves with cervix?\n'
    'Resonant vs dull percussion · Fothergill\'s sign · Bladder catheterisation if midline',
    fc='#E0F2F1', ec=C['bedside'], fs=8.5)

arr(ax, 14, 34.45, 14, 33.75)

# ─── First-line investigations ──────────────────────────────────
box(ax, 14, 33.25, 12, 0.9,
    '1st-LINE INVESTIGATIONS  ▸  USG Abdomen & Pelvis (TVS preferred)\n'
    'CBC · CRP/ESR · Urine hCG (exclude pregnancy) · Serum CA-125',
    fc='#F1F8E9', ec=C['invest'], fs=8.5)

arr(ax, 14, 32.8, 14, 32.1)

# ─── DIAMOND: Origin? ───────────────────────────────────────────
diamond(ax, 14, 31.5, 5.5, 1.2,
        'Determine\nOrgan of Origin?',
        fc='#FFFDE7', ec=C['origin'])

# ── Branch arrows ────────────────────────────────────────────────
# Left (Gynaecological)
ax.annotate('', xy=(5.5, 31.5), xytext=(11.25, 31.5),
            arrowprops=dict(arrowstyle='->', color=C['gynae'], lw=1.8), zorder=2)
ax.text(8.0, 31.7, 'GYNAECOLOGICAL', ha='center', fontsize=8.5, color=C['gynae'], fontweight='bold')

# Right (Non-gynaecological)
ax.annotate('', xy=(22.5, 31.5), xytext=(16.75, 31.5),
            arrowprops=dict(arrowstyle='->', color=C['nongynae'], lw=1.8), zorder=2)
ax.text(19.8, 31.7, 'NON-GYNAECOLOGICAL', ha='center', fontsize=8.5, color=C['nongynae'], fontweight='bold')

# ════════════════════════════════════════════════════════════════
#  LEFT COLUMN – GYNAECOLOGICAL
# ════════════════════════════════════════════════════════════════
box(ax, 5.5, 30.9, 5, 0.7, 'GYNAECOLOGICAL\nORIGIN',
    fc=C['gynae'], ec=C['gynae'], tc='white', fs=9, bold=True)

arr(ax, 5.5, 30.55, 5.5, 29.85)

# Uterine vs Adnexal diamond
diamond(ax, 5.5, 29.2, 5, 1.1, 'Uterine\nor Adnexal?',
        fc='#FCE4EC', ec=C['gynae'])

# ── UTERINE (left of diamond) ────────────────────────────────────
ax.annotate('', xy=(1.8, 29.2), xytext=(3.0, 29.2),
            arrowprops=dict(arrowstyle='->', color=C['uterine'], lw=1.6), zorder=2)
ax.text(2.4, 29.45, 'UTERINE', ha='center', fontsize=7.5, color=C['uterine'], fontweight='bold')

box(ax, 1.8, 28.6, 3.0, 0.9,
    'UTERINE CAUSES\n(moves with cervix;\nno groove btw uterus & mass)',
    fc='#FFEBEE', ec=C['uterine'], fs=7.5)

arr(ax, 1.8, 28.15, 1.8, 27.55)

# Uterine sub-boxes
for i, (txt, clr) in enumerate([
    ('FIBROMYOMA\n▸ Firm-hard, irregular\n▸ Nodular surface\n▸ Restricted mobility\n▸ AUB / Menorrhagia\n▸ TVS: hypo/hyperechoic\n▸ MRI: exact mapping', '#FFCDD2'),
    ('ADENOMYOSIS\n▸ Symmetrical, tender\n▸ ≤12wk size\n▸ Menorrhagia +\n   dysmenorrhoea\n▸ MRI (choice)\n▸ TVS: ill-defined hypoechoic', '#FFCDD2'),
    ('PYOMETRA\n▸ Uniform suprapubic\n▸ Purulent discharge\n▸ USG: fluid-filled cavity\n▸ R/O endometrial/\n   cervical Ca', '#FFCDD2'),
]):
    ypos = 26.8 - i*1.7
    box(ax, 1.8, ypos, 3.2, 1.35, txt,
        fc=clr, ec=C['uterine'], fs=6.8, bold=False)
    if i < 2:
        arr(ax, 1.8, ypos - 0.68, 1.8, ypos - 1.02)

# ── ADNEXAL (right of diamond) ───────────────────────────────────
ax.annotate('', xy=(9.0, 29.2), xytext=(8.0, 29.2),
            arrowprops=dict(arrowstyle='->', color=C['adnexal'], lw=1.6), zorder=2)
ax.text(8.5, 29.45, 'ADNEXAL', ha='center', fontsize=7.5, color=C['adnexal'], fontweight='bold')

# Adnexal box
box(ax, 9.5, 28.6, 3.5, 0.9,
    'ADNEXAL / TUBAL-OVARIAN\n(separate from uterus;\ncervix does NOT move with mass)',
    fc='#EDE7F6', ec=C['adnexal'], fs=7.5)

arr(ax, 9.5, 28.15, 9.5, 27.5)

# Adnexal causes
adnexal = [
    ('HYDROSALPINX\n▸ Thin-walled tubular\n   cystic mass\n▸ TVS: beads-on-a-string\n▸ HSG: distal obstruction\n▸ Laparoscopy (gold std)', '#D1C4E9'),
    ('TUBO-OVARIAN\nABSCESS\n▸ Fever + adnexal mass\n▸ CМT +ve\n▸ WBC↑ / CRP↑\n▸ TVS: complex thick-\n  walled cystic mass\n▸ CT if uncertain', '#D1C4E9'),
    ('OVARIAN MASS\n(see right column\nfor full algorithm)', '#D1C4E9'),
]
for i, (txt, clr) in enumerate(adnexal):
    ypos = 26.7 - i * 1.75
    box(ax, 9.5, ypos, 3.4, 1.4, txt,
        fc=clr, ec=C['adnexal'], fs=6.8)
    if i < 2:
        arr(ax, 9.5, ypos - 0.70, 9.5, ypos - 1.05)

# ════════════════════════════════════════════════════════════════
#  CENTRE-RIGHT COLUMN – OVARIAN ALGORITHM
# ════════════════════════════════════════════════════════════════
box(ax, 14, 30.9, 5, 0.7, 'OVARIAN MASS ALGORITHM',
    fc='#4527A0', ec='#4527A0', tc='white', fs=9, bold=True)

arr(ax, 14, 30.55, 14, 29.85)

# Advanced investigations
box(ax, 14, 29.3, 5.8, 0.9,
    'ADVANCED INVESTIGATIONS\n'
    'TVS + Colour Doppler · MRI pelvis · CT abdomen/pelvis\n'
    'Tumour markers: CA-125 · AFP · β-hCG · LDH · Inhibin',
    fc='#EDE7F6', ec=C['ovarian'], fs=7.8)

arr(ax, 14, 28.85, 14, 28.15)

diamond(ax, 14, 27.5, 5.5, 1.2,
        'Functional\nor Neoplastic?',
        fc='#E8EAF6', ec=C['ovarian'])

# Functional left
ax.annotate('', xy=(10.2, 27.5), xytext=(11.25, 27.5),
            arrowprops=dict(arrowstyle='->', color=C['ovarian'], lw=1.5), zorder=2)
ax.text(10.7, 27.7, 'FUNCTIONAL', ha='center', fontsize=7.5, color=C['ovarian'], fontweight='bold')

# Neoplastic right
ax.annotate('', xy=(17.8, 27.5), xytext=(16.75, 27.5),
            arrowprops=dict(arrowstyle='->', color=C['ovarian'], lw=1.5), zorder=2)
ax.text(17.3, 27.7, 'NEOPLASTIC', ha='center', fontsize=7.5, color=C['ovarian'], fontweight='bold')

# Functional cysts
box(ax, 9.8, 26.2, 4.0, 2.0,
    'FUNCTIONAL CYSTS\n\nFollicular Cyst\n▸ <3 cm: no Ix\n▸ <7 cm, simple: repeat USG 3-6/12\n▸ >7 cm or persistent: cystectomy\n\nCorpus Luteum Cyst\n▸ hCG, USG, culdocentesis\n▸ Often spontaneous regression\n▸ Rupture: laparoscopy\n\nTheca Lutein Cyst\n▸ Bilateral; assoc. GTD\n▸ Suppress with COCPs',
    fc='#E8EAF6', ec=C['ovarian'], fs=6.8)

# Neoplastic
box(ax, 18.2, 26.2, 5.0, 2.0,
    'OVARIAN NEOPLASM\n\nBENIGN\n▸ Serous/Mucinous cystadenoma\n▸ Dermoid cyst (Mature teratoma)\n▸ Fibroma, Thecoma\n▸ Endometrioma (chocolate cyst)\n\nMALIGNANT\n▸ Epithelial Ca (serous/mucinous/\n   endometrioid/clear cell)\n▸ Germ cell (Dysgerminoma etc.)\n▸ Sex cord-stromal (GCT, SLCT)\n▸ Metastatic (Krukenberg)',
    fc='#EDE7F6', ec='#311B92', fs=6.8)

arr(ax, 18.2, 25.2, 18.2, 24.5)

# RMI assessment box
box(ax, 18.2, 23.9, 5.0, 1.0,
    'RISK OF MALIGNANCY ASSESSMENT\n'
    'RMI / IOTA criteria / ADNEX model\n'
    'CT chest-abdomen-pelvis for staging',
    fc='#311B92', ec='#1A0072', tc='white', fs=7.5, bold=False)

arr(ax, 18.2, 23.4, 18.2, 22.7)

box(ax, 18.2, 22.3, 5.0, 0.75,
    'DEFINITIVE MANAGEMENT\n'
    'Benign: Cystectomy / Oophorectomy\n'
    'Malignant: TAH+BSO+Omentectomy ± Chemo',
    fc='#E8EAF6', ec='#311B92', fs=7.5)

# ════════════════════════════════════════════════════════════════
#  RIGHT COLUMN – NON-GYNAECOLOGICAL
# ════════════════════════════════════════════════════════════════
box(ax, 22.5, 30.9, 5, 0.7, 'NON-GYNAECOLOGICAL\nORIGIN',
    fc=C['nongynae'], ec=C['nongynae'], tc='white', fs=9, bold=True)

arr(ax, 22.5, 30.55, 22.5, 29.85)

non_gynae = [
    ('UROLOGICAL\n▸ Distended bladder (midline, dull,\n   disappears on catheterisation)\n▸ Pelvic kidney · Hydronephrosis\n▸ Polycystic kidney · Renal Ca\n→ CT urogram / Urology referral',
     '#E8F5E9', C['uro']),
    ('GASTROINTESTINAL\n▸ Appendicular mass/abscess\n   (RIF, tender, fever, WBC↑)\n▸ Cecal / Sigmoid carcinoma\n   (irregular, hard, bowel habit ΔΔ)\n▸ GIST\n→ CT / Colonoscopy / FNAC',
     '#FFF8E1', C['gi']),
    ('RETROPERITONEAL /\nABDOMINAL WALL\n▸ Mesenteric cyst (resonant,\n   perpendicular mobility)\n▸ Encysted ascites\n▸ Retro sarcoma / Lymphoma\n▸ Desmoid tumor\n→ CT / MRI / Image-guided biopsy',
     '#EFEBE9', C['retro']),
]
for i, (txt, fc, ec) in enumerate(non_gynae):
    ypos = 29.0 - i * 2.2
    box(ax, 22.5, ypos, 5.0, 1.8, txt, fc=fc, ec=ec, fs=7.0)
    if i < 2:
        arr(ax, 22.5, ypos - 0.90, 22.5, ypos - 1.30)

# ════════════════════════════════════════════════════════════════
#  BOTTOM: INVESTIGATION SUMMARY PANEL
# ════════════════════════════════════════════════════════════════
# Horizontal divider
ax.plot([0.5, 27.5], [20.3, 20.3], color='#90A4AE', lw=1.5, linestyle='--')

box(ax, 14, 20.0, 27, 0.45,
    'INVESTIGATION SUMMARY TABLE',
    fc='#37474F', ec='#37474F', tc='white', fs=10, bold=True)

inv_cols = [
    ('IMAGING', '#1565C0', '#E3F2FD',
     'USG Abdomen/Pelvis (TVS)\n→ 1st-line; cystic vs solid\n\nMRI Pelvis\n→ Tissue characterisation;\n   adenomyosis vs fibroid\n\nCT Abdomen/Pelvis\n→ Staging; lymph nodes;\n   retro / GIT masses'),
    ('TUMOUR MARKERS', '#6A1B9A', '#F3E5F5',
     'CA-125 → Epithelial ovarian Ca\nAFP → Yolk sac tumor\nβ-hCG → Choriocarcinoma\nLDH → Dysgerminoma\nInhibin → Granulosa cell tumor\nCEA → Mucinous tumors\nCA 19-9 → Mucinous tumors'),
    ('SPECIAL PROCEDURES', '#00695C', '#E0F2F1',
     'Diagnostic Laparoscopy\n→ Gold std for adnexal/tubal\n   pathology; pelvic endo\n\nHysteroscopy\n→ Intracavitary fibroids;\n   endometrial biopsy\n\nSIS / HSG\n→ Submucous fibroids;\n   tubal patency'),
    ('LABS & OTHERS', '#E65100', '#FFF3E0',
     'CBC → Anemia / Infection\nLFT → Liver metastasis\nRFT → Pre-chemo baseline\nUrine hCG → R/O pregnancy\nEndocervical swabs → PID\nAscitic fluid cytology\nHistopathology (gold std)'),
]

for i, (hdr, ec, fc, body) in enumerate(inv_cols):
    x = 1.5 + i * 6.5
    box(ax, x, 19.5, 6.0, 0.5, hdr, fc=ec, ec=ec, tc='white', fs=8.5, bold=True)
    box(ax, x, 17.7, 6.0, 3.2, body, fc=fc, ec=ec, fs=7.5, va='center')

# ── Arrows from bottom of main algo to summary panel
arr(ax, 14, 22.0, 14, 20.25, color='#78909C')

# ════════════════════════════════════════════════════════════════
#  FOTHERGILL's & CLINICAL CLUES panel
# ════════════════════════════════════════════════════════════════
ax.plot([0.5, 27.5], [15.8, 15.8], color='#90A4AE', lw=1.5, linestyle='--')

box(ax, 14, 15.55, 27, 0.4,
    'KEY CLINICAL CLUES & DIFFERENTIATING SIGNS',
    fc='#263238', ec='#263238', tc='white', fs=10, bold=True)

clues = [
    ('Mass moves with cervix', 'Uterine origin', '#C62828'),
    ('Mass separate; cervix fixed', 'Ovarian/adnexal origin', '#6A1B9A'),
    ('Disappears on catheterisation', 'Distended bladder', '#2E7D32'),
    ('Fothergill\'s sign +ve\n(persists on rectus contraction)', 'Abdominal wall mass', '#1565C0'),
    ('Resonant percussion over mass', 'Mesenteric/bowel origin', '#4E342E'),
    ('Bilateral, cystic, rapid onset', 'Consider theca lutein / GTD', '#0277BD'),
    ('Menorrhagia + worsening dysmenorrhoea', 'Adenomyosis strongly suggested', '#AD1457'),
    ('Irregular, firm, nodular uterus', 'Fibromyoma', '#C62828'),
]
cols = 4
for i, (sign, dx, clr) in enumerate(clues):
    col = i % cols
    row = i // cols
    x = 2.0 + col * 6.5
    y = 14.8 - row * 1.05
    box(ax, x, y, 6.1, 0.85, f'Sign: {sign}\n→ Dx: {dx}',
        fc='#FAFAFA', ec=clr, fs=7.5, bold=False)

# ════════════════════════════════════════════════════════════════
#  MANAGEMENT SUMMARY PANEL
# ════════════════════════════════════════════════════════════════
ax.plot([0.5, 27.5], [12.6, 12.6], color='#90A4AE', lw=1.5, linestyle='--')

box(ax, 14, 12.35, 27, 0.4,
    'MANAGEMENT PRINCIPLES BY DIAGNOSIS',
    fc='#1B5E20', ec='#1B5E20', tc='white', fs=10, bold=True)

mgmt = [
    ('FIBROMYOMA', '#C62828', '#FFEBEE',
     'Small/Asymptomatic: Observation\nMedical: GnRH agonists, LNG-IUS\nSurgical: Myomectomy (fertility)\nHysterectomy (definitive)'),
    ('ADENOMYOSIS', '#AD1457', '#FCE4EC',
     'NSAIDs; GnRH agonists; Mirena LNG-IUS\nHysterectomy (definitive)\nAdenomyomectomy (fertility-sparing)\nMRgFUS (emerging)'),
    ('TOA / HYDROSALPINX', '#37474F', '#ECEFF1',
     'TOA: IV antibiotics 14 days;\ndrainage if ≥8 cm or no response\nHydrosalpinx: Salpingectomy;\nIVF for severe tubal disease'),
    ('OVARIAN CYST\n(Benign)', '#4527A0', '#EDE7F6',
     'Functional: Expectant / COCPs\n<7 cm simple: USG in 3-6/12\nPersistent/large: Cystectomy\n(lap or open)'),
    ('OVARIAN Ca\n(Malignant)', '#311B92', '#E8EAF6',
     'TAH + BSO + Omentectomy +\nperitoneal washings\nFertility-sparing (Stage Ia, young)\nChemo: Carboplatin + Paclitaxel\nTargeted: Bevacizumab, PARP inh.'),
    ('NON-GYNAECOLOGICAL', '#263238', '#ECEFF1',
     'Bladder: Catheterise + Urology\nAppendix mass: Ochsner-Sherren;\nInterval appendectomy\nBowel Ca: Hemicolectomy\nTB mass: ATT'),
]

for i, (hdr, ec, fc, body) in enumerate(mgmt):
    col = i % 3
    row = i // 3
    x = 2.5 + col * 8.5
    y = 11.3 - row * 2.5
    box(ax, x, y, 7.8, 0.5, hdr, fc=ec, ec=ec, tc='white', fs=8.5, bold=True)
    box(ax, x, y - 0.9, 7.8, 1.6, body, fc=fc, ec=ec, fs=7.5, va='center')

# ── Footer ───────────────────────────────────────────────────────
ax.text(14, 0.35,
        'Sources: DC Dutta\'s Textbook of Gynaecology · Shaw\'s Textbook of Gynaecology · Williams Gynaecology',
        ha='center', fontsize=7, color='#607D8B', style='italic')

plt.tight_layout(pad=0.2)
plt.savefig('/tmp/workspace/abdominal-mass-flowchart/diagnostic_algorithm.png',
            dpi=180, bbox_inches='tight', facecolor=fig.get_facecolor())
print("Saved successfully.")
Running Command

cd /tmp/workspace/abdominal-mass-flowchart && python3 flowchart.py 2>&1

Loading Image
Running Command

ls -lh /tmp/workspace/abdominal-mass-flowchart/

The file was generated successfully (1.4 MB PNG). Here is your flowchart:

diagnostic algorithm

Image · PNG


What the flowchart covers (based on your PDF)

The diagnostic algorithm is organized into 5 tiers:

Tier 1 - Clinical entry

  • History (age, parity, menstrual history, AUB, pressure symptoms, family history)
  • Physical examination (site, size, surface, consistency, mobility, P/V bimanual, P/R)
  • Bedside diagnostics (Fothergill's sign, percussion, catheterisation test, cervical movement)

Tier 2 - First-line investigations

  • USG abdomen & pelvis (TVS preferred), CBC, CRP/ESR, urine hCG, serum CA-125

Tier 3 - Determine organ of origin (decision diamond)

Gynaecological branch:
  • Uterine (moves with cervix, no groove between uterus and mass):
    • Fibromyoma - firm-hard, nodular, restricted mobility, AUB
    • Adenomyosis - symmetrical tender uterus ≤12 weeks size, menorrhagia + progressive dysmenorrhoea
    • Pyometra - purulent discharge, USG fluid-filled cavity, rule out malignancy
  • Adnexal (separate from uterus, cervix does not move with mass):
    • Hydrosalpinx - thin-walled tubular cystic mass, "beads-on-a-string" on TVS
    • Tubo-ovarian abscess - fever, cervical motion tenderness, complex cystic mass on TVS
    • Ovarian mass (feeds into ovarian algorithm)
Non-gynaecological branch:
  • Urological (distended bladder, pelvic kidney, hydronephrosis)
  • Gastrointestinal (appendicular mass/abscess, cecal/sigmoid Ca, GIST)
  • Retroperitoneal/abdominal wall (mesenteric cyst, encysted ascites, sarcoma/lymphoma)

Tier 4 - Ovarian mass sub-algorithm

  • Functional cysts (follicular, corpus luteum, theca lutein) vs. neoplastic
  • Neoplastic: Benign (cystadenoma, dermoid, endometrioma) vs. Malignant (epithelial, germ cell, sex cord-stromal, metastatic/Krukenberg)
  • RMI/IOTA criteria assessment and CT staging for malignant cases

Tier 5 - Summary panels

  • Investigation summary (imaging, tumour markers, special procedures, labs)
  • Key clinical differentiating signs (Fothergill's, resonance, cervical traction)
  • Management principles by diagnosis
Sources: DC Dutta's Textbook of Gynaecology, Shaw's Textbook of Gynaecology, Williams Gynaecology (as referenced in your PDF)

Generate as powerpoint

Reading File
Reading File
Writing File

~/abdominal-mass-flowchart/build_pptx.py

"""
Diagnostic Algorithm: Abdominal/Pelvic Mass in Women
Multi-slide PPTX with embedded flowchart visuals via matplotlib + python-pptx
"""

import io, textwrap
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.util import Inches, Pt

# ── Colour palette ──────────────────────────────────────────────
PAL = dict(
    navy='#1A237E', blue='#1565C0', teal='#00695C', green='#2E7D32',
    purple='#6A1B9A', dpurple='#4527A0', xpurple='#311B92',
    red='#C62828', pink='#AD1457', orange='#E65100', amber='#F57F17',
    grey='#37474F', dgrey='#263238', brown='#4E342E',
    ltblue='#E3F2FD', ltgreen='#E8F5E9', ltpurple='#EDE7F6',
    ltred='#FFEBEE', ltpink='#FCE4EC', ltorange='#FFF3E0',
    ltamber='#FFF8E1', white='#FFFFFF', offwhite='#F0F4F8',
)

def hex2rgb(h):
    h = h.lstrip('#')
    return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))

def rgb(h):
    return RGBColor(*hex2rgb(h))

# ══════════════════════════════════════════════════════════════════
# Helper: render a matplotlib figure and return a BytesIO PNG stream
# ══════════════════════════════════════════════════════════════════
def fig_to_stream(fig):
    buf = io.BytesIO()
    fig.savefig(buf, format='png', dpi=200, bbox_inches='tight',
                facecolor=fig.get_facecolor())
    buf.seek(0)
    return buf

# ══════════════════════════════════════════════════════════════════
# Matplotlib helper functions (shared across figures)
# ══════════════════════════════════════════════════════════════════
def mbox(ax, x, y, w, h, text,
         fc='#E3F2FD', ec='#1565C0', tc='#212121',
         fs=8.5, bold=False, radius=0.25, va='center', lw=1.4):
    rect = FancyBboxPatch((x - w/2, y - h/2), w, h,
                          boxstyle=f"round,pad={radius}",
                          facecolor=fc, edgecolor=ec, linewidth=lw, zorder=3)
    ax.add_patch(rect)
    ax.text(x, y, text, ha='center', va=va, fontsize=fs,
            color=tc, fontweight='bold' if bold else 'normal',
            zorder=4, multialignment='center')

def diamond(ax, x, y, w, h, text,
            fc='#FFFDE7', ec='#F57F17', tc='#212121', fs=8.5):
    dx, dy = w/2, h/2
    pts = [(x, y+dy), (x+dx, y), (x, y-dy), (x-dx, y)]
    poly = plt.Polygon(pts, closed=True, facecolor=fc, edgecolor=ec,
                       linewidth=1.8, zorder=3)
    ax.add_patch(poly)
    ax.text(x, y, text, ha='center', va='center', fontsize=fs,
            color=tc, fontweight='bold', zorder=4, multialignment='center')

def arr(ax, x1, y1, x2, y2, label='', color='#455A64', lw=1.6):
    ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
                arrowprops=dict(arrowstyle='->', color=color,
                                lw=lw, connectionstyle='arc3,rad=0.0'),
                zorder=2)
    if label:
        mx, my = (x1+x2)/2+0.05, (y1+y2)/2
        ax.text(mx, my, label, fontsize=7, color=color,
                fontstyle='italic', zorder=5)

def harr(ax, x1, y, x2, label='', color='#455A64', lw=1.6):
    ax.annotate('', xy=(x2, y), xytext=(x1, y),
                arrowprops=dict(arrowstyle='->', color=color, lw=lw), zorder=2)
    if label:
        ax.text((x1+x2)/2, y+0.08, label, ha='center', fontsize=7,
                color=color, fontweight='bold', zorder=5)

# ══════════════════════════════════════════════════════════════════
# SLIDE 1 FIGURE: Title / Overview
# ══════════════════════════════════════════════════════════════════
def make_title_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#0D1B4B')

    # gradient-like header band
    for i, alpha in enumerate([0.18, 0.14, 0.10]):
        ax.add_patch(mpatches.Rectangle((0, 9-(i+1)*1.0), 16, 1.0,
                     fc='white', alpha=alpha, zorder=1))

    ax.text(8, 7.8, 'DIAGNOSTIC ALGORITHM',
            ha='center', fontsize=32, fontweight='bold',
            color='white', zorder=5)
    ax.text(8, 7.0, 'Abdominal / Pelvic Mass in Women',
            ha='center', fontsize=20, color='#90CAF9', zorder=5)
    ax.plot([2, 14], [6.65, 6.65], color='#42A5F5', lw=2)

    # 6 topic boxes
    topics = [
        ('1. History &\nExamination', '#1565C0', '#E3F2FD'),
        ('2. Bedside\nDiagnostics', '#00695C', '#E0F2F1'),
        ('3. First-line\nInvestigations', '#558B2F', '#F1F8E9'),
        ('4. Uterine\nCauses', '#C62828', '#FFEBEE'),
        ('5. Adnexal /\nOvarian Causes', '#6A1B9A', '#EDE7F6'),
        ('6. Non-Gynae\nCauses', '#37474F', '#ECEFF1'),
    ]
    cols = 3
    for i, (title, ec, fc) in enumerate(topics):
        col = i % cols
        row = i // cols
        x = 1.8 + col * 4.3
        y = 5.2 - row * 2.2
        mbox(ax, x, y, 3.8, 1.6, title, fc=fc, ec=ec, tc=ec, fs=12, bold=True)

    ax.text(8, 0.4,
            'Sources: DC Dutta\'s Textbook of Gynaecology · Shaw\'s Textbook · Williams Gynaecology',
            ha='center', fontsize=8, color='#90CAF9', style='italic')
    return fig

# ══════════════════════════════════════════════════════════════════
# SLIDE 2 FIGURE: History & Examination + Bedside Diagnostics
# ══════════════════════════════════════════════════════════════════
def make_history_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#F0F4F8')

    ax.text(8, 8.6, 'Step 1 & 2: History, Examination & Bedside Diagnostics',
            ha='center', fontsize=16, fontweight='bold', color='#1A237E')
    ax.plot([0.5, 15.5], [8.3, 8.3], color='#1565C0', lw=2)

    # HISTORY box
    mbox(ax, 4.0, 7.6, 7.5, 0.9,
         'HISTORY', fc='#1565C0', ec='#1565C0', tc='white', fs=12, bold=True)
    history_items = [
        '▸ Age, parity, menopausal status',
        '▸ Menstrual history: AUB, menorrhagia, dysmenorrhoea',
        '▸ Pain: onset, character, radiation, associated features',
        '▸ Urinary / bowel symptoms (pressure effects)',
        '▸ Subfertility / recurrent pregnancy loss',
        '▸ Weight loss, fever, anorexia (malignancy screen)',
        '▸ Family history of gynaecological / GI malignancy',
        '▸ OCP / HRT use; prior pelvic surgery or STI',
    ]
    for i, item in enumerate(history_items):
        ax.text(0.6, 6.95 - i * 0.35, item, fontsize=9, color='#212121')

    # EXAMINATION box
    mbox(ax, 12.0, 7.6, 7.5, 0.9,
         'PHYSICAL EXAMINATION', fc='#0277BD', ec='#0277BD', tc='white', fs=12, bold=True)
    exam_items = [
        '▸ General: pallor, lymphadenopathy, cachexia',
        '▸ Abdominal: site, size, shape, surface, margins',
        '▸ Consistency: firm-hard (fibroid), cystic (ovarian)',
        '▸ Mobility: side-to-side vs restricted',
        '▸ Percussion: dull (solid/fluid) vs resonant (bowel)',
        '▸ P/V bimanual: uterine vs adnexal; cervical motion',
        '▸ P/R: POD nodules, uterosacral involvement',
        '▸ Confirm lower pole unreachable → pelvic origin',
    ]
    for i, item in enumerate(exam_items):
        ax.text(8.6, 6.95 - i * 0.35, item, fontsize=9, color='#212121')

    ax.plot([0.3, 15.7], [4.1, 4.1], color='#00695C', lw=1.5, linestyle='--')

    # BEDSIDE diagnostics
    mbox(ax, 8.0, 3.7, 15.0, 0.7,
         'BEDSIDE DIAGNOSTIC CLUES', fc='#00695C', ec='#00695C', tc='white', fs=12, bold=True)

    bedside = [
        ('Uterus moves with cervix?', 'YES → Uterine origin', '#C62828'),
        ('Separate from uterus; cervix fixed?', 'YES → Adnexal/ovarian origin', '#6A1B9A'),
        ('Mass disappears on catheterisation?', 'YES → Distended bladder', '#2E7D32'),
        ("Fothergill's sign +ve?", 'Mass persists on rectus contraction\n→ Abdominal wall origin', '#1565C0'),
        ('Resonant note over mass?', 'Mesenteric/bowel mass', '#4E342E'),
        ('Lower pole cannot be reached?', 'Pelvic (not abdominal) origin', '#AD1457'),
    ]
    cols = 2
    for i, (sign, dx, clr) in enumerate(bedside):
        col = i % cols
        row = i // cols
        x = 2.2 + col * 7.5
        y = 2.9 - row * 0.85
        mbox(ax, x, y, 7.0, 0.7,
             f'Sign: {sign}\n→ {dx}',
             fc='#FAFAFA', ec=clr, tc='#212121', fs=8.5)

    ax.text(8, 0.25,
            'KEY: Cervical traction distinguishes uterine from adnexal · Fothergill\'s sign distinguishes abdominal wall from intraperitoneal mass',
            ha='center', fontsize=7.5, color='#37474F', style='italic')
    return fig

# ══════════════════════════════════════════════════════════════════
# SLIDE 3 FIGURE: Investigations
# ══════════════════════════════════════════════════════════════════
def make_investigations_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#F0F4F8')

    ax.text(8, 8.6, 'Step 3: Investigations',
            ha='center', fontsize=16, fontweight='bold', color='#1A237E')
    ax.plot([0.5, 15.5], [8.3, 8.3], color='#1565C0', lw=2)

    panels = [
        ('IMAGING', '#1565C0', '#E3F2FD',
         ['USG Abdomen & Pelvis (TVS) — 1st-line',
          '  ▸ Cystic vs solid; organ of origin',
          '  ▸ Fibroid: hypo/hyperechoic, peripheral vascularisation',
          '  ▸ TOA: complex thick-walled cystic mass',
          '  ▸ Hydrosalpinx: beads-on-a-string sign',
          'MRI Pelvis',
          '  ▸ Best for adenomyosis vs fibroid',
          '  ▸ Soft tissue characterisation',
          'CT Abdomen & Pelvis',
          '  ▸ Staging malignancy; lymph node assessment',
          '  ▸ Retroperitoneal / GIT masses',
          '3D USG — accurate fibroid localisation',
          'SIS — submucous fibroid / endometrial polyp',
          ]),
        ('TUMOUR MARKERS', '#6A1B9A', '#F3E5F5',
         ['CA-125 → Epithelial ovarian carcinoma',
          'AFP (Alpha-fetoprotein) → Yolk sac tumour',
          'β-hCG → Choriocarcinoma / GTD',
          'LDH → Dysgerminoma',
          'Inhibin → Granulosa cell tumour',
          'CEA / CA 19-9 → Mucinous tumours',
          'Note: CA-125 usually NEGATIVE in hydrosalpinx',
          '       (helps differentiate from ovarian malignancy)',
          ]),
        ('SPECIAL PROCEDURES', '#00695C', '#E0F2F1',
         ['Diagnostic Laparoscopy (Gold Standard)',
          '  ▸ Pelvic endometriosis / tubal pathology',
          '  ▸ Differentiates pedunculated fibroid from',
          '    ovarian tumour',
          'Hysteroscopy',
          '  ▸ Direct cavity visualisation',
          '  ▸ Submucous fibroid; endometrial biopsy',
          'SIS / HSG',
          '  ▸ Submucous fibroid / tubal patency',
          'Uterine Curettage',
          '  ▸ Coexisting endometrial pathology',
          '  ▸ Hysteroscopy + biopsy preferred',
          ]),
        ('LABS & OTHERS', '#E65100', '#FFF3E0',
         ['CBC → Anaemia (AUB), infection (TOA)',
          'CRP / ESR → Raised in PID, TOA',
          'LFT → Liver metastasis screen',
          'RFT → Pre-chemo / contrast CT baseline',
          'Urine hCG → Exclude pregnancy (all ages)',
          'Cervical / vaginal swabs → STI / PID',
          'Ascitic fluid cytology → Malignant cells',
          'Histopathology → Gold standard for Dx,',
          '  tumour type & grade',
          'Colonoscopy / OGD → Rule out GIT Ca',
          'Image-guided FNAC / Biopsy → Lymphoma,',
          '  TB mass, retroperitoneal sarcoma',
          ]),
    ]

    for i, (hdr, ec, fc, items) in enumerate(panels):
        col = i % 2
        row = i // 2
        x0 = 0.3 + col * 8.0
        y0 = 7.6 - row * 3.8
        w, h = 7.6, 3.5
        # header
        mbox(ax, x0 + w/2, y0, w, 0.55, hdr,
             fc=ec, ec=ec, tc='white', fs=11, bold=True)
        # body background
        rect = FancyBboxPatch((x0, y0 - h), w, h,
                              boxstyle="round,pad=0.1",
                              facecolor=fc, edgecolor=ec, linewidth=1.2, zorder=2)
        ax.add_patch(rect)
        for j, item in enumerate(items):
            ax.text(x0 + 0.15, y0 - 0.3 - j * 0.27, item,
                    fontsize=8, color='#212121', zorder=4)

    return fig

# ══════════════════════════════════════════════════════════════════
# SLIDE 4 FIGURE: Main Decision Flowchart
# ══════════════════════════════════════════════════════════════════
def make_main_flow_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#F0F4F8')

    ax.text(8, 8.6, 'Step 4: Determine Origin — Main Decision Flow',
            ha='center', fontsize=15, fontweight='bold', color='#1A237E')
    ax.plot([0.5, 15.5], [8.3, 8.3], color='#1565C0', lw=2)

    # START
    mbox(ax, 8, 7.8, 9, 0.65,
         'WOMAN WITH ABDOMINAL / PELVIC MASS',
         fc='#1A237E', ec='#1A237E', tc='white', fs=12, bold=True)
    arr(ax, 8, 7.47, 8, 7.05)

    # History + Exam + Investigations
    mbox(ax, 8, 6.7, 9, 0.6,
         'History ▸ Examination ▸ Bedside Tests ▸ USG + Basic Labs',
         fc='#E3F2FD', ec='#1565C0', fs=10)
    arr(ax, 8, 6.4, 8, 5.98)

    # Diamond: Organ of origin
    diamond(ax, 8, 5.55, 4.5, 0.9,
            'Determine Organ\nof Origin (USG + clinical)',
            fc='#FFFDE7', ec='#E65100')

    # LEFT: Gynaecological
    harr(ax, 5.75, 5.55, 2.5, 'GYNAECOLOGICAL', '#AD1457')
    arr(ax, 2.5, 5.1, 2.5, 4.7)

    # RIGHT: Non-gynaecological
    harr(ax, 10.25, 5.55, 13.5, 'NON-GYNAECOLOGICAL', '#37474F')
    arr(ax, 13.5, 5.1, 13.5, 4.7)

    # GYNAE sub-diamond
    diamond(ax, 2.5, 4.3, 4.0, 0.75,
            'Uterine or\nAdnexal?',
            fc='#FCE4EC', ec='#AD1457')
    harr(ax, 0.5, 4.3, 0.5, '#C62828')  # placeholder
    ax.annotate('', xy=(0.6, 4.3), xytext=(0.5, 4.3),  # tiny stub
                arrowprops=dict(arrowstyle='->', color='#C62828', lw=1.2))

    # Uterine (below left of gynae diamond)
    ax.annotate('', xy=(1.2, 4.3), xytext=(0.5, 4.3),
                arrowprops=dict(arrowstyle='->', color='#C62828', lw=1.5))
    # Actually draw arrows properly
    # Left arm: UTERINE
    ax.annotate('', xy=(1.3, 3.55), xytext=(2.5, 3.95),
                arrowprops=dict(arrowstyle='->', color='#C62828', lw=1.5))
    ax.text(1.5, 3.85, 'UTERINE', fontsize=7.5, color='#C62828', fontweight='bold')
    # Right arm: ADNEXAL
    ax.annotate('', xy=(4.6, 3.55), xytext=(3.4, 3.95),
                arrowprops=dict(arrowstyle='->', color='#6A1B9A', lw=1.5))
    ax.text(3.8, 3.85, 'ADNEXAL', fontsize=7.5, color='#6A1B9A', fontweight='bold')

    # Uterine causes box
    mbox(ax, 1.3, 3.0, 2.4, 0.8,
         'UTERINE\n▸ Fibromyoma\n▸ Adenomyosis\n▸ Pyometra',
         fc='#FFEBEE', ec='#C62828', fs=8)
    # Adnexal causes box
    mbox(ax, 4.6, 3.0, 2.4, 0.8,
         'ADNEXAL\n▸ Hydrosalpinx\n▸ TOA\n▸ Ovarian mass',
         fc='#EDE7F6', ec='#6A1B9A', fs=8)

    arr(ax, 1.3, 2.6, 1.3, 2.15)
    arr(ax, 4.6, 2.6, 4.6, 2.15)

    # See detailed slides note
    mbox(ax, 1.3, 1.8, 2.4, 0.55,
         'See Slide 5\n(Uterine detail)',
         fc='#FFCDD2', ec='#C62828', fs=7.5)
    mbox(ax, 4.6, 1.8, 2.4, 0.55,
         'See Slide 6\n(Adnexal detail)',
         fc='#D1C4E9', ec='#6A1B9A', fs=7.5)

    # NON-GYNAE box
    mbox(ax, 13.5, 4.3, 4.5, 0.65,
         'NON-GYNAECOLOGICAL',
         fc='#37474F', ec='#37474F', tc='white', fs=10, bold=True)
    arr(ax, 13.5, 3.97, 13.5, 3.6)

    ng_items = [
        ('UROLOGICAL\n▸ Distended bladder\n▸ Pelvic kidney / Hydronephrosis', '#2E7D32', '#E8F5E9'),
        ('GASTROINTESTINAL\n▸ Appendicular mass\n▸ Cecal / Sigmoid Ca · GIST', '#F57F17', '#FFF8E1'),
        ('RETROPERITONEAL\n▸ Mesenteric cyst\n▸ Lymphoma / Sarcoma', '#4E342E', '#EFEBE9'),
    ]
    for j, (txt, ec, fc) in enumerate(ng_items):
        y = 3.2 - j * 1.05
        mbox(ax, 13.5, y, 4.2, 0.85, txt, fc=fc, ec=ec, fs=8)
        if j < 2:
            arr(ax, 13.5, y - 0.43, 13.5, y - 0.62)

    mbox(ax, 13.5, 0.85, 4.2, 0.55,
         'See Slide 7\n(Non-Gynae detail)',
         fc='#ECEFF1', ec='#37474F', fs=7.5)

    # Ovarian algorithm pointer
    mbox(ax, 8, 2.3, 4, 0.65,
         'OVARIAN MASS\nSub-Algorithm',
         fc='#4527A0', ec='#4527A0', tc='white', fs=9, bold=True)
    ax.annotate('', xy=(6.82, 2.55), xytext=(4.6+1.2, 2.55),
                arrowprops=dict(arrowstyle='->', color='#4527A0', lw=1.5))
    mbox(ax, 8, 1.6, 4, 0.55,
         'See Slide 8\n(Ovarian algorithm)',
         fc='#EDE7F6', ec='#4527A0', fs=7.5)
    return fig

# ══════════════════════════════════════════════════════════════════
# SLIDE 5 FIGURE: Uterine Causes
# ══════════════════════════════════════════════════════════════════
def make_uterine_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#FFF8F8')

    ax.text(8, 8.6, 'Uterine Causes of Pelvic Mass',
            ha='center', fontsize=16, fontweight='bold', color='#B71C1C')
    ax.plot([0.5, 15.5], [8.3, 8.3], color='#C62828', lw=2)

    mbox(ax, 8, 7.8, 9.5, 0.65,
         'UTERINE ORIGIN  (mass moves with cervix · no groove between uterus and mass)',
         fc='#C62828', ec='#C62828', tc='white', fs=11, bold=True)

    causes = [
        {
            'title': 'FIBROMYOMA (Leiomyoma)',
            'col': '#C62828', 'fc': '#FFEBEE',
            'clinical': [
                'Most common benign solid tumour in females',
                'Asymptomatic in 75%; AUB/Menorrhagia (30%)',
                'Dysmenorrhoea, dyspareunia, subfertility',
                'Firm-hard, irregular, nodular uterus',
                'Mobility: restricted up-down; free side-side',
                'Pressure symptoms on bladder / rectum',
            ],
            'ix': [
                'TVS + Colour Doppler (1st-line)',
                '  ▸ Hypo/hyperechoic lesion; peripheral vascularity',
                '  ▸ Central vascularity → degeneration',
                'SIS — submucous fibroids',
                'HSG — filling defect (submucous)',
                'MRI — most accurate; maps location/size',
                'Hysteroscopy — submucous resection',
            ],
        },
        {
            'title': 'ADENOMYOSIS',
            'col': '#AD1457', 'fc': '#FCE4EC',
            'clinical': [
                'Endometrial glands within myometrium',
                'Multiparous women ~40 years',
                'Menorrhagia + progressive 2° dysmenorrhoea',
                'Symmetrical tender uterus (≤12 wk size)',
                'Rarely exceeds 12-week size',
                'KEY: pain differentiates from fibroids',
            ],
            'ix': [
                'TVS: ill-defined hypoechoic areas; heterogeneous echo',
                'MRI (investigation of choice)',
                '  ▸ Hypoechoic/anechoic areas in wall',
                '  ▸ Best for differentiating from fibroid',
                'Diagnostic Hx + Curettage',
                '  ▸ Exclude intrauterine pathology',
            ],
        },
        {
            'title': 'PYOMETRA',
            'col': '#E65100', 'fc': '#FFF3E0',
            'clinical': [
                'Pus collection within uterine cavity',
                'Prerequisites: cervical obstruction + infection',
                'Causes: carcinoma, senile endometritis,',
                '  post-procedure (conisation, cautery)',
                'Purulent blood-stained vaginal discharge',
                'Uniform suprapubic swelling',
            ],
            'ix': [
                'USG: distended uterine cavity with fluid',
                'MUST exclude endometrial / cervical Ca',
                'Management steps:',
                '  1. Exclude malignancy',
                '  2. Cervical dilatation + drain pus',
                '  3. Broad-spectrum IV antibiotics',
                '  4. Curettage after 7-14 days',
            ],
        },
    ]

    for i, c in enumerate(causes):
        x0 = 0.3 + i * 5.3
        w = 5.0
        # Title header
        mbox(ax, x0 + w/2, 6.85, w, 0.6, c['title'],
             fc=c['col'], ec=c['col'], tc='white', fs=10, bold=True)
        # Clinical features
        mbox(ax, x0 + w/2, 6.45, w, 0.45, 'Clinical Features',
             fc=c['fc'], ec=c['col'], fs=9, bold=True)
        rect = FancyBboxPatch((x0, 3.65), w, 2.7,
                              boxstyle="round,pad=0.1",
                              facecolor=c['fc'], edgecolor=c['col'],
                              linewidth=1.2, zorder=2)
        ax.add_patch(rect)
        for j, item in enumerate(c['clinical']):
            ax.text(x0+0.15, 6.15 - j*0.41, item, fontsize=8.2, color='#212121', zorder=4)
        # Investigations
        mbox(ax, x0 + w/2, 3.45, w, 0.45, 'Investigations',
             fc=c['fc'], ec=c['col'], fs=9, bold=True)
        rect2 = FancyBboxPatch((x0, 0.55), w, 2.8,
                               boxstyle="round,pad=0.1",
                               facecolor='#FAFAFA', edgecolor=c['col'],
                               linewidth=1.2, zorder=2)
        ax.add_patch(rect2)
        for j, item in enumerate(c['ix']):
            ax.text(x0+0.15, 3.15 - j*0.37, item, fontsize=8, color='#212121', zorder=4)

    ax.text(8, 0.2,
            'KEY CLUE: Menorrhagia + worsening dysmenorrhoea → Adenomyosis · Irregular firm nodular uterus → Fibromyoma · Purulent discharge + suprapubic swelling → Pyometra',
            ha='center', fontsize=7.5, color='#37474F', style='italic')
    return fig

# ══════════════════════════════════════════════════════════════════
# SLIDE 6 FIGURE: Adnexal Causes
# ══════════════════════════════════════════════════════════════════
def make_adnexal_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#F3E5F5')

    ax.text(8, 8.6, 'Adnexal / Tubal-Ovarian Causes',
            ha='center', fontsize=16, fontweight='bold', color='#4A148C')
    ax.plot([0.5, 15.5], [8.3, 8.3], color='#6A1B9A', lw=2)

    mbox(ax, 8, 7.8, 9.5, 0.65,
         'ADNEXAL ORIGIN  (separate from uterus · bimanual: cervix does NOT move with mass)',
         fc='#6A1B9A', ec='#6A1B9A', tc='white', fs=11, bold=True)

    causes = [
        {
            'title': 'HYDROSALPINX',
            'col': '#5E35B1', 'fc': '#EDE7F6',
            'clinical': [
                'Fallopian tube distension with serous fluid',
                'End-result of PID / acute salpingitis',
                'Often asymptomatic; chronic pelvic pain',
                'Infertility; pelvic heaviness',
                'Adnexal cystic mass; restricted mobility',
                'Torsion → acute pain',
            ],
            'ix': [
                'TVS (investigation of choice)',
                '  ▸ Thin-walled elongated cystic mass',
                '  ▸ Beads-on-a-string sign',
                '  ▸ Incomplete septae',
                'HSG: distal obstruction; no peritoneal spill',
                'Diagnostic laparoscopy (gold standard)',
                'CA-125 usually NEGATIVE',
            ],
        },
        {
            'title': 'TUBO-OVARIAN ABSCESS (TOA)',
            'col': '#C62828', 'fc': '#FFEBEE',
            'clinical': [
                'Inflammatory abscess: tube + ovary + pelvis',
                'Complication of PID (most common cause)',
                'Fever, lower abdominal pain, vaginal discharge',
                'Cervical motion tenderness (CMT)',
                'Adnexal tenderness / mass',
                'Leukocytosis; raised CRP/ESR',
            ],
            'ix': [
                'CBC → Leukocytosis',
                'CRP/ESR → Raised',
                'TVS: complex cystic mass, thick walls,',
                '  internal echoes, cul-de-sac fluid',
                'CT: thick-walled cystic mass + inflamm.',
                'Blood cultures (if septic)',
                'Swabs: STI / STD screen',
            ],
        },
        {
            'title': 'OVARIAN MASS',
            'col': '#4527A0', 'fc': '#E8EAF6',
            'clinical': [
                'Bimanual: uterus separated from mass',
                'Mass movement ≠ cervical movement',
                'Lower pole felt through fornix',
                'Benign: mobile, smooth, cystic',
                'Malignant: fixed, nodular, irregular',
                'POD nodules → advanced malignancy',
            ],
            'ix': [
                'TVS + Colour Doppler',
                '  ▸ Tumour volume, cyst wall, septa,',
                '  ▸ Vascularity pattern (neovascularisation)',
                'CT: adnexal mass; staging',
                'MRI: benign vs malignant',
                'Tumour markers: CA-125, AFP, β-hCG',
                '→ See Ovarian Algorithm (Slide 8)',
            ],
        },
    ]

    for i, c in enumerate(causes):
        x0 = 0.3 + i * 5.3
        w = 5.0
        mbox(ax, x0 + w/2, 6.85, w, 0.6, c['title'],
             fc=c['col'], ec=c['col'], tc='white', fs=10, bold=True)
        mbox(ax, x0 + w/2, 6.45, w, 0.45, 'Clinical Features',
             fc=c['fc'], ec=c['col'], fs=9, bold=True)
        rect = FancyBboxPatch((x0, 3.65), w, 2.7,
                              boxstyle="round,pad=0.1",
                              facecolor=c['fc'], edgecolor=c['col'],
                              linewidth=1.2, zorder=2)
        ax.add_patch(rect)
        for j, item in enumerate(c['clinical']):
            ax.text(x0+0.15, 6.15 - j*0.41, item, fontsize=8.2, color='#212121', zorder=4)
        mbox(ax, x0 + w/2, 3.45, w, 0.45, 'Investigations',
             fc=c['fc'], ec=c['col'], fs=9, bold=True)
        rect2 = FancyBboxPatch((x0, 0.55), w, 2.8,
                               boxstyle="round,pad=0.1",
                               facecolor='#FAFAFA', edgecolor=c['col'],
                               linewidth=1.2, zorder=2)
        ax.add_patch(rect2)
        for j, item in enumerate(c['ix']):
            ax.text(x0+0.15, 3.15 - j*0.37, item, fontsize=8, color='#212121', zorder=4)

    ax.text(8, 0.2,
            'MANAGEMENT: TOA → IV Antibiotics 14 days; drain if ≥8 cm  ·  Hydrosalpinx → Salpingectomy / Neosalpingostomy  ·  Ovarian mass → see Slide 8',
            ha='center', fontsize=7.5, color='#37474F', style='italic')
    return fig

# ══════════════════════════════════════════════════════════════════
# SLIDE 7 FIGURE: Non-Gynaecological Causes
# ══════════════════════════════════════════════════════════════════
def make_nongynae_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#ECEFF1')

    ax.text(8, 8.6, 'Non-Gynaecological Causes of Abdominal Mass',
            ha='center', fontsize=16, fontweight='bold', color='#263238')
    ax.plot([0.5, 15.5], [8.3, 8.3], color='#37474F', lw=2)

    mbox(ax, 8, 7.8, 9.5, 0.65,
         'NON-GYNAECOLOGICAL ORIGIN  (bimanual: uterus/adnexa independent of mass)',
         fc='#37474F', ec='#37474F', tc='white', fs=11, bold=True)

    cols_data = [
        {
            'title': 'UROLOGICAL', 'col': '#2E7D32', 'fc': '#E8F5E9',
            'conditions': [
                ('Distended Bladder',
                 '▸ Midline suprapubic dull mass\n▸ Disappears on catheterisation\n▸ Acute/chronic urinary retention'),
                ('Pelvic Kidney',
                 '▸ Fixed in pelvis; non-mobile\n▸ IV urogram / CT urogram\n▸ Urology referral'),
                ('Hydronephrosis',
                 '▸ Flank mass extending to iliac fossa\n▸ USG: dilated pelvicalyceal system\n▸ CT urogram confirms'),
                ('Renal Cell Ca / Wilms\' Tumour',
                 '▸ Haematuria, flank mass\n▸ CT: heterogeneous renal mass\n▸ Urology / oncology referral'),
            ],
        },
        {
            'title': 'GASTROINTESTINAL', 'col': '#E65100', 'fc': '#FFF3E0',
            'conditions': [
                ('Appendicular Mass / Abscess',
                 '▸ Fixed tender mass RIF\n▸ Preceded by acute RIF pain + fever\n▸ Ochsner-Sherren regimen initially'),
                ('Cecal Carcinoma',
                 '▸ Hard irregular mass RIF\n▸ Anaemia, occult blood loss, wt loss\n▸ Colonoscopy + CT staging'),
                ('Sigmoid Carcinoma',
                 '▸ Left iliac fossa mass\n▸ Altered bowel habits, rectal bleeding\n▸ Sigmoidoscopy + CT'),
                ('GIST',
                 '▸ Smooth, mobile, well-defined\n▸ CT: heterogeneous enhancing mass\n▸ Surgical resection'),
            ],
        },
        {
            'title': 'RETROPERITONEAL / WALL', 'col': '#4E342E', 'fc': '#EFEBE9',
            'conditions': [
                ('Mesenteric Cyst',
                 '▸ Mobile perpendicular to mesentery\n▸ Band of resonance over mass\n▸ CT / MRI; surgical excision'),
                ('Encysted Ascites',
                 '▸ Dull, shifting; no free fluid\n▸ History: TB, malignancy\n▸ CT + ascitic fluid cytology'),
                ('Retroperitoneal Sarcoma /\nLymphoma',
                 '▸ Deep fixed mass; systemic Sx\n▸ CT/MRI; LDH raised in lymphoma\n▸ Image-guided biopsy'),
                ('Abdominal Wall\n(Desmoid / Haematoma)',
                 '▸ Fothergill\'s sign POSITIVE\n▸ Persists on rectus contraction\n▸ USG / MRI; Rectus sheath'),
            ],
        },
    ]

    for i, cat in enumerate(cols_data):
        x0 = 0.2 + i * 5.25
        w = 5.0
        mbox(ax, x0 + w/2, 7.0, w, 0.55, cat['title'],
             fc=cat['col'], ec=cat['col'], tc='white', fs=11, bold=True)
        for j, (name, detail) in enumerate(cat['conditions']):
            y = 6.15 - j * 1.5
            mbox(ax, x0 + w/2, y, w, 0.45, name,
                 fc=cat['fc'], ec=cat['col'], fs=9, bold=True)
            lines = detail.split('\n')
            for k, line in enumerate(lines):
                ax.text(x0 + 0.15, y - 0.28 - k * 0.28, line,
                        fontsize=8, color='#212121', zorder=4)

    ax.text(8, 0.25,
            'KEY BEDSIDE TEST: Catheterise for midline mass → rules out distended bladder immediately   ·   Fothergill\'s sign: mass visible on tensing rectus → abdominal wall origin',
            ha='center', fontsize=7.5, color='#37474F', style='italic')
    return fig

# ══════════════════════════════════════════════════════════════════
# SLIDE 8 FIGURE: Ovarian Mass Algorithm
# ══════════════════════════════════════════════════════════════════
def make_ovarian_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#EDE7F6')

    ax.text(8, 8.6, 'Ovarian Mass — Diagnostic Sub-Algorithm',
            ha='center', fontsize=16, fontweight='bold', color='#311B92')
    ax.plot([0.5, 15.5], [8.3, 8.3], color='#4527A0', lw=2)

    # Entry
    mbox(ax, 8, 7.75, 8, 0.65,
         'OVARIAN MASS CONFIRMED ON CLINICAL EXAM + USG',
         fc='#4527A0', ec='#4527A0', tc='white', fs=11, bold=True)
    arr(ax, 8, 7.42, 8, 6.98)

    # Advanced investigations
    mbox(ax, 8, 6.65, 10, 0.6,
         'Advanced Ix: TVS + Colour Doppler · MRI · CT · Tumour markers (CA-125, AFP, β-hCG, LDH, Inhibin)',
         fc='#E8EAF6', ec='#4527A0', fs=9)
    arr(ax, 8, 6.35, 8, 5.93)

    # Diamond: Functional vs Neoplastic
    diamond(ax, 8, 5.52, 5, 0.85,
            'Functional or\nNeoplastic?',
            fc='#F3E5F5', ec='#4527A0')

    # Functional: LEFT
    ax.annotate('', xy=(3.0, 5.52), xytext=(5.5, 5.52),
                arrowprops=dict(arrowstyle='->', color='#6A1B9A', lw=1.8))
    ax.text(4.2, 5.68, 'FUNCTIONAL', ha='center', fontsize=8.5,
            color='#6A1B9A', fontweight='bold')

    # Neoplastic: RIGHT
    ax.annotate('', xy=(13.5, 5.52), xytext=(10.5, 5.52),
                arrowprops=dict(arrowstyle='->', color='#311B92', lw=1.8))
    ax.text(12.1, 5.68, 'NEOPLASTIC', ha='center', fontsize=8.5,
            color='#311B92', fontweight='bold')

    # FUNCTIONAL CYSTS
    mbox(ax, 3.0, 4.95, 5.5, 0.6, 'FUNCTIONAL CYSTS',
         fc='#6A1B9A', ec='#6A1B9A', tc='white', fs=10, bold=True)

    func_cysts = [
        ('Follicular Cyst', '#D1C4E9',
         '<3 cm: no Ix needed\n<7 cm simple: USG in 3-6/12\n>7 cm or persistent: cystectomy'),
        ('Corpus Luteum Cyst', '#D1C4E9',
         'hCG + vaginal USG + culdocentesis\nSpontaneous regression usually\nRupture/hematocrit >15% → surgery'),
        ('Theca Lutein Cyst', '#D1C4E9',
         'Bilateral; associated with GTD\nResolves as hCG normalises\nCOCPs suppress ovarian activity'),
    ]
    for j, (name, fc, detail) in enumerate(func_cysts):
        y = 4.25 - j * 1.25
        mbox(ax, 3.0, y, 5.3, 0.5, name, fc=fc, ec='#6A1B9A', fs=9, bold=True)
        lines = detail.split('\n')
        for k, line in enumerate(lines):
            ax.text(0.35, y - 0.28 - k * 0.27, line, fontsize=8, color='#212121')

    # NEOPLASTIC
    mbox(ax, 13.0, 4.95, 5.5, 0.6, 'NEOPLASTIC',
         fc='#311B92', ec='#311B92', tc='white', fs=10, bold=True)
    arr(ax, 13.0, 4.65, 13.0, 4.3)

    # Benign vs Malignant diamond
    diamond(ax, 13.0, 3.9, 4.2, 0.75,
            'Benign or\nMalignant?',
            fc='#E8EAF6', ec='#311B92')

    # Benign: left
    ax.annotate('', xy=(10.3, 3.9), xytext=(10.9, 3.9),
                arrowprops=dict(arrowstyle='->', color='#4527A0', lw=1.5))
    ax.text(10.6, 4.08, 'BENIGN', ha='center', fontsize=7.5,
            color='#4527A0', fontweight='bold')

    mbox(ax, 10.3, 3.1, 4.0, 1.3,
         'BENIGN NEOPLASMS\n▸ Serous cystadenoma (40% malignant potential)\n▸ Mucinous cystadenoma (5-10% malignant)\n▸ Dermoid cyst (mature teratoma; 1-2% malignant)\n▸ Fibroma / Thecoma\n▸ Endometrioma (chocolate cyst)\nTx: Cystectomy / Oophorectomy',
         fc='#E8EAF6', ec='#4527A0', fs=7.5)

    # Malignant: right (down from diamond)
    ax.annotate('', xy=(13.0, 2.75), xytext=(13.0, 3.52),
                arrowprops=dict(arrowstyle='->', color='#B71C1C', lw=1.5))
    ax.text(13.2, 3.18, 'MALIG.', fontsize=7.5, color='#B71C1C', fontweight='bold')

    mbox(ax, 13.0, 2.1, 5.5, 1.0,
         'MALIGNANT OVARIAN TUMOURS\nEpithelial: Serous / Mucinous / Endometrioid / Clear cell Ca\nGerm cell: Dysgerminoma · Immature teratoma · Yolk sac\nSex cord-stromal: Granulosa cell · Sertoli-Leydig\nMetastatic: Krukenberg tumour (from GIT primary)\nRMI / IOTA criteria · CT staging (FIGO)\nTx: TAH + BSO + Omentectomy ± Carboplatin/Paclitaxel',
         fc='#FFEBEE', ec='#B71C1C', fs=7.5)

    ax.text(8, 0.25,
            'FIGO Staging: Stage I – confined to ovaries · Stage II – pelvic extension · Stage III – peritoneal metastasis / retro nodes · Stage IV – distant metastasis',
            ha='center', fontsize=7.5, color='#37474F', style='italic')
    return fig

# ══════════════════════════════════════════════════════════════════
# SLIDE 9 FIGURE: Management Summary
# ══════════════════════════════════════════════════════════════════
def make_management_fig():
    fig, ax = plt.subplots(figsize=(16, 9))
    ax.set_xlim(0, 16); ax.set_ylim(0, 9); ax.axis('off')
    fig.patch.set_facecolor('#E8F5E9')

    ax.text(8, 8.6, 'Management Principles by Diagnosis',
            ha='center', fontsize=16, fontweight='bold', color='#1B5E20')
    ax.plot([0.5, 15.5], [8.3, 8.3], color='#2E7D32', lw=2)

    mgmt = [
        ('FIBROMYOMA', '#C62828', '#FFEBEE',
         'CONSERVATIVE\n▸ Observation (small, asymptomatic)\n▸ GnRH agonists (preoperative)\n▸ Levonorgestrel IUS (Mirena)\nSURGICAL\n▸ Myomectomy (fertility-sparing)\n▸ Hysterectomy (definitive)\n▸ UAE (uterine artery embolisation)'),
        ('ADENOMYOSIS', '#AD1457', '#FCE4EC',
         'MEDICAL\n▸ NSAIDs → dysmenorrhoea\n▸ GnRH agonists\n▸ Mirena LNG-IUS (menorrhagia/pain)\n▸ Danazol\nSURGICAL\n▸ Total hysterectomy (definitive)\n▸ Adenomyomectomy (young/fertility)\nEMERGING: MRgFUS'),
        ('TOA', '#E65100', '#FFF3E0',
         'MEDICAL\n▸ IV broad-spectrum antibiotics\n▸ Doxycycline + Metronidazole x14d\n▸ Clindamycin alternative\nDRAINAGE\n▸ If ≥8 cm or no improvement 48-72h\n▸ Ultrasound/CT-guided (TVS/TA)\nSURGICAL\n▸ Laparoscopy/laparotomy if ruptured'),
        ('HYDROSALPINX', '#5E35B1', '#EDE7F6',
         'EXPECTANT\n▸ Asymptomatic + family complete\nSURGICAL\n▸ Salpingectomy (treatment of choice)\n▸ Neosalpingostomy (mild disease,\n  fertility desired; up to 80% PR)\n▸ IVF preferred for severe tubal\n  disease\n▸ Laparoscopic adhesiolysis'),
        ('OVARIAN CYST\n(Benign)', '#4527A0', '#EDE7F6',
         'FUNCTIONAL CYSTS\n▸ <7 cm simple: USG follow-up\n▸ Spontaneous regression expected\n▸ COCPs to suppress\nNEOPLASTIC BENIGN\n▸ Ovarian cystectomy (young)\n▸ Unilateral oophorectomy (large)\n▸ BSO (postmenopausal)\n▸ Emergency surgery if torsion'),
        ('OVARIAN Ca\n(Malignant)', '#B71C1C', '#FFEBEE',
         'SURGERY\n▸ TAH + BSO + Omentectomy\n▸ Peritoneal washings\n▸ Fertility-sparing (Stage Ia, young)\nCHEMOTHERAPY\n▸ Carboplatin + Paclitaxel\n▸ BEP (germ cell tumours)\nTARGETED\n▸ Bevacizumab; PARP inhibitors\nRADIOTHERAPY: dysgerminoma / palliation'),
    ]

    cols = 3
    for i, (hdr, ec, fc, body) in enumerate(mgmt):
        col = i % cols
        row = i // cols
        x0 = 0.2 + col * 5.25
        y0 = 7.65 - row * 3.7
        w = 5.0
        mbox(ax, x0 + w/2, y0, w, 0.6, hdr,
             fc=ec, ec=ec, tc='white', fs=10, bold=True)
        rect = FancyBboxPatch((x0, y0 - 2.85), w, 2.75,
                              boxstyle="round,pad=0.1",
                              facecolor=fc, edgecolor=ec, linewidth=1.2, zorder=2)
        ax.add_patch(rect)
        lines = body.split('\n')
        for j, line in enumerate(lines):
            ax.text(x0 + 0.15, y0 - 0.25 - j * 0.33, line,
                    fontsize=8, color='#212121', zorder=4)

    ax.text(8, 0.22,
            'NON-GYNAE: Bladder → Catheterise + Urology  ·  Appendicular mass → Ochsner-Sherren + interval appendectomy  ·  Bowel Ca → Resection  ·  TB mass → ATT',
            ha='center', fontsize=7.5, color='#37474F', style='italic')
    return fig

# ══════════════════════════════════════════════════════════════════
# BUILD PPTX
# ══════════════════════════════════════════════════════════════════
def add_image_slide(prs, fig_fn, slide_title):
    """Render a matplotlib figure, embed in a blank slide."""
    fig = fig_fn()
    img_stream = fig_to_stream(fig)
    plt.close(fig)

    blank = prs.slide_layouts[6]
    slide = prs.slides.add_slide(blank)

    # Slide background
    background = slide.background
    fill = background.fill
    fill.solid()
    fill.fore_color.rgb = rgb('#FFFFFF')

    # Full-slide image
    slide.shapes.add_picture(
        img_stream,
        left=Inches(0), top=Inches(0),
        width=Inches(13.333), height=Inches(7.5)
    )
    return slide

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

slides_info = [
    (make_title_fig,       'Title'),
    (make_history_fig,     'History & Examination'),
    (make_investigations_fig, 'Investigations'),
    (make_main_flow_fig,   'Main Decision Flow'),
    (make_uterine_fig,     'Uterine Causes'),
    (make_adnexal_fig,     'Adnexal Causes'),
    (make_nongynae_fig,    'Non-Gynaecological Causes'),
    (make_ovarian_fig,     'Ovarian Mass Algorithm'),
    (make_management_fig,  'Management Summary'),
]

print("Building slides...")
for fn, title in slides_info:
    print(f"  → {title}")
    add_image_slide(prs, fn, title)

out_path = '/tmp/workspace/abdominal-mass-flowchart/Diagnostic_Algorithm_Abdominal_Mass_Women.pptx'
prs.save(out_path)
print(f"\nSaved: {out_path}")
Running Command

cd /tmp/workspace/abdominal-mass-flowchart && python3 build_pptx.py 2>&1

Running Command

ls -lh /tmp/workspace/abdominal-mass-flowchart/Diagnostic_Algorithm_Abdominal_Mass_Women.pptx

Your PowerPoint is ready (2.7 MB, 9 slides, 16:9 widescreen):

Diagnostic Algorithm Abdominal Mass Women

Presentation · PPTX


What's inside (slide by slide)

SlideContent
1Title / Overview - Colour-coded topic index for all 6 sections
2History & Examination + Bedside Clues - Full history checklist, exam findings, Fothergill's sign, cervical traction tests
3Investigations - 4-panel layout: Imaging, Tumour Markers, Special Procedures (laparoscopy/hysteroscopy/HSG), Labs
4Main Decision Flow - Flowchart: Start → USG → Gynaecological vs Non-Gynaecological → Uterine vs Adnexal with arrows to detail slides
5Uterine Causes - Side-by-side: Fibromyoma / Adenomyosis / Pyometra (clinical features + investigations each)
6Adnexal Causes - Hydrosalpinx / TOA / Ovarian Mass (beads-on-a-string, CMT, TVS features)
7Non-Gynaecological Causes - Urological / GI / Retroperitoneal-Wall causes with key differentiating signs
8Ovarian Mass Sub-Algorithm - Functional (follicular/CL/theca lutein) vs Neoplastic → Benign vs Malignant → FIGO staging
9Management Summary - 6-box grid covering all diagnoses (medical, surgical, emerging therapies)
This is a shared conversation. Sign in to Orris to start your own chat.