I have a seminar of a 5 minutes on the topic human control of menstrual cycle so provide a important informations
Hypothalamus → Anterior Pituitary → Ovaries → Uterus
GnRH → FSH + LH → E2 + P4 → Endometrium
| Signal | Effect on Pituitary/Hypothalamus |
|---|---|
| Rising E2 (follicular phase) | Negative feedback → suppresses FSH/LH |
| High E2 (pre-ovulatory) | Positive feedback → triggers LH surge |
| Progesterone (luteal phase) | Negative feedback → slows GnRH pulse frequency |
| Inhibin B (from granulosa) | Specifically suppresses FSH secretion |

| Day | Phase | Dominant Hormone | Endometrium | Key Event |
|---|---|---|---|---|
| 1-4 | Menstruation | Low E2, Low P4 | Shedding | Bleeding |
| 5-13 | Proliferative | Rising E2 | Thickens | Follicle grows |
| ~14 | Ovulation | LH surge | Peak thickness | Egg released |
| 15-28 | Secretory | Progesterone | Secretory glands | Corpus luteum active |
| 26-28 | Late luteal | E2 + P4 fall | Breakdown | Corpus luteum dies |
Make pdf of it and flowchart also
ls /home/daytona/workspace/ 2>/dev/null && echo "---" && mkdir -p /home/daytona/workspace/menstrual-cycle
~/menstrual-cycle/create_flowchart.py
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
import matplotlib.patheffects as pe
fig, ax = plt.subplots(1, 1, figsize=(14, 20))
ax.set_xlim(0, 14)
ax.set_ylim(0, 20)
ax.axis('off')
fig.patch.set_facecolor('#F8F4FF')
ax.set_facecolor('#F8F4FF')
# --- Helper functions ---
def draw_box(ax, x, y, w, h, text, facecolor, edgecolor, fontsize=11, bold=False, text_color='white', radius=0.4):
box = FancyBboxPatch((x - w/2, y - h/2), w, h,
boxstyle=f"round,pad=0.1,rounding_size={radius}",
linewidth=2, edgecolor=edgecolor, facecolor=facecolor, zorder=3)
ax.add_patch(box)
weight = 'bold' if bold else 'normal'
ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
color=text_color, fontweight=weight, zorder=4, wrap=True,
multialignment='center')
def draw_arrow(ax, x1, y1, x2, y2, color='#444444', label='', label_side='right'):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color, lw=2.2), zorder=2)
if label:
mx = (x1 + x2) / 2
my = (y1 + y2) / 2
offset = 0.35 if label_side == 'right' else -0.35
ax.text(mx + offset, my, label, ha='center', va='center', fontsize=8.5,
color=color, fontstyle='italic', zorder=5)
def draw_curved_arrow(ax, x1, y1, x2, y2, color, label=''):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color, lw=2,
connectionstyle='arc3,rad=0.35'), zorder=2)
if label:
mx = (x1 + x2) / 2 + 0.6
my = (y1 + y2) / 2
ax.text(mx, my, label, ha='center', va='center', fontsize=8.5, color=color, fontstyle='italic')
# =====================
# TITLE
# =====================
ax.text(7, 19.3, 'HUMAN CONTROL OF THE MENSTRUAL CYCLE', ha='center', va='center',
fontsize=16, fontweight='bold', color='#2C0057',
bbox=dict(boxstyle='round,pad=0.4', facecolor='#D0B3FF', edgecolor='#6A0DAD', linewidth=2))
ax.text(7, 18.6, 'Hypothalamic–Pituitary–Ovarian–Uterine Axis', ha='center', va='center',
fontsize=12, color='#5A005A', fontstyle='italic')
# =====================
# AXIS BOXES (main vertical flow)
# =====================
# 1. Hypothalamus
draw_box(ax, 7, 17.5, 4.5, 0.9, 'HYPOTHALAMUS\n(Arcuate Nucleus)',
facecolor='#6A0DAD', edgecolor='#3D007A', fontsize=12, bold=True)
# Arrow down
draw_arrow(ax, 7, 17.05, 7, 16.2, color='#333333', label='GnRH pulses\n(every 60–120 min)', label_side='right')
# 2. Anterior Pituitary
draw_box(ax, 7, 15.8, 4.5, 0.9, 'ANTERIOR PITUITARY\n(Gonadotrophs)',
facecolor='#0066CC', edgecolor='#003D80', fontsize=12, bold=True)
# Arrow down — splits into FSH and LH
draw_arrow(ax, 7, 15.35, 7, 14.6, color='#333333')
ax.text(7.35, 14.98, 'FSH + LH', ha='left', va='center', fontsize=9, color='#333333', fontstyle='italic')
# 3a. FSH box (left branch)
draw_box(ax, 4.2, 14.1, 3.0, 0.9, 'FSH\n(Follicle-Stimulating\nHormone)',
facecolor='#0099AA', edgecolor='#006677', fontsize=9.5, bold=True)
# 3b. LH box (right branch)
draw_box(ax, 9.8, 14.1, 3.0, 0.9, 'LH\n(Luteinizing\nHormone)',
facecolor='#CC5500', edgecolor='#993300', fontsize=9.5, bold=True)
# Branches from pituitary
ax.annotate('', xy=(4.2, 14.55), xytext=(6.5, 15.35),
arrowprops=dict(arrowstyle='->', color='#0099AA', lw=2), zorder=2)
ax.annotate('', xy=(9.8, 14.55), xytext=(7.5, 15.35),
arrowprops=dict(arrowstyle='->', color='#CC5500', lw=2), zorder=2)
# =====================
# OVARIAN SECTION
# =====================
ax.text(7, 13.35, '▼ OVARY ▼', ha='center', va='center', fontsize=11, color='#990066', fontweight='bold')
# FSH → Follicle growth
draw_arrow(ax, 4.2, 13.65, 4.2, 12.8, color='#0099AA', label='Follicle\ngrowth', label_side='right')
draw_box(ax, 4.2, 12.4, 3.4, 0.9, 'FOLLICULAR PHASE\n(Days 1–14)\nDominant follicle grows',
facecolor='#009966', edgecolor='#005C3D', fontsize=9, bold=False)
# Follicle → Estrogen
draw_arrow(ax, 4.2, 11.95, 4.2, 11.2, color='#CC0066', label='Secretes\nEstradiol (E2)', label_side='right')
draw_box(ax, 4.2, 10.8, 3.2, 0.7, 'Rising ESTROGEN (E2)\n(Granulosa cells aromatize)',
facecolor='#CC0066', edgecolor='#880044', fontsize=9)
# LH surge box
draw_box(ax, 9.8, 12.4, 3.2, 0.9, 'LH SURGE\n(~Day 14)\nPositive feedback!',
facecolor='#FF6600', edgecolor='#CC3300', fontsize=9, bold=True)
draw_arrow(ax, 9.8, 13.65, 9.8, 12.85, color='#CC5500')
# Estrogen → positive feedback → LH surge
draw_curved_arrow(ax, 5.8, 10.8, 8.5, 12.4, color='#FF0000',
label='Positive\nfeedback\n(E2 >200 pg/mL)')
# LH surge → Ovulation
draw_arrow(ax, 9.8, 11.95, 9.8, 11.2, color='#FF6600', label='~9 h after\nLH peak', label_side='right')
draw_box(ax, 9.8, 10.8, 3.2, 0.7, 'OVULATION\n(~Day 14)',
facecolor='#FF4444', edgecolor='#CC0000', fontsize=10, bold=True)
# Connecting line — both phases lead to central arrow
ax.annotate('', xy=(7, 9.8), xytext=(4.2, 10.45),
arrowprops=dict(arrowstyle='->', color='#555555', lw=1.8), zorder=2)
ax.annotate('', xy=(7, 9.8), xytext=(9.8, 10.45),
arrowprops=dict(arrowstyle='->', color='#555555', lw=1.8), zorder=2)
# Corpus Luteum
draw_box(ax, 7, 9.4, 4.2, 0.7, 'CORPUS LUTEUM forms\n(LH-dependent)',
facecolor='#8B4513', edgecolor='#5C2A00', fontsize=9.5, bold=True)
draw_arrow(ax, 7, 9.05, 7, 8.3, color='#333333', label='Secretes E2\n+ Progesterone', label_side='right')
# =====================
# LUTEAL PHASE
# =====================
draw_box(ax, 7, 7.9, 4.5, 0.9, 'LUTEAL PHASE (Days 15–28)\nProgesterone dominates',
facecolor='#6B4FA0', edgecolor='#3D2070', fontsize=9.5, bold=True)
# Negative feedback arrow (curved, back to pituitary)
draw_curved_arrow(ax, 9.2, 7.9, 10.5, 15.35, color='#AA0000',
label='Negative\nfeedback\n(P4 + E2)')
# =====================
# UTERINE CYCLE
# =====================
ax.text(7, 7.1, '▼ UTERUS (ENDOMETRIUM) ▼', ha='center', va='center',
fontsize=10.5, color='#660033', fontweight='bold')
# Two uterine outcomes
draw_box(ax, 3.5, 6.1, 3.8, 0.9, 'PROLIFERATIVE\n(Days 5–14)\nE2: Endometrium thickens',
facecolor='#009966', edgecolor='#005C3D', fontsize=9)
draw_box(ax, 10.5, 6.1, 3.8, 0.9, 'SECRETORY\n(Days 15–28)\nP4: Glands tortuous, edema',
facecolor='#6B4FA0', edgecolor='#3D2070', fontsize=9)
ax.annotate('', xy=(3.5, 6.55), xytext=(5.5, 7.45),
arrowprops=dict(arrowstyle='->', color='#009966', lw=1.8))
ax.annotate('', xy=(10.5, 6.55), xytext=(8.5, 7.45),
arrowprops=dict(arrowstyle='->', color='#6B4FA0', lw=1.8))
# =====================
# TWO OUTCOMES
# =====================
ax.text(7, 5.3, 'If no fertilization:', ha='center', va='center', fontsize=10, color='#333333', fontstyle='italic')
draw_box(ax, 3.5, 4.5, 3.8, 0.9, 'Corpus luteum degenerates\n→ E2 & P4 fall sharply',
facecolor='#888888', edgecolor='#444444', fontsize=9)
draw_box(ax, 10.5, 4.5, 3.8, 0.9, 'Spiral arteries spasm\n→ MENSTRUATION (Day 1)',
facecolor='#CC0033', edgecolor='#880022', fontsize=9)
ax.annotate('', xy=(3.5, 4.95), xytext=(3.5, 5.65),
arrowprops=dict(arrowstyle='->', color='#888888', lw=1.8))
draw_arrow(ax, 5.4, 4.5, 8.6, 4.5, color='#CC0033')
# If fertilization
ax.text(7, 3.7, 'If fertilization occurs:', ha='center', va='center', fontsize=10, color='#006633', fontstyle='italic')
draw_box(ax, 7, 3.1, 5.5, 0.9,
'Embryo → hCG rescues corpus luteum\n→ Progesterone maintained → No menstruation',
facecolor='#006633', edgecolor='#004422', fontsize=9)
# Cycle repeats arrow
ax.annotate('', xy=(0.8, 17.5), xytext=(0.8, 4.5),
arrowprops=dict(arrowstyle='->', color='#999999', lw=2,
connectionstyle='arc3,rad=0'), zorder=1)
ax.plot([0.8, 2.5], [4.5, 4.5], color='#999999', lw=2, zorder=1)
ax.plot([0.8, 2.5], [17.5, 17.5], color='#999999', lw=2, zorder=1)
ax.text(0.35, 11, 'CYCLE\nREPEATS', ha='center', va='center', fontsize=8, color='#999999',
fontweight='bold', rotation=90)
# =====================
# LEGEND
# =====================
legend_y = 2.4
ax.text(7, legend_y, 'KEY: Red arrows = Positive feedback | Dark red curved = Negative feedback | Phases: Follicular (green) · Luteal (purple)',
ha='center', va='center', fontsize=8, color='#444444',
bbox=dict(boxstyle='round,pad=0.3', facecolor='#EEE8FF', edgecolor='#AAAACC', lw=1))
# Day ruler at bottom
days = [1, 5, 14, 15, 24, 28]
labels = ['Day 1\nMenses\nbegins', 'Day 5\nEndometrium\nregrows', 'Day 14\nOvulation\n(LH surge)',
'Day 15\nLuteal\nphase', 'Day 24\nCorpus\nluteum dies', 'Day 28\nNext\ncycle']
ax.text(7, 1.9, 'TIMELINE (28-day cycle)', ha='center', fontsize=9, fontweight='bold', color='#333333')
for i, (d, lbl) in enumerate(zip(days, labels)):
xp = 1.5 + i * 2.05
ax.plot(xp, 1.5, 'o', markersize=10, color='#6A0DAD', zorder=5)
ax.text(xp, 1.5, str(d), ha='center', va='center', fontsize=7, color='white', fontweight='bold', zorder=6)
ax.text(xp, 0.8, lbl, ha='center', va='center', fontsize=6.5, color='#333333')
# connect dots
xs = [1.5 + i*2.05 for i in range(6)]
ax.plot(xs, [1.5]*6, '-', color='#AAAAAA', lw=1.5, zorder=2)
plt.tight_layout(pad=0.5)
plt.savefig('/home/daytona/workspace/menstrual-cycle/flowchart.png', dpi=180, bbox_inches='tight',
facecolor='#F8F4FF')
print("Flowchart saved!")
cd /home/daytona/workspace/menstrual-cycle && python create_flowchart.py 2>&1
~/menstrual-cycle/create_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table,
TableStyle, Image, HRFlowable, PageBreak, KeepTogether)
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus.flowables import HRFlowable
import os
OUTPUT = '/home/daytona/workspace/menstrual-cycle/Menstrual_Cycle_Seminar.pdf'
FLOWCHART = '/home/daytona/workspace/menstrual-cycle/flowchart.png'
# ─── Colors ───
PURPLE_DARK = colors.HexColor('#2C0057')
PURPLE_MED = colors.HexColor('#6A0DAD')
PURPLE_LIGHT = colors.HexColor('#EEE8FF')
BLUE = colors.HexColor('#0066CC')
TEAL = colors.HexColor('#0099AA')
GREEN = colors.HexColor('#009966')
ORANGE = colors.HexColor('#CC5500')
CRIMSON = colors.HexColor('#CC0033')
BROWN = colors.HexColor('#8B4513')
GREY_LIGHT = colors.HexColor('#F5F5F5')
GREY_MED = colors.HexColor('#DDDDDD')
WHITE = colors.white
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=2*cm, leftMargin=2*cm,
topMargin=2*cm, bottomMargin=2*cm,
title='Human Control of the Menstrual Cycle',
author='Seminar Notes'
)
W, H = A4
styles = getSampleStyleSheet()
# ─── Custom Styles ───
def S(name, **kw):
base = kw.pop('parent', 'Normal')
s = ParagraphStyle(name, parent=styles[base], **kw)
return s
title_style = S('MyTitle', parent='Title', fontSize=22, textColor=PURPLE_DARK,
alignment=TA_CENTER, spaceAfter=4, leading=28, fontName='Helvetica-Bold')
subtitle_style = S('MySubtitle', fontSize=13, textColor=PURPLE_MED,
alignment=TA_CENTER, spaceAfter=8, fontName='Helvetica-Oblique')
h1_style = S('H1', fontSize=14, textColor=WHITE, fontName='Helvetica-Bold',
backColor=PURPLE_MED, borderPad=6, leading=18,
spaceAfter=6, spaceBefore=14, alignment=TA_LEFT)
h2_style = S('H2', fontSize=12, textColor=PURPLE_DARK, fontName='Helvetica-Bold',
spaceBefore=8, spaceAfter=4, borderLeftPadding=6,
borderLeftColor=PURPLE_MED, borderLeftWidth=3)
body_style = S('Body', fontSize=10.5, textColor=colors.HexColor('#222222'),
leading=15, spaceAfter=4, alignment=TA_JUSTIFY)
bullet_style = S('Bullet', fontSize=10.5, textColor=colors.HexColor('#222222'),
leading=14, spaceAfter=3, leftIndent=16, firstLineIndent=-10)
note_style = S('Note', fontSize=9, textColor=colors.HexColor('#555555'),
fontName='Helvetica-Oblique', alignment=TA_CENTER, spaceAfter=6)
tag_style = S('Tag', fontSize=9.5, textColor=WHITE, fontName='Helvetica-Bold',
alignment=TA_CENTER, backColor=PURPLE_MED, borderPad=4)
def section_header(text, color=PURPLE_MED):
return Table(
[[Paragraph(text, S('sh', fontSize=13, textColor=WHITE, fontName='Helvetica-Bold',
alignment=TA_LEFT, leading=18))]],
colWidths=[W - 4*cm],
style=TableStyle([
('BACKGROUND', (0,0), (-1,-1), color),
('ROUNDEDCORNERS', [6]),
('TOPPADDING', (0,0), (-1,-1), 7),
('BOTTOMPADDING', (0,0), (-1,-1), 7),
('LEFTPADDING', (0,0), (-1,-1), 12),
])
)
def info_box(text, bg=PURPLE_LIGHT, border=PURPLE_MED):
return Table(
[[Paragraph(text, S('ib', fontSize=10.5, textColor=PURPLE_DARK,
leading=15, alignment=TA_LEFT))]],
colWidths=[W - 4*cm],
style=TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('BOX', (0,0), (-1,-1), 1.5, border),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
('LEFTPADDING', (0,0), (-1,-1), 12),
('RIGHTPADDING', (0,0), (-1,-1), 12),
])
)
def bullet(text, color=PURPLE_MED):
return Paragraph(f'<font color="#{color.hexval()[2:]}">●</font> {text}', bullet_style)
story = []
# ═══════════════════════════════════════════════
# PAGE 1 — TITLE + INTRO + AXIS
# ═══════════════════════════════════════════════
# Title block
story.append(Spacer(1, 0.3*cm))
title_tbl = Table(
[[Paragraph('HUMAN CONTROL OF THE<br/>MENSTRUAL CYCLE', title_style)]],
colWidths=[W - 4*cm],
style=TableStyle([
('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#D0B3FF')),
('BOX', (0,0), (-1,-1), 2, PURPLE_DARK),
('TOPPADDING', (0,0), (-1,-1), 16),
('BOTTOMPADDING', (0,0), (-1,-1), 16),
('LEFTPADDING', (0,0), (-1,-1), 16),
('RIGHTPADDING', (0,0), (-1,-1), 16),
])
)
story.append(title_tbl)
story.append(Spacer(1, 0.15*cm))
story.append(Paragraph('5-Minute Seminar Notes | Human Physiology', subtitle_style))
story.append(Paragraph('Sources: Costanzo Physiology 7e · Ganong\'s Physiology 26e · Berek & Novak\'s Gynecology · Harrison\'s 22e (2025)', note_style))
story.append(HRFlowable(width='100%', thickness=1.5, color=PURPLE_MED, spaceAfter=10))
# 1. INTRODUCTION
story.append(section_header('1. INTRODUCTION'))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
'The menstrual cycle is a precisely coordinated hormonal axis involving four key '
'structures: the <b>hypothalamus, anterior pituitary, ovaries,</b> and <b>uterus (endometrium)</b>. '
'Its primary purpose is to prepare the female reproductive system for fertilization and pregnancy each month. '
'On average the cycle spans <b>28 days</b>, though 21–35 days is considered normal. '
'<b>Day 1</b> is defined as the first day of menstrual bleeding.', body_style))
story.append(Spacer(1, 0.2*cm))
story.append(info_box(
'<b>The 4-component axis:</b> Hypothalamus (GnRH) → Anterior Pituitary (FSH + LH) '
'→ Ovaries (Estrogen + Progesterone) → Uterus (Endometrium cyclically prepared)',
bg=colors.HexColor('#EEE8FF'), border=PURPLE_MED
))
story.append(Spacer(1, 0.3*cm))
# 2. HORMONAL AXIS
story.append(section_header('2. THE HORMONAL AXIS — WHO CONTROLS WHAT?', color=BLUE))
story.append(Spacer(1, 0.2*cm))
hormones_data = [
[Paragraph('<b>Hormone</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER)),
Paragraph('<b>Source</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER)),
Paragraph('<b>Key Role</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER))],
[Paragraph('<b>GnRH</b>', S('tc', fontSize=10, fontName='Helvetica-Bold', textColor=PURPLE_DARK)),
Paragraph('Hypothalamus (arcuate nucleus)', S('tc', fontSize=9.5)),
Paragraph('Pulsatile stimulation of FSH & LH; must be pulsatile to work', S('tc', fontSize=9.5))],
[Paragraph('<b>FSH</b>', S('tc', fontSize=10, fontName='Helvetica-Bold', textColor=BLUE)),
Paragraph('Anterior pituitary (gonadotrophs)', S('tc', fontSize=9.5)),
Paragraph('Follicle growth; stimulates estrogen production by granulosa cells', S('tc', fontSize=9.5))],
[Paragraph('<b>LH</b>', S('tc', fontSize=10, fontName='Helvetica-Bold', textColor=ORANGE)),
Paragraph('Anterior pituitary (gonadotrophs)', S('tc', fontSize=9.5)),
Paragraph('Triggers ovulation (LH surge); forms & maintains corpus luteum', S('tc', fontSize=9.5))],
[Paragraph('<b>Estradiol (E2)</b>', S('tc', fontSize=10, fontName='Helvetica-Bold', textColor=CRIMSON)),
Paragraph('Granulosa cells (ovary)', S('tc', fontSize=9.5)),
Paragraph('Endometrial proliferation; cervical mucus thinning; positive feedback for LH surge', S('tc', fontSize=9.5))],
[Paragraph('<b>Progesterone (P4)</b>', S('tc', fontSize=10, fontName='Helvetica-Bold', textColor=BROWN)),
Paragraph('Corpus luteum (ovary)', S('tc', fontSize=9.5)),
Paragraph('Secretory endometrium; negative feedback on GnRH/LH; thickens cervical mucus', S('tc', fontSize=9.5))],
[Paragraph('<b>Inhibin B</b>', S('tc', fontSize=10, fontName='Helvetica-Bold', textColor=TEAL)),
Paragraph('Granulosa cells (ovary)', S('tc', fontSize=9.5)),
Paragraph('Selectively suppresses FSH secretion', S('tc', fontSize=9.5))],
]
hormone_tbl = Table(hormones_data, colWidths=[3.5*cm, 4.5*cm, 8.3*cm])
hormone_tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), BLUE),
('BACKGROUND', (0,1), (-1,1), colors.HexColor('#EDF5FF')),
('BACKGROUND', (0,2), (-1,2), GREY_LIGHT),
('BACKGROUND', (0,3), (-1,3), colors.HexColor('#EDF5FF')),
('BACKGROUND', (0,4), (-1,4), GREY_LIGHT),
('BACKGROUND', (0,5), (-1,5), colors.HexColor('#EDF5FF')),
('BACKGROUND', (0,6), (-1,6), GREY_LIGHT),
('BOX', (0,0), (-1,-1), 1.2, BLUE),
('INNERGRID', (0,0), (-1,-1), 0.5, GREY_MED),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 8),
('RIGHTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))
story.append(hormone_tbl)
story.append(Spacer(1, 0.3*cm))
# ═══════════════════════════════════════════════
# PAGE 2 — PHASES
# ═══════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header('3. PHASE 1 — FOLLICULAR / PROLIFERATIVE PHASE (Days 1–14)', color=GREEN))
story.append(Spacer(1, 0.15*cm))
story.append(bullet('Low FSH & LH at start; hypothalamus increases GnRH pulse frequency → FSH rises.'))
story.append(bullet('Several primordial follicles begin growth; by day 6, <b>one dominant follicle</b> is selected '
'based on its superior estrogen-secreting capacity.'))
story.append(bullet('<b>Two-cell, two-gonadotropin model:</b> Theca interna cells (LH-driven) produce androgens → '
'aromatized to <b>estradiol</b> by granulosa cells (FSH-driven).'))
story.append(bullet('Rising <b>estradiol</b> causes endometrial <b>proliferation</b> (Days 5–14): glands elongate, '
'spiral arteries grow, lining thickens.'))
story.append(bullet('Estradiol makes cervical mucus <b>thin, watery, elastic</b> — the "ferning" pattern — '
'creating channels for sperm.'))
story.append(bullet('For most of this phase, rising E2 exerts <b>negative feedback</b> on the pituitary.'))
story.append(Spacer(1, 0.25*cm))
story.append(section_header('4. THE LH SURGE AND OVULATION (~Day 14)', color=colors.HexColor('#CC5500')))
story.append(Spacer(1, 0.15*cm))
story.append(info_box(
'<b>Key concept — Switch from negative to positive feedback:</b><br/>'
'When E2 exceeds ~200 pg/mL for ≥48 hours, the estrogen feedback switches from '
'inhibitory to stimulatory. This triggers a massive LH surge from the anterior pituitary.',
bg=colors.HexColor('#FFF0E0'), border=ORANGE
))
story.append(Spacer(1, 0.15*cm))
story.append(bullet('The LH surge initiates: (1) completion of meiosis I in the oocyte, '
'(2) follicle rupture, (3) release of the secondary oocyte.'))
story.append(bullet('<b>Ovulation occurs ~9 hours after the LH peak.</b>'))
story.append(bullet('Minor follicular bleeding into the abdominal cavity can cause brief mid-cycle pain — '
'called <b>mittelschmerz</b>.'))
story.append(Spacer(1, 0.25*cm))
story.append(section_header('5. PHASE 2 — LUTEAL / SECRETORY PHASE (Days 15–28)', color=colors.HexColor('#6B4FA0')))
story.append(Spacer(1, 0.15*cm))
story.append(bullet('The ruptured follicle transforms into the <b>corpus luteum</b> (LH-dependent), '
'which secretes both <b>estrogen and progesterone</b>.'))
story.append(bullet('<b>Progesterone dominates</b> and transforms the endometrium into secretory phase: '
'glands become coiled/tortuous, fill with glycogen, stroma becomes edematous, '
'spiral arteries elongate and coil.'))
story.append(bullet('Cervical mucus becomes <b>thick and non-penetrable</b> (sperm-hostile) — '
'fertilization window has passed.'))
story.append(bullet('Progesterone + E2 exert <b>negative feedback</b> on the hypothalamus and pituitary, '
'suppressing further FSH/LH secretion.'))
story.append(Spacer(1, 0.2*cm))
# Two outcomes
outcomes_data = [
[Paragraph('<b>If NO Fertilization</b>', S('ot', fontSize=11, textColor=WHITE, fontName='Helvetica-Bold', alignment=TA_CENTER)),
Paragraph('<b>If Fertilization OCCURS</b>', S('ot', fontSize=11, textColor=WHITE, fontName='Helvetica-Bold', alignment=TA_CENTER))],
[Paragraph('Corpus luteum degenerates (~Day 24)<br/>'
'→ E2 & P4 fall sharply<br/>'
'→ Spiral arteries spasm<br/>'
'→ Endometrium sloughs → <b>Menstruation</b><br/>'
'→ Cycle repeats from Day 1',
S('oc', fontSize=10, leading=15, textColor=colors.HexColor('#333333'))),
Paragraph('Embryo implants → trophoblast secretes <b>hCG</b><br/>'
'→ hCG rescues the corpus luteum<br/>'
'→ Progesterone maintained<br/>'
'→ No menstruation → Pregnancy continues',
S('oc', fontSize=10, leading=15, textColor=colors.HexColor('#333333')))]
]
outcomes_tbl = Table(outcomes_data, colWidths=[8.15*cm, 8.15*cm])
outcomes_tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (0,0), CRIMSON),
('BACKGROUND', (1,0), (1,0), GREEN),
('BACKGROUND', (0,1), (0,1), colors.HexColor('#FFF0F0')),
('BACKGROUND', (1,1), (1,1), colors.HexColor('#F0FFF5')),
('BOX', (0,0), (-1,-1), 1.5, GREY_MED),
('INNERGRID', (0,0), (-1,-1), 0.8, GREY_MED),
('TOPPADDING', (0,0), (-1,-1), 9),
('BOTTOMPADDING', (0,0), (-1,-1), 9),
('LEFTPADDING', (0,0), (-1,-1), 10),
('RIGHTPADDING', (0,0), (-1,-1), 10),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(outcomes_tbl)
story.append(Spacer(1, 0.3*cm))
# ═══════════════════════════════════════════════
# PAGE 3 — FEEDBACK + CLINICAL + TIMELINE TABLE
# ═══════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header('6. FEEDBACK CONTROL SUMMARY', color=CRIMSON))
story.append(Spacer(1, 0.15*cm))
fb_data = [
[Paragraph('<b>Signal</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10.5, alignment=TA_CENTER)),
Paragraph('<b>Type</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10.5, alignment=TA_CENTER)),
Paragraph('<b>Effect</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10.5, alignment=TA_CENTER))],
['Rising E2 (follicular phase)', 'Negative', 'Suppresses FSH & LH → prevents premature LH surge'],
['High E2 (>200 pg/mL, ≥48 h)', 'POSITIVE', 'Triggers LH surge → ovulation'],
['Progesterone (luteal phase)', 'Negative', 'Slows GnRH pulse frequency → suppresses FSH/LH'],
['Inhibin B (granulosa)', 'Negative', 'Selectively suppresses FSH only'],
]
for i in range(1, len(fb_data)):
for j in range(len(fb_data[i])):
fb_data[i][j] = Paragraph(fb_data[i][j], S('td', fontSize=10, leading=14))
fb_tbl = Table(fb_data, colWidths=[5.5*cm, 2.8*cm, 8.0*cm])
fb_tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), CRIMSON),
('BACKGROUND', (0,2), (-1,2), GREY_LIGHT),
('BACKGROUND', (0,4), (-1,4), GREY_LIGHT),
('BOX', (0,0), (-1,-1), 1.2, CRIMSON),
('INNERGRID', (0,0), (-1,-1), 0.5, GREY_MED),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('TEXTCOLOR', (1,2), (1,2), colors.HexColor('#CC0000')), # POSITIVE in red
]))
story.append(fb_tbl)
story.append(Spacer(1, 0.3*cm))
# 7. CLINICAL APPLICATIONS
story.append(section_header('7. CLINICAL RELEVANCE', color=TEAL))
story.append(Spacer(1, 0.15*cm))
clin_data = [
[Paragraph('<b>Application</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER)),
Paragraph('<b>Mechanism</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER))],
[Paragraph('<b>Combined oral contraceptives</b>', S('tc2', fontSize=9.5, fontName='Helvetica-Bold')),
Paragraph('Steady estrogen + progestin mimic luteal phase → suppress GnRH pulses → no LH surge → no ovulation', S('tc2', fontSize=9.5))],
[Paragraph('<b>Clomiphene citrate</b>\n(fertility drug)', S('tc2', fontSize=9.5, fontName='Helvetica-Bold')),
Paragraph('Blocks estrogen receptors in hypothalamus → prevents negative feedback → FSH/LH rise → stimulates follicular growth', S('tc2', fontSize=9.5))],
[Paragraph('<b>GnRH agonists</b>\n(e.g., leuprolide)', S('tc2', fontSize=9.5, fontName='Helvetica-Bold')),
Paragraph('Continuous GnRH → receptor downregulation → shuts down entire axis. Used in endometriosis, IVF protocols', S('tc2', fontSize=9.5))],
[Paragraph('<b>Hypothalamic amenorrhea</b>', S('tc2', fontSize=9.5, fontName='Helvetica-Bold')),
Paragraph('Stress, extreme weight loss, or over-exercise disrupts pulsatile GnRH → loss of FSH/LH → no ovulation, no periods', S('tc2', fontSize=9.5))],
]
for i in range(1, len(clin_data)):
for j in range(len(clin_data[i])):
clin_data[i][j] = Paragraph(str(clin_data[i][j]) if isinstance(clin_data[i][j], str) else clin_data[i][j].text
if hasattr(clin_data[i][j], 'text') else clin_data[i][j],
S('tc2', fontSize=9.5, leading=14))
# rebuild properly
clin_data2 = [
[Paragraph('<b>Application</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER)),
Paragraph('<b>Mechanism</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER))],
[Paragraph('<b>Combined oral contraceptives</b>', S('tc2', fontSize=9.5, fontName='Helvetica-Bold', textColor=PURPLE_DARK)),
Paragraph('Steady estrogen + progestin mimic luteal phase → suppress GnRH pulses → no LH surge → no ovulation', S('tc2', fontSize=9.5, leading=14))],
[Paragraph('<b>Clomiphene citrate</b> (fertility drug)', S('tc2', fontSize=9.5, fontName='Helvetica-Bold', textColor=PURPLE_DARK)),
Paragraph('Blocks estrogen receptors → prevents negative feedback → FSH/LH rise → stimulates follicular growth', S('tc2', fontSize=9.5, leading=14))],
[Paragraph('<b>GnRH agonists</b> (leuprolide)', S('tc2', fontSize=9.5, fontName='Helvetica-Bold', textColor=PURPLE_DARK)),
Paragraph('Continuous GnRH → receptor downregulation → shuts down entire HPO axis. Used in endometriosis, IVF', S('tc2', fontSize=9.5, leading=14))],
[Paragraph('<b>Hypothalamic amenorrhea</b>', S('tc2', fontSize=9.5, fontName='Helvetica-Bold', textColor=PURPLE_DARK)),
Paragraph('Stress/weight loss/over-exercise disrupts pulsatile GnRH → no FSH/LH → anovulation + no periods', S('tc2', fontSize=9.5, leading=14))],
]
clin_tbl = Table(clin_data2, colWidths=[5.5*cm, 10.8*cm])
clin_tbl.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), TEAL),
('BACKGROUND', (0,1), (-1,1), colors.HexColor('#E8FAFA')),
('BACKGROUND', (0,3), (-1,3), colors.HexColor('#E8FAFA')),
('BOX', (0,0), (-1,-1), 1.2, TEAL),
('INNERGRID', (0,0), (-1,-1), 0.5, GREY_MED),
('TOPPADDING', (0,0), (-1,-1), 7),
('BOTTOMPADDING', (0,0), (-1,-1), 7),
('LEFTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(clin_tbl)
story.append(Spacer(1, 0.3*cm))
# 8. QUICK-REFERENCE TIMELINE TABLE
story.append(section_header('8. QUICK-REFERENCE TIMELINE TABLE', color=PURPLE_DARK))
story.append(Spacer(1, 0.15*cm))
tl_data = [
[Paragraph('<b>Days</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER)),
Paragraph('<b>Phase</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER)),
Paragraph('<b>Dominant Hormone</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER)),
Paragraph('<b>Endometrium</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER)),
Paragraph('<b>Key Event</b>', S('th', textColor=WHITE, fontName='Helvetica-Bold', fontSize=10, alignment=TA_CENTER))],
['1–4', 'Menstruation', 'Low E2, Low P4', 'Shedding (stratum functionale)', 'Bleeding; cycle Day 1'],
['5–13', 'Proliferative (Follicular)', 'Rising Estradiol', 'Thickens; glands elongate', 'Dominant follicle selected'],
['~14', 'Ovulation', 'LH surge', 'Peak thickness', 'Egg released (~9 h after LH peak)'],
['15–28', 'Secretory (Luteal)', 'Progesterone + E2', 'Tortuous glands; edematous stroma', 'Corpus luteum active'],
['24–28', 'Late Luteal', 'E2 + P4 falling', 'Breakdown of blood supply', 'Corpus luteum degenerates'],
]
row_colors = [
colors.HexColor('#FFE8E8'), colors.HexColor('#FFF0E8'),
colors.HexColor('#FFFACC'), colors.HexColor('#E8F5E8'),
colors.HexColor('#EDE8F5')
]
for i in range(1, len(tl_data)):
for j in range(len(tl_data[i])):
tl_data[i][j] = Paragraph(tl_data[i][j], S('td', fontSize=9.5, leading=13))
tl_tbl = Table(tl_data, colWidths=[1.6*cm, 3.8*cm, 3.5*cm, 4.2*cm, 3.2*cm])
ts = TableStyle([
('BACKGROUND', (0,0), (-1,0), PURPLE_DARK),
('BOX', (0,0), (-1,-1), 1.2, PURPLE_DARK),
('INNERGRID', (0,0), (-1,-1), 0.5, GREY_MED),
('TOPPADDING', (0,0), (-1,-1), 6),
('BOTTOMPADDING', (0,0), (-1,-1), 6),
('LEFTPADDING', (0,0), (-1,-1), 7),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
])
for i, c in enumerate(row_colors):
ts.add('BACKGROUND', (0, i+1), (-1, i+1), c)
tl_tbl.setStyle(ts)
story.append(tl_tbl)
story.append(Spacer(1, 0.5*cm))
# ═══════════════════════════════════════════════
# PAGE 4 — FLOWCHART
# ═══════════════════════════════════════════════
story.append(PageBreak())
story.append(section_header('9. FLOWCHART — Hormonal Control of the Menstrual Cycle', color=PURPLE_DARK))
story.append(Spacer(1, 0.2*cm))
if os.path.exists(FLOWCHART):
img = Image(FLOWCHART, width=16.5*cm, height=23.5*cm)
story.append(img)
else:
story.append(Paragraph('[Flowchart image not found]', body_style))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
'Figure: Complete hormonal control flowchart of the 28-day menstrual cycle showing the '
'hypothalamic–pituitary–ovarian axis, feedback mechanisms, endometrial phases, and clinical outcomes.',
note_style))
# ─── BUILD ───
doc.build(story)
print("PDF created successfully at:", OUTPUT)
cd /home/daytona/workspace/menstrual-cycle && python create_pdf.py 2>&1
ls -lh /home/daytona/workspace/menstrual-cycle/
flowchart
Image · PNG
Menstrual Cycle Seminar
PDF Document · PDF
| Page | Content |
|---|---|
| Page 1 | Title + Introduction + Hormone Table (GnRH, FSH, LH, E2, P4, Inhibin B) |
| Page 2 | Follicular Phase → LH Surge → Luteal Phase + Fertilization vs. No Fertilization outcomes |
| Page 3 | Feedback Control Summary Table + Clinical Applications Table + 28-Day Timeline Table |
| Page 4 | Full colour flowchart of the entire hormonal axis |