What the points to observed in case of testing of fsh/ lh in pcos .

Reading File
Finding Sources
Finding Sources
Searching PubMed

FSH LH ratio PCOS diagnosis

Reading File
Reading File
Reading File
Excellent - I now have rich, authoritative content from multiple textbooks. Let me compile the full answer.

FSH / LH Testing in PCOS - Key Points to Observe

1. Role of FSH and LH in PCOS Pathophysiology

In PCOS, there is a fundamental disruption of GnRH pulsatility. Abnormally increased GnRH pulse frequency preferentially stimulates LH over FSH secretion. This leads to:
  • Elevated LH - drives ovarian thecal cells to overproduce androgens
  • Normal or low FSH - insufficient to support dominant follicle maturation, leading to follicular arrest and anovulation
  • Elevated LH stimulates increased ovarian androgen production, which is further amplified by concurrent insulin resistance
(Harrison's Principles of Internal Medicine 22E; Berek & Novak's Gynecology)

2. LH:FSH Ratio

The classic finding in PCOS is an LH:FSH ratio > 2:1 or 3:1. This is one of the most cited biochemical observations:
  • A ratio >2-3 is "commonly observed" and supports the diagnosis
  • The ratio reflects the GnRH pulse frequency abnormality
  • However, this ratio is NOT part of formal diagnostic criteria (Rotterdam 2003, updated 2023) - it is classified only as a minor diagnostic criterion
"All other frequently encountered manifestations offer less consistent findings and therefore qualify only as minor diagnostic criteria for PCOS. They include elevated LH-to-FSH ratio, IR, perimenarchal onset of hirsutism, and obesity."
  • Berek & Novak's Gynecology, p. 1920

3. When and How to Measure: Critical Timing Points

ParameterRecommendation
Day of cycleMeasure FSH and LH on Day 2 or Day 3 of the menstrual cycle (early follicular phase)
FSH Day 3Baseline FSH - elevated levels (>10 mIU/mL) suggest diminished ovarian reserve rather than PCOS
LH pulse variabilityLH is secreted in pulses - a single random measurement may not be reliable
Fasting statePreferred, especially if concurrent metabolic tests are ordered
"Given the pulsatility of LH secretion, a random serum LH/FSH ratio is not included in the diagnostic criteria."
  • Harrison's Principles 22E, p. 3178

4. Important Confounders and Pitfalls

a) LH baseline elevation causes false-positive ovulation tests
  • PCOS patients have chronically elevated baseline LH
  • Urinary LH ovulation predictor kits will give false-positive results because LH stays elevated throughout the cycle, not just at the surge
  • Henry's Clinical Diagnosis (Tietz); Berek & Novak's
b) Obesity blunts LH levels
  • Obese women with PCOS may NOT show a clearly elevated LH or high LH:FSH ratio
  • Lean/anovulatory PCOS patients are more likely to show the classic high LH pattern
  • "Lean oligo-ovulatory patients with PCOS generally have high LH levels in the presence of normal to low levels of FSH and estradiol" - Harrison's 22E
c) FSH may appear normal or low-normal
  • A normal FSH does not rule out PCOS
  • The diagnostic weight lies more on the LH elevation and the ratio rather than FSH alone
d) Must exclude other causes before interpreting LH/FSH
  • Elevated FSH + LH together = primary ovarian failure (not PCOS)
  • Low/inappropriately normal FSH + LH = hypothalamic-pituitary origin (Sheehan syndrome, eating disorders)
  • High LH + low FSH = pattern suggestive of PCOS

5. What FSH/LH Testing Helps Rule Out

Since PCOS is a diagnosis of exclusion, FSH/LH levels help differentiate it from:
ConditionFSHLH
Primary ovarian failure/POIVery HIGHVery HIGH
Hypothalamic amenorrheaLow or normalLow or normal
PCOS (typical lean)Normal / low-normalELEVATED (>LH:FSH >2-3)
Pituitary tumorVariableVariable
Other exclusions needed alongside FSH/LH: prolactin (hyperprolactinemia), TSH (hypothyroidism), 17-OH progesterone (congenital adrenal hyperplasia), total/free testosterone, DHEAS, fasting glucose and insulin.

6. FSH/LH in Context of the Full PCOS Diagnostic Workup

The 2023 updated Rotterdam criteria (confirmed in Harrison's 22E) require 2 of 3:
  1. Irregular menses (oligomenorrhea/amenorrhea)
  2. Clinical or biochemical hyperandrogenism (elevated testosterone, modified Ferriman-Gallwey score)
  3. Polycystic ovarian morphology on ultrasound (≥20 antral follicles or ovarian volume ≥10 cm) or elevated AMH
FSH/LH testing is part of the exclusion workup, not a primary diagnostic criterion. However, it adds important clinical information about the mechanism and helps confirm the neuroendocrine pattern.

7. AMH as a Newer Marker

Anti-Mullerian Hormone (AMH) is now incorporated into the 2023 criteria as an alternative to ultrasound morphology. Elevated AMH reflects the large pool of small antral follicles. Some guidelines consider AMH superior to LH:FSH ratio for PCOS confirmation.
  • Henry's Clinical Diagnosis and Management by Laboratory Methods (Tietz)

Summary of Key Observations

PointClinical Relevance
LH:FSH ratio >2-3Supportive but not diagnostic on its own
Measure on Day 2-3Baseline follicular phase is the standard timing
LH pulsatilitySingle random value is unreliable - ratio may vary
Obese PCOSMay have normal LH:FSH - do not rule out PCOS on this alone
Elevated FSH + LHPoints to ovarian failure, NOT PCOS
Urinary LH kits unreliableBaseline LH elevation causes false positives in PCOS
FSH/LH is for exclusionNot a primary diagnostic criterion per Rotterdam 2023

Create a quick reference PDF on PCOS diagnostic workup including all hormone tests

Reading File
Running Command

mkdir -p /home/daytona/workspace/pcos-pdf && ls /home/daytona/workspace/

Writing File

~/pcos-pdf/generate_pcos_pdf.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.platypus import PageBreak
from reportlab.lib.colors import HexColor

OUTPUT = "/home/daytona/workspace/pcos-pdf/PCOS_Diagnostic_Workup_Quick_Reference.pdf"

# ── Color palette ──────────────────────────────────────────────
C_TEAL      = HexColor("#1A6B72")   # header bar
C_TEAL_LT   = HexColor("#D6EEF0")   # section header bg
C_PURPLE    = HexColor("#5B2C8D")   # accent headings
C_PURPLE_LT = HexColor("#EDE7F6")   # alternating row
C_GOLD      = HexColor("#C8860A")   # warning / note highlight
C_GOLD_LT   = HexColor("#FFF8E1")   # note box bg
C_RED       = HexColor("#C0392B")   # critical flags
C_GREEN     = HexColor("#1E7D45")   # normal range
C_GREY_BG   = HexColor("#F4F6F8")   # table alt row
C_GREY_LINE = HexColor("#B0BEC5")   # grid lines
C_WHITE     = colors.white
C_BLACK     = HexColor("#1A1A2E")

doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    topMargin=1.5*cm, bottomMargin=1.5*cm,
    leftMargin=1.8*cm, rightMargin=1.8*cm
)

W = A4[0] - 3.6*cm   # usable width

styles = getSampleStyleSheet()

# Custom styles
def ps(name, **kw):
    base = kw.pop("parent", "Normal")
    s = ParagraphStyle(name, parent=styles[base], **kw)
    return s

TITLE   = ps("TITLE",   fontSize=22, textColor=C_WHITE,    alignment=TA_CENTER, fontName="Helvetica-Bold", leading=28)
SUBTITLE= ps("SUBTITLE",fontSize=11, textColor=HexColor("#B2EBF2"), alignment=TA_CENTER, fontName="Helvetica", leading=16)
SEC_HDR = ps("SEC_HDR", fontSize=11, textColor=C_WHITE,    alignment=TA_LEFT,   fontName="Helvetica-Bold", leading=15, spaceBefore=6, spaceAfter=4)
BODY    = ps("BODY",    fontSize=9,  textColor=C_BLACK,    alignment=TA_JUSTIFY,fontName="Helvetica",      leading=13, spaceBefore=2)
BOLD9   = ps("BOLD9",   fontSize=9,  textColor=C_BLACK,    fontName="Helvetica-Bold", leading=13)
SMALL   = ps("SMALL",   fontSize=8,  textColor=HexColor("#555555"), fontName="Helvetica", leading=12)
NOTE    = ps("NOTE",    fontSize=8.5,textColor=HexColor("#5D4037"),fontName="Helvetica-Oblique", leading=12)
CELL_H  = ps("CELL_H",  fontSize=8.5,textColor=C_WHITE,   fontName="Helvetica-Bold", alignment=TA_CENTER, leading=12)
CELL    = ps("CELL",    fontSize=8.5,textColor=C_BLACK,    fontName="Helvetica",      alignment=TA_LEFT,   leading=12)
CELL_C  = ps("CELL_C",  fontSize=8.5,textColor=C_BLACK,    fontName="Helvetica",      alignment=TA_CENTER, leading=12)
CELL_B  = ps("CELL_B",  fontSize=8.5,textColor=C_BLACK,    fontName="Helvetica-Bold", alignment=TA_LEFT,   leading=12)
FOOT    = ps("FOOT",    fontSize=7.5,textColor=HexColor("#757575"), alignment=TA_CENTER, fontName="Helvetica-Oblique", leading=10)

story = []

# ── Helper: section header banner ─────────────────────────────
def sec(title, color=C_TEAL):
    tbl = Table([[Paragraph(f"  {title}", SEC_HDR)]], colWidths=[W])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), color),
        ("ROWBACKGROUNDS",(0,0),(-1,-1),[color]),
        ("TOPPADDING",(0,0),(-1,-1),5),
        ("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),8),
        ("RIGHTPADDING",(0,0),(-1,-1),8),
    ]))
    return tbl

def note_box(text, bg=C_GOLD_LT, border=C_GOLD):
    tbl = Table([[Paragraph(text, NOTE)]], colWidths=[W])
    tbl.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("BOX",(0,0),(-1,-1),1, border),
        ("TOPPADDING",(0,0),(-1,-1),5),
        ("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),8),
        ("RIGHTPADDING",(0,0),(-1,-1),8),
    ]))
    return tbl

def std_table(headers, rows, col_widths, alt=True):
    data = [[Paragraph(h, CELL_H) for h in headers]]
    for i, row in enumerate(rows):
        bg = C_GREY_BG if (alt and i % 2 == 1) else C_WHITE
        data.append([Paragraph(str(c), CELL) for c in row])
    tbl = Table(data, colWidths=col_widths, repeatRows=1)
    style = [
        ("BACKGROUND",(0,0),(-1,0), C_TEAL),
        ("GRID",(0,0),(-1,-1),0.4, C_GREY_LINE),
        ("ROWBACKGROUNDS",(0,1),(-1,-1),[C_WHITE, C_GREY_BG]),
        ("TOPPADDING",(0,0),(-1,-1),4),
        ("BOTTOMPADDING",(0,0),(-1,-1),4),
        ("LEFTPADDING",(0,0),(-1,-1),5),
        ("RIGHTPADDING",(0,0),(-1,-1),5),
        ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    ]
    tbl.setStyle(TableStyle(style))
    return tbl

# ═══════════════════════════════════════════════════════════════
# TITLE BLOCK
# ═══════════════════════════════════════════════════════════════
title_data = [[Paragraph("PCOS Diagnostic Workup", TITLE)],
              [Paragraph("Hormone Tests · Quick Reference Guide", SUBTITLE)],
              [Paragraph("Based on Rotterdam 2003 / Updated 2023 International Guidelines  |  Harrison's 22E · Berek & Novak · Henry's Clinical Lab Methods", SUBTITLE)]]
title_tbl = Table(title_data, colWidths=[W])
title_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,-1), C_TEAL),
    ("TOPPADDING",(0,0),(-1,0),14),
    ("BOTTOMPADDING",(0,-1),(-1,-1),12),
    ("TOPPADDING",(0,1),(-1,1),4),
    ("BOTTOMPADDING",(0,0),(-1,1),2),
    ("TOPPADDING",(0,2),(-1,2),2),
    ("LINEBELOW",(0,1),(-1,1),0.5, HexColor("#80CBC4")),
]))
story.append(title_tbl)
story.append(Spacer(1, 6))

# ═══════════════════════════════════════════════════════════════
# SECTION 1 – DIAGNOSTIC CRITERIA (Rotterdam 2023)
# ═══════════════════════════════════════════════════════════════
story.append(sec("1.  DIAGNOSTIC CRITERIA  —  Rotterdam 2023  (2 of 3 required, after exclusion of other causes)"))
story.append(Spacer(1, 4))

crit_data = [
    [Paragraph("Criterion", CELL_H), Paragraph("Details", CELL_H), Paragraph("Notes", CELL_H)],
    [Paragraph("<b>1. Menstrual Irregularity</b>", CELL),
     Paragraph("Oligomenorrhea (&lt;8 cycles/yr) or amenorrhea", CELL),
     Paragraph("In adolescents: wait ≥3 yrs post-menarche before diagnosing", CELL)],
    [Paragraph("<b>2. Hyperandrogenism</b>", CELL),
     Paragraph("Clinical (hirsutism, acne, alopecia) OR biochemical (↑ total/free testosterone)", CELL),
     Paragraph("mFG score ≥4–6 depending on ethnicity", CELL)],
    [Paragraph("<b>3. Polycystic Ovarian Morphology</b>", CELL),
     Paragraph("≥20 antral follicles or ovarian volume ≥10 cm³ on USS  OR  elevated AMH", CELL),
     Paragraph("Ultrasound criteria NOT used in adolescents", CELL)],
]
crit_tbl = Table(crit_data, colWidths=[3.8*cm, 8.2*cm, W-12*cm])
crit_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), C_TEAL),
    ("GRID",(0,0),(-1,-1),0.4, C_GREY_LINE),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[C_WHITE, C_GREY_BG]),
    ("TOPPADDING",(0,0),(-1,-1),5),
    ("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("RIGHTPADDING",(0,0),(-1,-1),5),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
]))
story.append(crit_tbl)
story.append(Spacer(1, 4))
story.append(note_box("⚠  PCOS is a DIAGNOSIS OF EXCLUSION. Always rule out: hypothyroidism, hyperprolactinemia, congenital adrenal hyperplasia (CAH), Cushing syndrome, androgen-secreting tumors, and primary ovarian insufficiency (POI) before confirming PCOS."))
story.append(Spacer(1, 8))

# ═══════════════════════════════════════════════════════════════
# SECTION 2 – COMPLETE HORMONE TEST PANEL
# ═══════════════════════════════════════════════════════════════
story.append(sec("2.  COMPLETE HORMONE TEST PANEL"))
story.append(Spacer(1, 4))

panel_headers = ["Test", "Purpose in PCOS", "Timing", "Reference / Interpretation"]
panel_rows = [
    # ── FSH/LH block ──
    ["FSH (Follicle-Stimulating Hormone)",
     "Assess gonadotropin axis; rule out ovarian failure or hypothalamic-pituitary dysfunction",
     "Day 2–3 of cycle (early follicular phase)",
     "Normal: 3–10 mIU/mL. Low/normal in PCOS. FSH >10 mIU/mL → diminished ovarian reserve"],
    ["LH (Luteinizing Hormone)",
     "Elevated LH drives ovarian androgen excess via thecal cell stimulation; key neuroendocrine marker",
     "Day 2–3 of cycle (fasting preferred)",
     "Lean PCOS: LH elevated (often 10–20 mIU/mL). Random value unreliable due to pulsatility"],
    ["LH : FSH Ratio",
     "Classic PCOS pattern: GnRH pulse frequency skew toward LH",
     "Same sample as above",
     "Ratio >2:1 or >3:1 supportive of PCOS. Minor criterion only — not in Rotterdam 2023 criteria. Normal in obese PCOS."],
    # ── Androgens ──
    ["Total Testosterone",
     "Primary biochemical hyperandrogenism marker; required for Rotterdam criterion 2",
     "8–10 AM fasting (diurnal variation); Day 2–5 preferred",
     "PCOS: typically 100–200 ng/dL. >200 ng/dL raises suspicion of androgen-secreting tumor"],
    ["Free Testosterone",
     "More sensitive than total T; useful when total T borderline normal",
     "Same as total testosterone",
     "Elevated free T with normal total T still confirms biochemical HA (Rotterdam criterion)"],
    ["DHEA-S (Dehydroepiandrosterone Sulfate)",
     "Adrenal androgen marker; distinguishes adrenal vs ovarian source of androgens",
     "Any time (relatively stable level)",
     ">700 µg/dL → adrenal tumor. Mildly elevated in PCOS (~50%). Very high → adrenal source"],
    ["17-OH Progesterone",
     "Rule out non-classical congenital adrenal hyperplasia (21-hydroxylase deficiency)",
     "Day 2–5, early AM",
     "<2 ng/mL: normal. >2 ng/mL: proceed to ACTH stimulation test. Key exclusion test"],
    # ── Prolactin/Thyroid ──
    ["Prolactin (PRL)",
     "Rule out hyperprolactinemia as cause of anovulation and irregular menses",
     "Fasting morning, no sexual activity prior",
     ">25–30 ng/mL → hyperprolactinemia. Mild elevation can be stress-related; recheck"],
    ["TSH (Thyroid Stimulating Hormone)",
     "Rule out hypothyroidism mimicking PCOS (weight gain, irregular cycles, anovulation)",
     "Any time (stable)",
     "Normal: 0.4–4.0 mIU/L. TSH >4 → hypothyroidism. Address before confirming PCOS"],
    # ── Insulin / Metabolic ──
    ["Fasting Insulin",
     "Assess insulin resistance (IR); IR amplifies ovarian androgen production",
     "8–12 hr fast",
     "No universal cutoff. HOMA-IR = [fasting glucose (mmol/L) × fasting insulin (µU/mL)] / 22.5. HOMA-IR >2.5 suggests IR"],
    ["Fasting Blood Glucose / HbA1c",
     "Screen for type 2 diabetes and prediabetes; PCOS carries significant metabolic risk",
     "Fasting (8–12 hrs)",
     "FBG ≥126 mg/dL or HbA1c ≥6.5% = diabetes. Impaired FBG: 100–125 mg/dL"],
    ["2-hr OGTT (Oral Glucose Tolerance Test)",
     "Preferred over FBG in PCOS for detecting glucose intolerance; AES-PCOS Society recommended",
     "Fasting; 75 g glucose load",
     "2-hr glucose ≥200 mg/dL = diabetes; 140–199 mg/dL = impaired glucose tolerance"],
    # ── AMH ──
    ["AMH (Anti-Mullerian Hormone)",
     "Reflects antral follicle pool; now included in 2023 PCOS criteria as alternative to USS",
     "Any day of cycle (stable across cycle)",
     "Elevated in PCOS (reflects large small-antral follicle pool). Lab/assay-specific cutoffs — consult local lab"],
    # ── Estradiol / Progesterone ──
    ["Estradiol (E2)",
     "Assess follicular activity; Day 3 E2 with FSH helps interpret ovarian reserve",
     "Day 2–3 of cycle",
     "Day 3 E2 >80 pg/mL + elevated FSH = ovarian stimulation/failure. Low in hypothalamic amenorrhea"],
    ["Progesterone (midluteal)",
     "Confirm ovulation occurred; useful in assessing anovulation",
     "Day 21 (28-day cycle) or 7 days before expected next period",
     ">3 ng/mL = ovulation confirmed. <3 ng/mL = anovulatory cycle"],
    # ── Cortisol ──
    ["24-hr Urinary Free Cortisol / 1 mg Overnight Dexamethasone Suppression Test",
     "Rule out Cushing syndrome — can mimic PCOS with central obesity, hirsutism, irregular cycles",
     "As directed",
     "Post-dexamethasone cortisol <1.8 µg/dL = Cushing excluded (screening)"],
    # ── Lipid profile ──
    ["Fasting Lipid Profile",
     "Screen for dyslipidemia; part of metabolic risk assessment mandatory in PCOS",
     "12-hr fast",
     "Look for: ↑ LDL, ↑ triglycerides, ↓ HDL — metabolic syndrome pattern common in PCOS"],
]

col_w = [3.5*cm, 5.0*cm, 3.2*cm, W - 11.7*cm]
panel_data = [[Paragraph(h, CELL_H) for h in panel_headers]]
row_colors = [
    C_TEAL_LT, C_TEAL_LT, C_PURPLE_LT,   # FSH, LH, ratio
    C_GREY_BG, C_WHITE, C_GREY_BG, C_WHITE,  # androgens
    C_GOLD_LT, C_GOLD_LT,                 # PRL, TSH
    C_WHITE, C_GREY_BG, C_WHITE,          # metabolic
    C_TEAL_LT,                            # AMH
    C_GREY_BG, C_WHITE,                   # E2, Prog
    C_GOLD_LT,                            # Cortisol
    C_WHITE,                              # Lipids
]
for i, row in enumerate(panel_rows):
    panel_data.append([Paragraph(str(c), CELL) for c in row])

panel_tbl = Table(panel_data, colWidths=col_w, repeatRows=1)
row_style = [
    ("BACKGROUND",(0,0),(-1,0), C_TEAL),
    ("GRID",(0,0),(-1,-1),0.35, C_GREY_LINE),
    ("TOPPADDING",(0,0),(-1,-1),4),
    ("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("RIGHTPADDING",(0,0),(-1,-1),5),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
]
for i, bg in enumerate(row_colors):
    row_style.append(("BACKGROUND",(0,i+1),(-1,i+1), bg))

panel_tbl.setStyle(TableStyle(row_style))
story.append(panel_tbl)
story.append(Spacer(1, 8))

# ═══════════════════════════════════════════════════════════════
# SECTION 3 – FSH / LH INTERPRETATION TABLE
# ═══════════════════════════════════════════════════════════════
story.append(sec("3.  FSH / LH PATTERN INTERPRETATION  —  Differential Diagnosis"))
story.append(Spacer(1, 4))

interp_headers = ["Condition", "FSH", "LH", "LH:FSH Ratio", "Key Distinguishing Features"]
interp_rows = [
    ["<b>PCOS (lean/typical)</b>", "Normal / Low-normal", "<b>↑ Elevated</b>", ">2:1 to 3:1",
     "Testosterone ↑, irregular cycles, polycystic ovaries on USS, anovulation"],
    ["<b>PCOS (obese)</b>", "Normal", "Normal or mildly ↑", "Often normal",
     "Obesity blunts LH; diagnosis relies more on testosterone + USS + AMH"],
    ["<b>Primary Ovarian Insufficiency (POI)</b>", "<b>↑↑ Very High</b>", "↑ Elevated", "<1:1 or near 1:1",
     "FSH >25 mIU/mL (x2), estradiol low, age <40; anti-thyroid Ab, karyotype"],
    ["<b>Hypothalamic Amenorrhea</b>", "Low / Normal", "Low / Normal", "~1:1",
     "Stress, weight loss, exercise; estradiol low; GnRH pulse frequency reduced"],
    ["<b>Hyperprolactinemia</b>", "Low-normal", "Low-normal", "Variable",
     "Prolactin ↑; galactorrhea; MRI pituitary; LH/FSH suppressed by prolactin"],
    ["<b>Non-classical CAH</b>", "Normal", "Normal", "Normal",
     "17-OH progesterone ↑ (>2 ng/mL Day 3); ACTH stim test positive"],
    ["<b>Hypothyroidism</b>", "Normal", "Normal", "Normal",
     "TSH ↑, free T4 ↓; may cause hyperprolactinemia secondarily"],
    ["<b>Androgen-Secreting Tumor</b>", "Normal/Low", "Normal/Low", "Normal",
     "Testosterone >200 ng/dL; rapid virilization; pelvic/adrenal imaging"],
]
col_w2 = [3.5*cm, 2.0*cm, 2.0*cm, 2.5*cm, W - 10*cm]
interp_data = [[Paragraph(h, CELL_H) for h in interp_headers]]
alt_colors2 = [C_WHITE, C_GREY_BG] * 10
for i, row in enumerate(interp_rows):
    interp_data.append([Paragraph(str(c), CELL) for c in row])

interp_tbl = Table(interp_data, colWidths=col_w2, repeatRows=1)
interp_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), C_PURPLE),
    ("GRID",(0,0),(-1,-1),0.35, C_GREY_LINE),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[C_WHITE, C_PURPLE_LT]),
    ("TOPPADDING",(0,0),(-1,-1),4),
    ("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("RIGHTPADDING",(0,0),(-1,-1),5),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
]))
story.append(interp_tbl)
story.append(Spacer(1, 8))

# ═══════════════════════════════════════════════════════════════
# SECTION 4 – FSH/LH TESTING PITFALLS
# ═══════════════════════════════════════════════════════════════
story.append(sec("4.  KEY PITFALLS IN FSH / LH TESTING IN PCOS", color=C_GOLD))
story.append(Spacer(1, 4))

pitfall_data = [
    [Paragraph("Pitfall", CELL_H), Paragraph("Explanation", CELL_H), Paragraph("Action", CELL_H)],
    [Paragraph("LH pulsatility", CELL_B),
     Paragraph("LH secreted in pulses — a single random draw may miss elevation. LH can vary 2-3x within hours.", CELL),
     Paragraph("Measure Day 2-3 fasting. Interpret ratio cautiously; single value unreliable.", CELL)],
    [Paragraph("Obesity blunts LH", CELL_B),
     Paragraph("Obese PCOS women often have normal LH:FSH due to adipose-mediated LH suppression.", CELL),
     Paragraph("Do not rule out PCOS based on normal LH alone. Rely on testosterone + USS + AMH.", CELL)],
    [Paragraph("Urinary LH kits (OPKs) unreliable", CELL_B),
     Paragraph("Chronically elevated baseline LH in PCOS gives false-positive ovulation predictor kit results throughout the cycle.", CELL),
     Paragraph("Use midluteal serum progesterone (Day 21) to confirm ovulation instead.", CELL)],
    [Paragraph("LH:FSH ratio is minor criterion only", CELL_B),
     Paragraph("Rotterdam 2023 guidelines do NOT include LH:FSH ratio as a primary diagnostic criterion.", CELL),
     Paragraph("Treat ratio as supportive evidence. Diagnosis requires 2 of 3 Rotterdam criteria.", CELL)],
    [Paragraph("Day of cycle matters", CELL_B),
     Paragraph("FSH measured in mid/late follicular phase may appear falsely elevated. LH peaks at ovulation.", CELL),
     Paragraph("Always measure FSH and LH on Day 2–3 of cycle for standardized interpretation.", CELL)],
    [Paragraph("Oral contraceptives suppress FSH/LH", CELL_B),
     Paragraph("OCP use suppresses gonadotropins — LH:FSH ratio will be falsely normal on OCP.", CELL),
     Paragraph("Ideally discontinue OCP ≥3 months before testing. Note OCP use in lab request.", CELL)],
]
p_col_w = [3.5*cm, 7.0*cm, W - 10.5*cm]
pitfall_tbl = Table(pitfall_data, colWidths=p_col_w, repeatRows=1)
pitfall_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), C_GOLD),
    ("GRID",(0,0),(-1,-1),0.35, C_GREY_LINE),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[C_WHITE, C_GOLD_LT]),
    ("TOPPADDING",(0,0),(-1,-1),4),
    ("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("RIGHTPADDING",(0,0),(-1,-1),5),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
]))
story.append(pitfall_tbl)
story.append(Spacer(1, 8))

# ═══════════════════════════════════════════════════════════════
# SECTION 5 – STEP-BY-STEP DIAGNOSTIC ALGORITHM
# ═══════════════════════════════════════════════════════════════
story.append(sec("5.  STEP-BY-STEP DIAGNOSTIC ALGORITHM"))
story.append(Spacer(1, 4))

steps = [
    ("Step 1 — History & Menstrual Pattern",
     "Document cycle frequency, duration, last menstrual period. ≥1 year of irregular cycles required. In adolescents: irregular menses ≥2 years post-menarche."),
    ("Step 2 — Clinical Hyperandrogenism Assessment",
     "Hirsutism: modified Ferriman-Gallwey (mFG) score ≥4–6. Also assess: acne, androgenic alopecia. Clinical HA alone can satisfy criterion 2."),
    ("Step 3 — Initial Hormone Screen (Exclusion Panel)",
     "Order: TSH, Prolactin, 17-OH Progesterone (Day 2–5 AM). These exclude hypothyroidism, hyperprolactinemia, and non-classical CAH — three key PCOS mimics."),
    ("Step 4 — Androgen Testing",
     "Total testosterone (AM fasting, Day 2–5). Free testosterone if total borderline. DHEA-S if adrenal source suspected (total T >200 ng/dL, rapid onset, virilization)."),
    ("Step 5 — FSH, LH & Estradiol",
     "Day 2–3 of cycle. FSH & LH to assess gonadotropin axis. Elevated FSH + LH → POI (not PCOS). Low FSH + LH → hypothalamic amenorrhea. Elevated LH with normal-low FSH → supports PCOS. Estradiol Day 3 to assess follicular reserve."),
    ("Step 6 — Metabolic Screening",
     "Fasting glucose, fasting insulin (HOMA-IR), HbA1c, fasting lipid profile. OGTT (75 g) preferred over FBG alone. Weight, BMI, waist circumference, blood pressure."),
    ("Step 7 — AMH",
     "Elevated AMH now accepted as an alternative to ultrasound in the 2023 guidelines. Use assay with validated PCOS cutoffs. Stable across cycle days."),
    ("Step 8 — Pelvic Ultrasound",
     "Transvaginal USS preferred. ≥20 antral follicles OR ovarian volume ≥10 cm³ in at least one ovary = polycystic morphology. Note: ultrasound NOT used to diagnose PCOS in adolescents."),
    ("Step 9 — Confirm Diagnosis",
     "Apply Rotterdam 2023 criteria: 2 of 3 criteria met + all exclusions cleared = PCOS confirmed. Document PCOS phenotype (1–4) for management planning."),
]

for step_title, step_body in steps:
    row = [[
        Paragraph(f"<b>{step_title}</b>", CELL_B),
        Paragraph(step_body, CELL)
    ]]
    t = Table(row, colWidths=[4.5*cm, W - 4.5*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,-1), C_TEAL_LT),
        ("BACKGROUND",(1,0),(1,-1), C_WHITE),
        ("BOX",(0,0),(-1,-1),0.5, C_GREY_LINE),
        ("LINEAFTER",(0,0),(0,-1),0.5, C_GREY_LINE),
        ("TOPPADDING",(0,0),(-1,-1),5),
        ("BOTTOMPADDING",(0,0),(-1,-1),5),
        ("LEFTPADDING",(0,0),(-1,-1),6),
        ("RIGHTPADDING",(0,0),(-1,-1),6),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    story.append(t)
    story.append(Spacer(1, 2))

story.append(Spacer(1, 6))

# ═══════════════════════════════════════════════════════════════
# SECTION 6 – ADDITIONAL TESTS BY INDICATION
# ═══════════════════════════════════════════════════════════════
story.append(sec("6.  ADDITIONAL TESTS BY INDICATION", color=C_PURPLE))
story.append(Spacer(1, 4))

add_headers = ["Indication", "Test(s) to Order", "What to Look For"]
add_rows = [
    ["Suspected Cushing syndrome\n(central obesity, striae, hypertension)",
     "24-hr urinary free cortisol\n1 mg overnight dexamethasone suppression test\nLate-night salivary cortisol",
     "Post-dex cortisol >1.8 µg/dL → Cushing not excluded → refer endocrinology"],
    ["Suspected androgen-secreting tumor\n(Testosterone >200 ng/dL, rapid virilization)",
     "Total and free testosterone\nDHEA-S\nPelvic/adrenal MRI or CT",
     "Very high T or DHEA-S; adrenal/ovarian mass on imaging → urgent referral"],
    ["Fertility evaluation\n(infertility workup, IVF planning)",
     "AMH\nAntral Follicle Count (AFC) on USS\nFSH Day 3 + Estradiol Day 3",
     "AMH + AFC assess ovarian reserve. FSH >10 = diminished reserve. High AMH in PCOS → OHSS risk with IVF"],
    ["Adolescent PCOS (puberty period)",
     "Testosterone (total + free)\nLH, FSH\nDo NOT use USS or AMH for criteria",
     "Diagnosis requires: irregular menses + hyperandrogenism only. Both criteria must be met."],
    ["Pregnancy or fertility treatment",
     "Progesterone, hCG\nThyroid function (TSH + free T4)\nFasting glucose",
     "Progesterone monitoring post-ovulation induction. Thyroid optimization before conception."],
    ["Monitoring on treatment (OCP/metformin)",
     "Annual: fasting glucose, HbA1c, lipid profile, BP\nTestosterone if symptomatic",
     "OCP suppresses FSH/LH — do not use to monitor gonadotropins on treatment"],
]
add_col_w = [4.5*cm, 5.0*cm, W - 9.5*cm]
story.append(std_table(add_headers, add_rows, add_col_w))
story.append(Spacer(1, 8))

# ═══════════════════════════════════════════════════════════════
# SECTION 7 – QUICK REFERENCE: PCOS PHENOTYPES
# ═══════════════════════════════════════════════════════════════
story.append(sec("7.  PCOS PHENOTYPES  (Rotterdam Classification)"))
story.append(Spacer(1, 4))

phen_headers = ["Phenotype", "HA (Clinical/Biochem)", "Ovulatory Dysfunction", "Polycystic Ovaries on USS", "Notes"]
phen_rows = [
    ["1 — Full PCOS", "✓", "✓", "✓", "Most common; classic presentation; highest metabolic risk"],
    ["2 — Classic without PCO", "✓", "✓", "✗", "Classic NIH 1990 phenotype; significant metabolic risk"],
    ["3 — Ovulatory PCOS", "✓", "✗", "✓", "Regular cycles but biochemical HA + polycystic USS; milder metabolic risk"],
    ["4 — Non-androgenic PCOS", "✗", "✓", "✓", "No hyperandrogenism; irregular cycles + polycystic USS; mildest phenotype; exclude other causes carefully"],
]
phen_col_w = [3.5*cm, 2.8*cm, 3.0*cm, 3.2*cm, W - 12.5*cm]
phen_data = [[Paragraph(h, CELL_H) for h in phen_headers]]
phen_alt = [C_WHITE, C_GREY_BG, C_TEAL_LT, C_PURPLE_LT]
for i, row in enumerate(phen_rows):
    phen_data.append([Paragraph(str(c), CELL) for c in row])

phen_tbl = Table(phen_data, colWidths=phen_col_w, repeatRows=1)
phen_style = [
    ("BACKGROUND",(0,0),(-1,0), C_TEAL),
    ("GRID",(0,0),(-1,-1),0.35, C_GREY_LINE),
    ("TOPPADDING",(0,0),(-1,-1),4),
    ("BOTTOMPADDING",(0,0),(-1,-1),4),
    ("LEFTPADDING",(0,0),(-1,-1),5),
    ("RIGHTPADDING",(0,0),(-1,-1),5),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
]
for i, bg in enumerate(phen_alt):
    phen_style.append(("BACKGROUND",(0,i+1),(-1,i+1), bg))
phen_tbl.setStyle(TableStyle(phen_style))
story.append(phen_tbl)
story.append(Spacer(1, 8))

# ═══════════════════════════════════════════════════════════════
# FOOTER NOTE
# ═══════════════════════════════════════════════════════════════
story.append(HRFlowable(width=W, thickness=0.5, color=C_GREY_LINE))
story.append(Spacer(1, 4))
story.append(Paragraph(
    "Sources: Harrison's Principles of Internal Medicine 22E (2025) · Berek &amp; Novak's Gynecology · "
    "Henry's Clinical Diagnosis &amp; Management by Laboratory Methods (Tietz) · Dermatology 2-Volume Set 5e · "
    "Textbook of Family Medicine 9e  |  Rotterdam Criteria (2003, updated 2023 International PCOS Guidelines)  |  "
    "For educational / clinical reference use. Verify all reference ranges with your local laboratory.",
    FOOT
))

# ── Build ──────────────────────────────────────────────────────
doc.build(story)
print(f"PDF saved: {OUTPUT}")
Running Command

cd /home/daytona/workspace/pcos-pdf && python generate_pcos_pdf.py

Running Command

ls -lh /home/daytona/workspace/pcos-pdf/PCOS_Diagnostic_Workup_Quick_Reference.pdf

Your PDF is ready for download:

PCOS Diagnostic Workup Quick Reference

PDF Document · PDF


Here is what the PDF contains across 7 sections:
SectionContent
1. Diagnostic CriteriaRotterdam 2003 / 2023 table - all 3 criteria with notes + exclusion warning box
2. Complete Hormone Test Panel16 tests: FSH, LH, LH:FSH ratio, Total T, Free T, DHEA-S, 17-OH Progesterone, Prolactin, TSH, Fasting Insulin, FBG/HbA1c, OGTT, AMH, Estradiol, Progesterone (midluteal), Cortisol, Lipid Profile - with timing, purpose, and reference ranges for each
3. FSH/LH Pattern InterpretationDifferential diagnosis table: PCOS lean, PCOS obese, POI, hypothalamic amenorrhea, hyperprolactinemia, CAH, hypothyroidism, androgen tumor
4. Key Pitfalls6 pitfalls: LH pulsatility, obesity effect, OPK false positives, LH:FSH as minor criterion, day of cycle, OCP suppression
5. Step-by-Step Algorithm9-step sequential diagnostic workflow from history to PCOS confirmation
6. Additional Tests by IndicationTests for Cushing, androgen tumor, fertility workup, adolescent PCOS, pregnancy
7. PCOS PhenotypesAll 4 Rotterdam phenotypes with HA/ovulation/USS status and clinical notes

Create a quick reference PDF on PCOS diagnostic workup including all hormone tests

Running Command

ls -lh /home/daytona/workspace/pcos-pdf/PCOS_Diagnostic_Workup_Quick_Reference.pdf

The file is confirmed present (18 KB, generated at 15:38 today). Here is your download link:

PCOS Diagnostic Workup Quick Reference

PDF Document · PDF

The PDF covers all 7 sections we built:
  1. Rotterdam 2023 Diagnostic Criteria (2 of 3 required)
  2. Complete Hormone Panel - 16 tests (FSH, LH, LH:FSH ratio, Total/Free Testosterone, DHEA-S, 17-OH Progesterone, Prolactin, TSH, AMH, Estradiol, Progesterone, Cortisol, Fasting Insulin, HbA1c, OGTT, Lipid Profile) with timing and reference ranges
  3. FSH/LH Pattern Interpretation differential diagnosis table
  4. Key Testing Pitfalls (LH pulsatility, obesity effect, OPK false positives, OCP suppression, etc.)
  5. 9-Step Diagnostic Algorithm
  6. Additional Tests by Indication (Cushing, androgen tumor, fertility, adolescent PCOS)
  7. PCOS Phenotypes 1-4
If you need any changes - additional sections, different layout, or a revised format - just let me know.

Generate a patient-friendly PCOS information handout in PDF format

Writing File

~/pcos-pdf/generate_pcos_patient_handout.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.lib.colors import HexColor
from reportlab.graphics.shapes import Drawing, Rect, Circle, String, Line
from reportlab.graphics import renderPDF

OUTPUT = "/home/daytona/workspace/pcos-pdf/PCOS_Patient_Handout.pdf"

# ── Palette (soft, warm, approachable) ─────────────────────────
C_ROSE       = HexColor("#C2185B")   # header / accent
C_ROSE_LT    = HexColor("#FCE4EC")   # soft pink bg
C_TEAL       = HexColor("#00796B")   # section headers
C_TEAL_LT    = HexColor("#E0F2F1")   # teal light bg
C_PURPLE     = HexColor("#6A1B9A")   # accent 2
C_PURPLE_LT  = HexColor("#F3E5F5")
C_AMBER      = HexColor("#F57F17")   # tips/notes
C_AMBER_LT   = HexColor("#FFFDE7")
C_GREEN      = HexColor("#2E7D32")
C_GREEN_LT   = HexColor("#E8F5E9")
C_BLUE       = HexColor("#1565C0")
C_BLUE_LT    = HexColor("#E3F2FD")
C_GREY_LT    = HexColor("#F5F5F5")
C_GREY_LINE  = HexColor("#CFD8DC")
C_BLACK      = HexColor("#212121")
C_DARK_GREY  = HexColor("#424242")
C_WHITE      = colors.white

doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    topMargin=1.4*cm, bottomMargin=1.4*cm,
    leftMargin=1.8*cm, rightMargin=1.8*cm
)
W = A4[0] - 3.6*cm

def ps(name, **kw):
    base = kw.pop("parent", "Normal")
    return ParagraphStyle(name, parent=getSampleStyleSheet()[base], **kw)

# Styles
TITLE    = ps("TITLE",   fontSize=26, textColor=C_WHITE,  alignment=TA_CENTER, fontName="Helvetica-Bold", leading=32)
TAGLINE  = ps("TAGLINE", fontSize=12, textColor=HexColor("#F8BBD0"), alignment=TA_CENTER, fontName="Helvetica-Oblique", leading=17)
SEC_H    = ps("SEC_H",   fontSize=12, textColor=C_WHITE,  alignment=TA_LEFT,   fontName="Helvetica-Bold", leading=16, spaceBefore=4, spaceAfter=2)
BODY     = ps("BODY",    fontSize=10, textColor=C_BLACK,  alignment=TA_JUSTIFY,fontName="Helvetica",      leading=15, spaceBefore=2)
BODY_L   = ps("BODY_L",  fontSize=10, textColor=C_BLACK,  alignment=TA_LEFT,   fontName="Helvetica",      leading=15, spaceBefore=2)
BOLD10   = ps("BOLD10",  fontSize=10, textColor=C_BLACK,  fontName="Helvetica-Bold", leading=15)
CELL     = ps("CELL",    fontSize=9.5,textColor=C_BLACK,  fontName="Helvetica",      leading=14, alignment=TA_LEFT)
CELL_H   = ps("CELL_H",  fontSize=9.5,textColor=C_WHITE,  fontName="Helvetica-Bold", leading=14, alignment=TA_LEFT)
CELL_C   = ps("CELL_C",  fontSize=9.5,textColor=C_BLACK,  fontName="Helvetica",      leading=14, alignment=TA_CENTER)
NOTE     = ps("NOTE",    fontSize=9,  textColor=HexColor("#4E342E"), fontName="Helvetica-Oblique", leading=13)
TIP_H    = ps("TIP_H",   fontSize=10, textColor=C_AMBER,  fontName="Helvetica-Bold", leading=14)
SMALL    = ps("SMALL",   fontSize=8,  textColor=HexColor("#757575"), fontName="Helvetica", leading=11, alignment=TA_CENTER)
Q        = ps("Q",       fontSize=10, textColor=C_TEAL,   fontName="Helvetica-Bold", leading=14)
ANS      = ps("ANS",     fontSize=10, textColor=C_BLACK,  fontName="Helvetica",      leading=15, alignment=TA_JUSTIFY)
BULLET   = ps("BULLET",  fontSize=10, textColor=C_BLACK,  fontName="Helvetica", leading=15, leftIndent=12, firstLineIndent=-12)
EMPH     = ps("EMPH",    fontSize=10, textColor=C_PURPLE, fontName="Helvetica-Bold", leading=15, alignment=TA_CENTER)

story = []

# ══════════════════════════════════════════════════════════════
# TITLE BANNER
# ══════════════════════════════════════════════════════════════
def title_banner():
    rows = [
        [Paragraph("Understanding PCOS", TITLE)],
        [Paragraph("Polycystic Ovary Syndrome", TAGLINE)],
        [Paragraph("A guide for patients &amp; families", TAGLINE)],
    ]
    t = Table(rows, colWidths=[W])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), C_ROSE),
        ("TOPPADDING",  (0,0), (-1,0), 16),
        ("BOTTOMPADDING",(0,-1),(-1,-1), 14),
        ("TOPPADDING",  (0,1), (-1,1), 3),
        ("BOTTOMPADDING",(0,0),(-1,1), 3),
        ("TOPPADDING",  (0,2), (-1,2), 2),
    ]))
    return t

story.append(title_banner())
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# Helper: section header
# ══════════════════════════════════════════════════════════════
def sec(icon, title, color=C_TEAL):
    t = Table([[Paragraph(f"  {icon}  {title}", SEC_H)]], colWidths=[W])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), color),
        ("TOPPADDING",(0,0),(-1,-1),6),
        ("BOTTOMPADDING",(0,0),(-1,-1),6),
        ("LEFTPADDING",(0,0),(-1,-1),6),
    ]))
    return t

def info_box(text, bg=C_AMBER_LT, border=C_AMBER, style=NOTE):
    t = Table([[Paragraph(text, style)]], colWidths=[W])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("BOX",(0,0),(-1,-1),1.2, border),
        ("TOPPADDING",(0,0),(-1,-1),7),
        ("BOTTOMPADDING",(0,0),(-1,-1),7),
        ("LEFTPADDING",(0,0),(-1,-1),10),
        ("RIGHTPADDING",(0,0),(-1,-1),10),
    ]))
    return t

def two_col(left_content, right_content, lw=None, rw=None):
    lw = lw or W * 0.48
    rw = rw or W * 0.48
    t = Table([[left_content, right_content]], colWidths=[lw, W - lw - 0.2*cm])
    t.setStyle(TableStyle([
        ("VALIGN",(0,0),(-1,-1),"TOP"),
        ("LEFTPADDING",(0,0),(-1,-1),0),
        ("RIGHTPADDING",(0,0),(-1,-1),0),
        ("TOPPADDING",(0,0),(-1,-1),0),
        ("BOTTOMPADDING",(0,0),(-1,-1),0),
    ]))
    return t

def bullet_table(items, bullet="•", color=C_TEAL, bg=C_WHITE):
    rows = []
    for item in items:
        rows.append([
            Paragraph(f'<font color="#{color.hexval()[2:]}"><b>{bullet}</b></font>', CELL),
            Paragraph(item, CELL)
        ])
    t = Table(rows, colWidths=[0.4*cm, W - 0.4*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), bg),
        ("TOPPADDING",(0,0),(-1,-1),2),
        ("BOTTOMPADDING",(0,0),(-1,-1),2),
        ("LEFTPADDING",(0,0),(-1,-1),4),
        ("RIGHTPADDING",(0,0),(-1,-1),4),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    return t

# ══════════════════════════════════════════════════════════════
# SECTION 1 – WHAT IS PCOS?
# ══════════════════════════════════════════════════════════════
story.append(sec("❓", "What Is PCOS?", C_ROSE))
story.append(Spacer(1, 5))
story.append(Paragraph(
    "PCOS (Polycystic Ovary Syndrome) is one of the most common hormonal conditions in women of "
    "reproductive age, affecting about <b>1 in 10 women worldwide</b>. It affects how the ovaries work "
    "and causes an imbalance in hormones — particularly higher-than-normal levels of male hormones "
    "(androgens). Despite its name, you do not need to have cysts on your ovaries to have PCOS.",
    BODY))
story.append(Spacer(1, 5))
story.append(info_box(
    "💡  PCOS is NOT your fault. It is caused by a combination of genetic and hormonal factors. "
    "It cannot be 'caught' and is not caused by anything you did or didn't do.",
    bg=C_TEAL_LT, border=C_TEAL, style=NOTE))
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 2 – SYMPTOMS
# ══════════════════════════════════════════════════════════════
story.append(sec("🩺", "Common Symptoms", C_ROSE))
story.append(Spacer(1, 5))
story.append(Paragraph(
    "PCOS affects every woman differently. You may have a few symptoms or many. "
    "Symptoms often start in your teens or early 20s.", BODY))
story.append(Spacer(1, 5))

sym_left = [
    ["<b>Symptom</b>", "<b>What It Means</b>"],
    ["Irregular or missed periods", "Ovulation does not happen regularly, causing unpredictable cycles"],
    ["Excess hair on face, chest, or back (hirsutism)", "Higher androgen (male hormone) levels stimulate hair follicles"],
    ["Acne or oily skin", "Androgens increase oil production in skin glands"],
    ["Thinning hair or hair loss on scalp", "Again related to elevated androgens acting on hair follicles"],
    ["Weight gain (especially around the belly)", "Insulin resistance makes it easier to gain and harder to lose weight"],
    ["Difficulty getting pregnant", "Irregular ovulation means fewer chances of conception each month"],
    ["Mood changes, anxiety, or low mood", "Hormonal imbalance and living with a chronic condition can affect mental health"],
    ["Fatigue", "Often linked to poor sleep, blood sugar swings, or low mood"],
]
sym_col_w = [4.5*cm, W - 4.5*cm]
sym_data = [[Paragraph(r[0], CELL_H if i==0 else CELL_C),
             Paragraph(r[1], CELL_H if i==0 else CELL)]
            for i, r in enumerate(sym_left)]
sym_tbl = Table(sym_data, colWidths=sym_col_w, repeatRows=1)
sym_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), C_ROSE),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[C_WHITE, C_ROSE_LT]),
    ("GRID",(0,0),(-1,-1),0.35, C_GREY_LINE),
    ("TOPPADDING",(0,0),(-1,-1),5),
    ("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("RIGHTPADDING",(0,0),(-1,-1),6),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
]))
story.append(sym_tbl)
story.append(Spacer(1, 5))
story.append(info_box(
    "⚠️  Not all women with PCOS have all symptoms. Some women only discover they have PCOS "
    "when they have difficulty getting pregnant. See your doctor if you have irregular periods or "
    "any of the symptoms above.",
    bg=C_AMBER_LT, border=C_AMBER))
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 3 – HOW IS PCOS DIAGNOSED?
# ══════════════════════════════════════════════════════════════
story.append(sec("🔬", "How Is PCOS Diagnosed?", C_TEAL))
story.append(Spacer(1, 5))
story.append(Paragraph(
    "There is no single test for PCOS. Your doctor will use a combination of your symptoms, "
    "blood tests, and an ultrasound scan. According to international guidelines (<b>Rotterdam Criteria 2023</b>), "
    "you need to have <b>2 out of the following 3 features</b>, after other conditions have been ruled out:", BODY))
story.append(Spacer(1, 5))

diag_rows = [
    ["Feature 1", "Irregular or absent periods\n(fewer than 8 menstrual cycles per year, or no periods)"],
    ["Feature 2", "Signs of high male hormones (androgens)\nEither on examination (e.g. excess facial hair, acne) OR in your blood tests"],
    ["Feature 3", "Polycystic-looking ovaries on ultrasound scan\nOR an elevated AMH blood test result"],
]
diag_col_w = [2.8*cm, W - 2.8*cm]
diag_data = [[Paragraph(f"<b>{r[0]}</b>", CELL), Paragraph(r[1], CELL)] for r in diag_rows]
diag_colors = [C_TEAL_LT, C_GREEN_LT, C_PURPLE_LT]
diag_tbl = Table(diag_data, colWidths=diag_col_w)
diag_tbl.setStyle(TableStyle([
    ("BOX",(0,0),(-1,-1),1, C_GREY_LINE),
    ("LINEBELOW",(0,0),(-1,-2),0.5, C_GREY_LINE),
    ("TOPPADDING",(0,0),(-1,-1),7),
    ("BOTTOMPADDING",(0,0),(-1,-1),7),
    ("LEFTPADDING",(0,0),(-1,-1),8),
    ("RIGHTPADDING",(0,0),(-1,-1),8),
    ("VALIGN",(0,0),(-1,-1),"MIDDLE"),
    *[("BACKGROUND",(0,i),(-1,i), diag_colors[i]) for i in range(3)],
]))
story.append(diag_tbl)
story.append(Spacer(1, 5))
story.append(info_box(
    "📋  PCOS is a diagnosis of exclusion — your doctor must first rule out other conditions "
    "that can look similar, such as thyroid problems, raised prolactin hormone, or adrenal gland conditions.",
    bg=C_BLUE_LT, border=C_BLUE))
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 4 – BLOOD TESTS EXPLAINED
# ══════════════════════════════════════════════════════════════
story.append(sec("🩸", "Blood Tests Your Doctor May Order", C_TEAL))
story.append(Spacer(1, 5))
story.append(Paragraph(
    "Blood tests help confirm the diagnosis and check for related health conditions. "
    "Here is a plain-language guide to each test:", BODY))
story.append(Spacer(1, 5))

tests = [
    ("Testosterone (Total & Free)",
     "Checks the level of the main 'male hormone' in your blood. Women normally have small "
     "amounts, but in PCOS it is often higher than normal. This can cause excess hair and acne.",
     C_ROSE_LT, C_ROSE),
    ("LH & FSH\n(Luteinising Hormone & Follicle-Stimulating Hormone)",
     "These are 'messenger hormones' from your brain that tell your ovaries to release an egg. "
     "In PCOS, LH is often higher than FSH, which disrupts ovulation. <i>Note: these are measured "
     "on Day 2–3 of your period for best results.</i>",
     C_TEAL_LT, C_TEAL),
    ("AMH\n(Anti-Müllerian Hormone)",
     "Reflects the number of small egg follicles in your ovaries. Women with PCOS often have a "
     "higher AMH level because they have more follicles than usual. AMH can be measured any day "
     "of your cycle.",
     C_PURPLE_LT, C_PURPLE),
    ("17-OH Progesterone",
     "Rules out a condition called Congenital Adrenal Hyperplasia (CAH), which can look very "
     "similar to PCOS. This is measured on Days 2–5 of your cycle, in the morning.",
     C_GREEN_LT, C_GREEN),
    ("Prolactin",
     "A hormone produced by the pituitary gland (in the brain). High prolactin can stop ovulation "
     "and cause irregular periods — so it needs to be ruled out before confirming PCOS.",
     C_BLUE_LT, C_BLUE),
    ("TSH (Thyroid Stimulating Hormone)",
     "Checks your thyroid gland. An underactive thyroid can cause weight gain, irregular periods, "
     "and tiredness — symptoms that overlap with PCOS. Can be tested any time of day.",
     C_AMBER_LT, C_AMBER),
    ("Fasting Blood Glucose & HbA1c",
     "Checks your blood sugar level and your average blood sugar over 3 months. Women with PCOS "
     "are more likely to develop prediabetes or type 2 diabetes, especially with weight gain.",
     C_ROSE_LT, C_ROSE),
    ("Fasting Insulin / HOMA-IR",
     "Checks whether your body is responding properly to insulin (the hormone that controls blood "
     "sugar). Many women with PCOS have 'insulin resistance' — their body makes extra insulin to "
     "keep blood sugar normal, which worsens hormone imbalance.",
     C_TEAL_LT, C_TEAL),
    ("DHEA-S",
     "Another male hormone, but made by the adrenal glands (above the kidneys) rather than the "
     "ovaries. Helps distinguish whether excess androgens come from the ovaries (PCOS) or adrenal "
     "glands (adrenal condition).",
     C_PURPLE_LT, C_PURPLE),
    ("Fasting Lipid Profile\n(Cholesterol & Triglycerides)",
     "PCOS increases the risk of unhealthy cholesterol levels. This test checks LDL ('bad') "
     "cholesterol, HDL ('good') cholesterol, and triglycerides (blood fats).",
     C_GREEN_LT, C_GREEN),
]

for test_name, test_desc, bg, border in tests:
    row = [[
        Paragraph(f"<b>{test_name}</b>", BOLD10),
        Paragraph(test_desc, CELL)
    ]]
    t = Table(row, colWidths=[4.2*cm, W - 4.2*cm])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,-1), bg),
        ("BACKGROUND",(1,0),(1,-1), C_WHITE),
        ("BOX",(0,0),(-1,-1),0.8, border),
        ("LINEAFTER",(0,0),(0,-1),0.8, border),
        ("TOPPADDING",(0,0),(-1,-1),6),
        ("BOTTOMPADDING",(0,0),(-1,-1),6),
        ("LEFTPADDING",(0,0),(-1,-1),7),
        ("RIGHTPADDING",(0,0),(-1,-1),7),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    story.append(t)
    story.append(Spacer(1, 3))

story.append(Spacer(1, 5))
story.append(info_box(
    "📌  For hormone tests like LH, FSH, testosterone and 17-OH progesterone, timing matters. "
    "Your doctor will usually ask you to come in on Day 2–5 of your menstrual cycle, fasting "
    "(nothing to eat for 8–10 hours). If you are on the contraceptive pill, these tests may need "
    "to wait until 3 months after stopping, as the pill suppresses hormone levels.",
    bg=C_AMBER_LT, border=C_AMBER))
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 5 – LONG-TERM HEALTH RISKS
# ══════════════════════════════════════════════════════════════
story.append(sec("❤️", "Long-Term Health Conditions to Be Aware Of", C_PURPLE))
story.append(Spacer(1, 5))
story.append(Paragraph(
    "PCOS can increase the risk of certain health conditions over time. "
    "The good news is that many of these risks can be <b>significantly reduced</b> with the "
    "right lifestyle changes and medical treatment.", BODY))
story.append(Spacer(1, 5))

risk_rows = [
    ["Health Condition", "What Increases the Risk?", "How to Reduce Your Risk"],
    ["Type 2 Diabetes\n& Prediabetes",
     "Insulin resistance, weight gain",
     "Healthy diet, regular exercise, regular blood sugar checks"],
    ["Cardiovascular Disease\n(Heart & Blood Vessels)",
     "Unhealthy cholesterol, high blood pressure, obesity",
     "Exercise, healthy eating, weight management, stop smoking"],
    ["Endometrial Cancer\n(Womb Cancer)",
     "Infrequent periods mean the womb lining builds up",
     "Induce regular periods (with medication if needed); lose weight if overweight"],
    ["Obstructive Sleep Apnoea",
     "Obesity and hormonal factors",
     "Weight loss, sleep study if snoring or poor sleep"],
    ["Mental Health Problems\n(Anxiety, Depression)",
     "Hormonal imbalance, body image concerns, fertility worries",
     "Seek support early; therapy, peer support groups, medication if needed"],
    ["Difficulty Conceiving",
     "Irregular ovulation reduces monthly chance of pregnancy",
     "Fertility treatments available; lifestyle changes improve ovulation"],
]
risk_col_w = [3.8*cm, 4.5*cm, W - 8.3*cm]
risk_data = [[Paragraph(str(c), CELL_H if i==0 else CELL) for c in r] for i, r in enumerate(risk_rows)]
risk_tbl = Table(risk_data, colWidths=risk_col_w, repeatRows=1)
risk_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), C_PURPLE),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[C_WHITE, C_PURPLE_LT]),
    ("GRID",(0,0),(-1,-1),0.35, C_GREY_LINE),
    ("TOPPADDING",(0,0),(-1,-1),5),
    ("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("RIGHTPADDING",(0,0),(-1,-1),6),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
]))
story.append(risk_tbl)
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 6 – TREATMENT OPTIONS
# ══════════════════════════════════════════════════════════════
story.append(sec("💊", "Treatment Options", C_GREEN))
story.append(Spacer(1, 5))
story.append(Paragraph(
    "There is no cure for PCOS, but the symptoms can be very well managed. "
    "Treatment is tailored to your specific symptoms and goals "
    "(e.g. managing periods, reducing hair growth, getting pregnant, or improving metabolic health).",
    BODY))
story.append(Spacer(1, 6))

# Lifestyle block
lifestyle_items = [
    "<b>Healthy diet:</b> Low glycaemic index (GI) foods — wholegrains, legumes, vegetables, lean protein. Reduce processed foods and sugary drinks.",
    "<b>Regular exercise:</b> Aim for at least 150 minutes of moderate activity per week. Both cardio and strength training are beneficial.",
    "<b>Weight management:</b> Losing even 5–10% of body weight can restore ovulation, improve periods, and reduce androgen levels in overweight women.",
    "<b>Stress reduction:</b> Chronic stress worsens insulin resistance. Sleep well (7–9 hours), try yoga, mindfulness, or relaxation techniques.",
]

lifestyle_tbl = Table([
    [Paragraph("🥗  Lifestyle Changes (First-Line Treatment for ALL Women with PCOS)", TIP_H)],
    [bullet_table(lifestyle_items, color=C_GREEN)],
], colWidths=[W])
lifestyle_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), C_GREEN_LT),
    ("BACKGROUND",(0,1),(-1,1), C_WHITE),
    ("BOX",(0,0),(-1,-1),1, C_GREEN),
    ("TOPPADDING",(0,0),(-1,0),7),
    ("BOTTOMPADDING",(0,0),(-1,0),5),
    ("TOPPADDING",(0,1),(-1,1),4),
    ("BOTTOMPADDING",(0,1),(-1,1),6),
    ("LEFTPADDING",(0,0),(-1,-1),10),
    ("RIGHTPADDING",(0,0),(-1,-1),10),
]))
story.append(lifestyle_tbl)
story.append(Spacer(1, 6))

# Medications
med_rows = [
    ["Treatment", "What It Does", "Used For"],
    ["Combined Oral Contraceptive Pill (OCP)",
     "Regulates periods, lowers androgen levels, improves acne and excess hair",
     "Irregular periods, acne, hirsutism"],
    ["Metformin",
     "Improves insulin resistance, helps regulate periods, may aid weight management",
     "Insulin resistance, prediabetes, irregular cycles, fertility support"],
    ["Anti-androgens\n(e.g. spironolactone, cyproterone acetate)",
     "Block the effect of male hormones on the skin and hair follicles",
     "Hirsutism (excess hair), acne, hair loss"],
    ["Clomifene / Letrozole",
     "Stimulates ovulation to help you get pregnant",
     "Fertility treatment for women trying to conceive"],
    ["Gonadotrophins\n(FSH / LH injections)",
     "Directly stimulate the ovaries to produce eggs",
     "Fertility treatment when tablets have not worked"],
    ["Inositol\n(Myo-inositol / D-chiro-inositol)",
     "Supplement that may improve insulin sensitivity and ovulation regularity",
     "Insulin resistance, ovulation support — ask your doctor"],
    ["Eflornithine cream",
     "Slows the growth of facial hair",
     "Facial hirsutism"],
]
med_col_w = [3.8*cm, 5.5*cm, W - 9.3*cm]
med_data = [[Paragraph(str(c), CELL_H if i==0 else CELL) for c in r] for i, r in enumerate(med_rows)]
med_tbl = Table(med_data, colWidths=med_col_w, repeatRows=1)
med_tbl.setStyle(TableStyle([
    ("BACKGROUND",(0,0),(-1,0), C_GREEN),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),[C_WHITE, C_GREEN_LT]),
    ("GRID",(0,0),(-1,-1),0.35, C_GREY_LINE),
    ("TOPPADDING",(0,0),(-1,-1),5),
    ("BOTTOMPADDING",(0,0),(-1,-1),5),
    ("LEFTPADDING",(0,0),(-1,-1),6),
    ("RIGHTPADDING",(0,0),(-1,-1),6),
    ("VALIGN",(0,0),(-1,-1),"TOP"),
]))
story.append(med_tbl)
story.append(Spacer(1, 5))
story.append(info_box(
    "💊  Always take medications only as prescribed by your doctor. Do NOT start or stop any "
    "treatment without medical advice. Some medications interact with each other or are not safe "
    "in pregnancy.",
    bg=C_AMBER_LT, border=C_AMBER))
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 7 – PCOS & FERTILITY
# ══════════════════════════════════════════════════════════════
story.append(sec("👶", "PCOS & Fertility — What You Need to Know", C_ROSE))
story.append(Spacer(1, 5))
story.append(Paragraph(
    "One of the most common concerns for women with PCOS is fertility. "
    "PCOS is a leading cause of irregular ovulation, which makes it harder — but <b>not impossible</b> — "
    "to get pregnant.", BODY))
story.append(Spacer(1, 5))

fert_items = [
    "Most women with PCOS <b>can and do</b> get pregnant, either naturally or with medical help.",
    "Losing weight (if you are overweight) is one of the most effective ways to restore ovulation.",
    "Letrozole is the first-line fertility medication for PCOS (recommended over clomifene in current guidelines).",
    "If tablets do not work, FSH injections or IVF may be offered.",
    "Women with PCOS undergoing IVF have a higher risk of Ovarian Hyperstimulation Syndrome (OHSS) — your fertility team will monitor this closely.",
    "During pregnancy, PCOS is associated with a slightly higher risk of gestational diabetes and high blood pressure — regular antenatal checks are important.",
]
story.append(bullet_table(fert_items, color=C_ROSE, bg=C_ROSE_LT))
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 8 – LOOKING AFTER YOUR MENTAL HEALTH
# ══════════════════════════════════════════════════════════════
story.append(sec("🧠", "Looking After Your Mental Health", C_PURPLE))
story.append(Spacer(1, 5))
story.append(Paragraph(
    "Living with PCOS can be emotionally challenging. Body image concerns, fertility worries, "
    "and the ongoing nature of the condition mean that many women with PCOS experience anxiety, "
    "depression, or low self-esteem. <b>You are not alone, and support is available.</b>", BODY))
story.append(Spacer(1, 5))

mh_items = [
    "<b>Talk to your doctor:</b> If you feel low, anxious, or overwhelmed, tell your GP or specialist. Mental health support is part of PCOS care.",
    "<b>Connect with others:</b> PCOS support groups (online and in person) can be enormously helpful.",
    "<b>Cognitive Behavioural Therapy (CBT):</b> Evidence-based talking therapy that helps with anxiety and depression.",
    "<b>Body-positive thinking:</b> Focus on health and how you feel, not just appearance.",
    "<b>Regular exercise:</b> One of the best evidence-based treatments for mild to moderate depression and anxiety.",
]
story.append(bullet_table(mh_items, color=C_PURPLE, bg=C_PURPLE_LT))
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 9 – QUESTIONS TO ASK YOUR DOCTOR
# ══════════════════════════════════════════════════════════════
story.append(sec("💬", "Questions to Ask Your Doctor", C_TEAL))
story.append(Spacer(1, 5))

qa_pairs = [
    ("Which PCOS features do I have? What is my phenotype?",
     "Understanding your phenotype helps personalise your treatment."),
    ("Do I have insulin resistance? Should I take metformin?",
     "Metformin is very helpful for some women but not all — depends on your results."),
    ("What blood tests do I need and when should I repeat them?",
     "Ongoing monitoring is important for metabolic health."),
    ("What are my fertility options?",
     "Even if you're not trying to get pregnant now, it's good to plan ahead."),
    ("Should I be referred to a specialist (endocrinologist, gynaecologist, dietitian)?",
     "PCOS is best managed by a team when possible."),
    ("Are there any clinical trials I could join?",
     "New treatments for PCOS are being studied — ask if any are available near you."),
]

for q, a in qa_pairs:
    row = [[Paragraph(f"Q: {q}", Q), Paragraph(f"A: {a}", ANS)]]
    t = Table(row, colWidths=[W*0.46, W*0.54])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(0,-1), C_TEAL_LT),
        ("BACKGROUND",(1,0),(1,-1), C_WHITE),
        ("BOX",(0,0),(-1,-1),0.6, C_GREY_LINE),
        ("LINEAFTER",(0,0),(0,-1),0.6, C_TEAL),
        ("TOPPADDING",(0,0),(-1,-1),6),
        ("BOTTOMPADDING",(0,0),(-1,-1),6),
        ("LEFTPADDING",(0,0),(-1,-1),7),
        ("RIGHTPADDING",(0,0),(-1,-1),7),
        ("VALIGN",(0,0),(-1,-1),"TOP"),
    ]))
    story.append(t)
    story.append(Spacer(1, 3))

story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# SECTION 10 – KEY FACTS AT A GLANCE
# ══════════════════════════════════════════════════════════════
story.append(sec("⭐", "Key Facts at a Glance", C_ROSE))
story.append(Spacer(1, 5))

facts = [
    ("1 in 10", "women of reproductive age have PCOS worldwide"),
    ("2 of 3", "Rotterdam criteria needed to diagnose PCOS"),
    ("50–70%", "of women with PCOS have insulin resistance"),
    ("5–10%", "weight loss can restore periods and ovulation"),
    ("70%", "of PCOS women in the US have hirsutism"),
    ("2–6×", "increased risk of endometrial cancer if periods are very infrequent"),
]
fact_col = W / 3 - 0.2*cm
fact_rows_top = facts[:3]
fact_rows_bot = facts[3:]

def fact_cell(num, desc):
    t = Table([
        [Paragraph(f'<font color="#{C_ROSE.hexval()[2:]}"><b>{num}</b></font>', ps("FN", fontSize=20, fontName="Helvetica-Bold", alignment=TA_CENTER, leading=24))],
        [Paragraph(desc, ps("FD", fontSize=9, fontName="Helvetica", alignment=TA_CENTER, leading=13, textColor=C_DARK_GREY))],
    ], colWidths=[fact_col])
    t.setStyle(TableStyle([
        ("BACKGROUND",(0,0),(-1,-1), C_ROSE_LT),
        ("BOX",(0,0),(-1,-1),0.8, C_ROSE),
        ("TOPPADDING",(0,0),(-1,-1),6),
        ("BOTTOMPADDING",(0,0),(-1,-1),6),
    ]))
    return t

row1 = [[fact_cell(n, d) for n, d in fact_rows_top]]
row2 = [[fact_cell(n, d) for n, d in fact_rows_bot]]
gap = 0.3*cm
fact_tbl1 = Table(row1, colWidths=[fact_col, fact_col, fact_col],
                  rowHeights=None)
fact_tbl1.setStyle(TableStyle([
    ("LEFTPADDING",(0,0),(-1,-1),2),
    ("RIGHTPADDING",(0,0),(-1,-1),2),
    ("TOPPADDING",(0,0),(-1,-1),0),
    ("BOTTOMPADDING",(0,0),(-1,-1),0),
]))
fact_tbl2 = Table(row2, colWidths=[fact_col, fact_col, fact_col])
fact_tbl2.setStyle(TableStyle([
    ("LEFTPADDING",(0,0),(-1,-1),2),
    ("RIGHTPADDING",(0,0),(-1,-1),2),
    ("TOPPADDING",(0,0),(-1,-1),0),
    ("BOTTOMPADDING",(0,0),(-1,-1),0),
]))
story.append(fact_tbl1)
story.append(Spacer(1, 4))
story.append(fact_tbl2)
story.append(Spacer(1, 8))

# ══════════════════════════════════════════════════════════════
# FOOTER
# ══════════════════════════════════════════════════════════════
story.append(HRFlowable(width=W, thickness=0.6, color=C_GREY_LINE))
story.append(Spacer(1, 4))
story.append(Paragraph(
    "This handout is for educational purposes only and does not replace advice from your doctor or healthcare team. "
    "Information based on: Rotterdam Criteria 2023 · Harrison's Principles of Internal Medicine 22E · "
    "Berek &amp; Novak's Gynecology · Henry's Clinical Diagnosis and Management by Laboratory Methods.",
    SMALL))
story.append(Spacer(1, 3))
story.append(Paragraph(
    "If you have concerns about your health, please speak to your GP, gynaecologist, or endocrinologist.",
    SMALL))

doc.build(story)
print(f"PDF saved: {OUTPUT}")
Running Command

cd /home/daytona/workspace/pcos-pdf && python generate_pcos_patient_handout.py 2>&1

Running Command

ls -lh /home/daytona/workspace/pcos-pdf/PCOS_Patient_Handout.pdf

Your patient-friendly handout is ready:

PCOS Patient Handout

PDF Document · PDF


Here is what is inside across 10 sections, all written in plain language a patient can understand:
SectionContent
1. What Is PCOS?Simple explanation, prevalence (1 in 10 women), reassurance that it is not the patient's fault
2. Common SymptomsTable of 8 symptoms with plain-English explanations (irregular periods, hirsutism, acne, hair loss, weight gain, fatigue, mood, fertility)
3. How Is PCOS Diagnosed?Rotterdam 2023 criteria explained in lay terms - 2 of 3 features required, colour-coded boxes
4. Blood Tests Explained10 tests (LH, FSH, Testosterone, AMH, 17-OH Progesterone, Prolactin, TSH, Fasting glucose/HbA1c, Insulin/HOMA-IR, DHEA-S, Lipids) - each with a plain-language "what it checks and why" explanation. Timing tip box included.
5. Long-Term Health RisksTable: diabetes, cardiovascular disease, endometrial cancer, sleep apnoea, mental health, fertility - with risk reduction strategies
6. Treatment OptionsLifestyle changes (first-line), plus medication table: OCP, Metformin, anti-androgens, Clomifene/Letrozole, FSH injections, Inositol, Eflornithine
7. PCOS & FertilityReassuring bullets: most women can conceive, letrozole is first-line, IVF options, OHSS risk, pregnancy monitoring
8. Mental HealthCBT, support groups, exercise, body-positive framing
9. Questions to Ask Your Doctor6 ready-to-use Q&A pairs patients can bring to appointments
10. Key Facts at a Glance6 visual statistic boxes (1 in 10, 2 of 3, 50-70%, 5-10%, 70%, 2-6×)
This is a shared conversation. Sign in to Orris to start your own chat.