Detailed Ppt on normal labour from oxorn foote,d.c dutta with pictures inserted at all topics
normal labour mechanism cardinal movements obstetric diagram images
normal labour stages cervical dilation partogram diagram obstetrics DC Dutta
mkdir -p /tmp/workspace/normal-labour-ppt && ls /tmp/workspace/
python /tmp/skills/shared/scripts/fetch_images.py \ "https://cdn.lecturio.com/assets/The-cardinal-movements-of-labor.jpg" \ "https://d45jl3w9libvn.cloudfront.net/jaypee/static/books/9789351527237/Chapters/images/151-1.jpg" \ "https://d45jl3w9libvn.cloudfront.net/jaypee/static/books/9789351527237/Chapters/images/464-1.jpg" \ "https://image.slidesharecdn.com/partograph-201001064106/85/Partograph-2-320.jpg" \ "https://media.springernature.com/lw685/springer-static/image/chp%3A10.1007%2F978-981-19-6145-8_14/MediaObjects/528146_1_En_14_Fig1_HTML.png" 2>&1 | head -20
ls -la /tmp/workspace/normal-labour-ppt/ 2>/dev/null && echo "---" && ls /tmp/workspace/ 2>/dev/null
python3 << 'PYEOF' import requests, os, sys from pathlib import Path out = Path("/tmp/workspace/normal-labour-ppt") out.mkdir(parents=True, exist_ok=True) headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36"} urls = { "cardinal": "https://www.lecturio.com/magazine/wp-content/uploads/2021/03/cardinal-movements-of-labor.jpg", "engagement": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Fetal_head_engagement_diagram.svg/640px-Fetal_head_engagement_diagram.svg.png", "partograph": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Partograph.jpg/800px-Partograph.jpg", "pelvis": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Pelvis_diagram.svg/640px-Pelvis_diagram.svg.png", "cervix": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4e/Cervical_dilation_vs_effacement.svg/640px-Cervical_dilation_vs_effacement.svg.png", "fetal_skull": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/Fetal_skull_diagram.svg/640px-Fetal_skull_diagram.svg.png", "stages": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Stages_of_labor.svg/640px-Stages_of_labor.svg.png", } saved = {} for name, url in urls.items(): try: r = requests.get(url, headers=headers, timeout=15) if r.status_code == 200 and len(r.content) > 1000: ext = ".png" if url.endswith(".png") or url.endswith(".svg.png") else ".jpg" p = out / f"img_{name}{ext}" p.write_bytes(r.content) saved[name] = str(p) print(f"OK {name}: {len(r.content)} bytes -> {p.name}") else: print(f"FAIL {name}: status {r.status_code}, size {len(r.content)}") except Exception as e: print(f"ERR {name}: {e}") print("\nSaved:", list(saved.keys())) PYEOF
~/normal-labour-ppt/build_ppt.py
"""
Normal Labour PowerPoint Builder
Uses python-pptx + matplotlib-generated images for every slide.
"""
import os
import io
import math
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.patches as patches
from matplotlib.patches import FancyArrowPatch, Arc, Circle, FancyBboxPatch, Wedge
from matplotlib.lines import Line2D
import numpy as np
from pathlib import Path
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
from pptx.util import Inches, Pt
OUT = Path("/tmp/workspace/normal-labour-ppt")
OUT.mkdir(parents=True, exist_ok=True)
# ── Colour palette ───────────────────────────────────────────────────────────
NAVY = RGBColor(0x1A, 0x3A, 0x5C)
TEAL = RGBColor(0x00, 0x7A, 0x87)
GOLD = RGBColor(0xF5, 0xA6, 0x23)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
LGRAY = RGBColor(0xF0, 0xF4, 0xF8)
DKGRAY = RGBColor(0x33, 0x33, 0x33)
RED = RGBColor(0xC0, 0x39, 0x2B)
GREEN = RGBColor(0x27, 0xAE, 0x60)
# ── Slide dimensions ─────────────────────────────────────────────────────────
W_IN = 13.333
H_IN = 7.5
# ─────────────────────────────────────────────────────────────────────────────
# HELPER: save matplotlib fig → BytesIO
# ─────────────────────────────────────────────────────────────────────────────
def fig_to_stream(fig):
buf = io.BytesIO()
fig.savefig(buf, format="png", bbox_inches="tight", dpi=130)
buf.seek(0)
plt.close(fig)
return buf
# ─────────────────────────────────────────────────────────────────────────────
# HELPER: add slide with gradient-ish header band
# ─────────────────────────────────────────────────────────────────────────────
def add_slide(prs, title_text, subtitle_text=""):
slide_layout = prs.slide_layouts[6] # blank
slide = prs.slides.add_slide(slide_layout)
sw, sh = prs.slide_width, prs.slide_height
# Background
bg = slide.shapes.add_shape(1, 0, 0, sw, sh)
bg.fill.solid(); bg.fill.fore_color.rgb = LGRAY
bg.line.fill.background()
# Header band
hband = slide.shapes.add_shape(1, 0, 0, sw, Inches(1.35))
hband.fill.solid(); hband.fill.fore_color.rgb = NAVY
hband.line.fill.background()
# Accent stripe
stripe = slide.shapes.add_shape(1, 0, Inches(1.35), sw, Inches(0.08))
stripe.fill.solid(); stripe.fill.fore_color.rgb = GOLD
stripe.line.fill.background()
# Title
tx = slide.shapes.add_textbox(Inches(0.35), Inches(0.12), Inches(12.3), Inches(1.0))
tf = tx.text_frame; tf.word_wrap = True
p = tf.paragraphs[0]
p.text = title_text
p.font.bold = True; p.font.size = Pt(32); p.font.color.rgb = WHITE
# Subtitle
if subtitle_text:
tx2 = slide.shapes.add_textbox(Inches(0.35), Inches(0.95), Inches(12.3), Inches(0.42))
tf2 = tx2.text_frame
p2 = tf2.paragraphs[0]
p2.text = subtitle_text
p2.font.size = Pt(16); p2.font.color.rgb = GOLD; p2.font.bold = True
return slide
# ─────────────────────────────────────────────────────────────────────────────
# HELPER: add bullet text box
# ─────────────────────────────────────────────────────────────────────────────
def add_bullets(slide, bullets, left, top, width, height, font_size=17):
tx = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
tf = tx.text_frame; tf.word_wrap = True
for i, b in enumerate(bullets):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
indent = b.startswith(" ")
p.text = b.strip()
p.font.size = Pt(font_size - (2 if indent else 0))
p.font.color.rgb = DKGRAY
p.space_after = Pt(4)
if not indent:
p.font.bold = False
p.level = 1 if indent else 0
# ─────────────────────────────────────────────────────────────────────────────
# HELPER: insert image stream on slide
# ─────────────────────────────────────────────────────────────────────────────
def insert_image(slide, stream, left, top, width, height):
slide.shapes.add_picture(stream, Inches(left), Inches(top),
Inches(width), Inches(height))
# ─────────────────────────────────────────────────────────────────────────────
# IMAGE GENERATORS
# ─────────────────────────────────────────────────────────────────────────────
def img_definition_timeline():
fig, ax = plt.subplots(figsize=(7, 4))
ax.set_xlim(0, 10); ax.set_ylim(0, 5); ax.axis("off")
ax.set_facecolor("#f0f4f8"); fig.patch.set_facecolor("#f0f4f8")
# Timeline bar
ax.add_patch(FancyBboxPatch((0.5, 2.2), 9, 0.6, boxstyle="round,pad=0.05",
fc="#1A3A5C", ec="none"))
events = [
(1.0, "Onset of\nTrue Labour"),
(3.5, "Full Cervical\nDilation (10 cm)"),
(6.5, "Delivery of\nBaby"),
(9.0, "Delivery of\nPlacenta"),
]
colors = ["#F5A623", "#00AAB5", "#27AE60", "#C0392B"]
for x, label, col in zip([e[0] for e in events], [e[1] for e in events], colors):
ax.add_patch(Circle((x, 2.5), 0.18, fc=col, ec="white", zorder=5, lw=2))
ax.text(x, 1.6, label, ha="center", va="top", fontsize=9, color="#1A3A5C",
fontweight="bold", linespacing=1.4)
# Stage labels
stage_data = [(2.25, "Stage I", "#1A3A5C"), (5.0, "Stage II", "#007A87"),
(7.75, "Stage III", "#27AE60")]
for xm, lbl, col in stage_data:
ax.text(xm, 3.2, lbl, ha="center", va="bottom", fontsize=10,
color=col, fontweight="bold")
ax.text(5, 4.6, "Normal Labour — Definition & Stages", ha="center", va="top",
fontsize=13, fontweight="bold", color="#1A3A5C")
return fig_to_stream(fig)
def img_true_vs_false():
fig, axes = plt.subplots(1, 2, figsize=(8, 4))
fig.patch.set_facecolor("#f0f4f8")
data = {
"True Labour": {
"color": "#27AE60",
"items": [
"Regular, rhythmic contractions",
"Increasing frequency & intensity",
"Pain radiates lumbosacral → abdomen",
"Cervix dilates & effaces",
"Show (bloody mucus) present",
"Walking increases contractions",
"Analgesics do NOT stop labour",
]
},
"False Labour": {
"color": "#C0392B",
"items": [
"Irregular contractions (Braxton-Hicks)",
"No progressive increase",
"Pain mainly in lower abdomen",
"No cervical change",
"Show usually absent",
"Walking may relieve pain",
"Analgesics may stop contractions",
]
}
}
for ax, (title, info) in zip(axes, data.items()):
ax.set_xlim(0, 1); ax.set_ylim(0, 1); ax.axis("off")
ax.set_facecolor("#f0f4f8")
ax.add_patch(FancyBboxPatch((0.02, 0.02), 0.96, 0.96,
boxstyle="round,pad=0.03",
fc="white", ec=info["color"], lw=2.5))
ax.text(0.5, 0.92, title, ha="center", va="top", fontsize=12,
fontweight="bold", color=info["color"])
for i, item in enumerate(info["items"]):
ax.text(0.08, 0.80 - i*0.11, f"• {item}", ha="left", va="top",
fontsize=8, color="#333333", wrap=True)
fig.suptitle("True vs False Labour", fontsize=14, fontweight="bold",
color="#1A3A5C", y=1.01)
plt.tight_layout()
return fig_to_stream(fig)
def img_uterine_contractions():
fig, ax = plt.subplots(figsize=(7, 3.5))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8")
t = np.linspace(0, 10, 1000)
# Simulate 3 contractions
y = np.zeros_like(t)
for peak in [1.5, 4.0, 7.0]:
y += 70 * np.exp(-((t - peak)**2) / 0.3)
y += 10 # baseline tone
y = np.clip(y, 0, 100)
ax.fill_between(t, 10, y, where=(y > 10), alpha=0.35, color="#007A87")
ax.plot(t, y, color="#1A3A5C", lw=2.5)
ax.axhline(10, color="#C0392B", lw=1.2, ls="--", label="Baseline tone (10 mmHg)")
ax.axhline(25, color="#F5A623", lw=1.2, ls=":", label="Threshold (25 mmHg)")
ax.set_xlabel("Time (minutes)", fontsize=10, color="#333")
ax.set_ylabel("Intrauterine Pressure (mmHg)", fontsize=10, color="#333")
ax.set_title("Uterine Contractions in Labour", fontsize=12,
fontweight="bold", color="#1A3A5C")
ax.set_ylim(0, 110)
ax.legend(fontsize=8, loc="upper right")
# Annotate peaks
for pk, lbl in [(1.5, "1st"), (4.0, "2nd"), (7.0, "3rd")]:
ax.annotate(f"{lbl}\nContraction\n~60-80 mmHg",
xy=(pk, 82), xytext=(pk + 0.3, 100),
arrowprops=dict(arrowstyle="->", color="#1A3A5C"),
fontsize=7.5, color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_cervical_dilation():
fig, axes = plt.subplots(1, 3, figsize=(8, 3.5))
fig.patch.set_facecolor("#f0f4f8")
stages = [
("Early Labour\n3-4 cm", 3.5, 0.3, "#F5A623"),
("Active Labour\n6-7 cm", 6.5, 0.6, "#007A87"),
("Full Dilation\n10 cm", 10, 1.0, "#27AE60"),
]
for ax, (label, diam, eff, col) in zip(axes, stages):
ax.set_xlim(-6, 6); ax.set_ylim(-6, 6); ax.set_aspect("equal")
ax.axis("off"); ax.set_facecolor("#f0f4f8")
# Cervix ring
outer = Circle((0, 0), 5, fc="#ffcccc", ec="#C0392B", lw=2)
inner = Circle((0, 0), diam/2, fc="white", ec=col, lw=2.5)
ax.add_patch(outer); ax.add_patch(inner)
# Effacement arc
arc = Arc((0, 0), 10.5, 10.5, angle=0, theta1=0,
theta2=360 * eff, color=col, lw=3)
ax.add_patch(arc)
ax.text(0, -5.6, label, ha="center", va="top", fontsize=9,
fontweight="bold", color=col)
ax.text(0, 0, f"{diam}\ncm", ha="center", va="center",
fontsize=11, fontweight="bold", color=col)
fig.suptitle("Cervical Dilation & Effacement", fontsize=12,
fontweight="bold", color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_cardinal_movements():
fig, axes = plt.subplots(2, 4, figsize=(11, 5))
fig.patch.set_facecolor("#f0f4f8")
movements = [
("1. Engagement", "#1A3A5C",
"Head enters pelvis\nbiparietal diameter\npasses pelvic inlet"),
("2. Descent", "#007A87",
"Progressive\ndownward movement\nthroughout labour"),
("3. Flexion", "#27AE60",
"Chin meets chest\nSmallest diameter\npresents"),
("4. Internal\nRotation", "#F5A623",
"Occiput rotates\nanteriorly to\npubic symphysis"),
("5. Extension", "#C0392B",
"Head extends as\nit passes under\npubic arch"),
("6. Restitution", "#8E44AD",
"Head returns to\noblique position\n(undoes rotation)"),
("7. External\nRotation", "#2980B9",
"Shoulders align\nwith AP diameter\nof outlet"),
("8. Expulsion", "#16A085",
"Anterior then\nposterior shoulder\ndelivered"),
]
for ax, (title, col, desc) in zip(axes.flat, movements):
ax.set_xlim(0, 1); ax.set_ylim(0, 1); ax.axis("off")
ax.set_facecolor("white")
ax.add_patch(FancyBboxPatch((0.02, 0.02), 0.96, 0.96,
boxstyle="round,pad=0.04",
fc="white", ec=col, lw=2))
# Icon circle
ax.add_patch(Circle((0.5, 0.72), 0.18, fc=col, ec="none"))
ax.text(0.5, 0.72, str(movements.index((title, col, desc)) + 1),
ha="center", va="center", fontsize=14, fontweight="bold", color="white")
ax.text(0.5, 0.50, title, ha="center", va="center", fontsize=8.5,
fontweight="bold", color=col, linespacing=1.3)
ax.text(0.5, 0.22, desc, ha="center", va="center", fontsize=7,
color="#444", linespacing=1.4)
fig.suptitle("8 Cardinal Movements of Labour", fontsize=13,
fontweight="bold", color="#1A3A5C", y=1.01)
plt.tight_layout(pad=0.3)
return fig_to_stream(fig)
def img_fetal_skull():
fig, axes = plt.subplots(1, 2, figsize=(9, 4))
fig.patch.set_facecolor("#f0f4f8")
# Left: Skull bones
ax = axes[0]
ax.set_xlim(0, 10); ax.set_ylim(0, 8); ax.axis("off")
ax.set_facecolor("#f0f4f8")
# Simple oval for skull
skull = mpatches.Ellipse((5, 4), 7, 5.5, fc="#FFE0B2", ec="#8B4513", lw=2)
ax.add_patch(skull)
# Sutures & fontanelles
ax.plot([5, 5], [1.2, 6.8], color="#8B4513", lw=1.5, ls="--") # sagittal
ax.plot([1.8, 8.2], [4, 4], color="#8B4513", lw=1.5, ls="--") # coronal approx
ax.plot([2.2, 7.8], [5.5, 5.5], color="#8B4513", lw=1, ls=":") # lambdoid approx
labels = [
(5, 6.8, "Anterior\nFontanelle\n(Bregma)", "green"),
(5, 1.5, "Posterior\nFontanelle\n(Lambda)", "blue"),
(5, 4.2, "Sagittal\nSuture", "#8B4513"),
(2.5, 4.0, "Coronal\nSuture", "purple"),
]
for x, y, lbl, col in labels:
ax.text(x, y, lbl, ha="center", va="center", fontsize=7,
color=col, fontweight="bold",
bbox=dict(fc="white", ec=col, boxstyle="round,pad=0.2", alpha=0.8))
ax.set_title("Fetal Skull — Landmarks", fontsize=10, fontweight="bold", color="#1A3A5C")
# Right: Diameters table
ax2 = axes[1]
ax2.set_xlim(0, 1); ax2.set_ylim(0, 1); ax2.axis("off")
ax2.set_facecolor("#f0f4f8")
headers = ["Diameter", "Measurement", "Presentation"]
rows = [
["Suboccipito-\nbregmatic", "9.5 cm", "Vertex (fully\nflexed)"],
["Suboccipito-\nfrontal", "10 cm", "Vertex (partially\nflexed)"],
["Occipito-\nfrontal", "11.5 cm", "Deflexed vertex"],
["Mento-\nvertical", "13.5 cm", "Brow"],
["Submento-\nbregmatic", "9.5 cm", "Face"],
["Biparietal", "9.5 cm", "Transverse"],
]
row_h = 0.12; col_w = [0.35, 0.25, 0.40]
xs = [0.02, 0.37, 0.62]
for j, (h, cw) in enumerate(zip(headers, col_w)):
ax2.add_patch(FancyBboxPatch((xs[j], 0.88), cw - 0.02, row_h - 0.01,
boxstyle="round,pad=0.01", fc="#1A3A5C", ec="none"))
ax2.text(xs[j] + (cw-0.02)/2, 0.88 + (row_h-0.01)/2,
h, ha="center", va="center", fontsize=8,
fontweight="bold", color="white")
for i, row in enumerate(rows):
bg_col = "white" if i % 2 == 0 else "#EBF5FB"
y = 0.88 - (i + 1) * row_h
for j, (cell, cw) in enumerate(zip(row, col_w)):
ax2.add_patch(FancyBboxPatch((xs[j], y), cw - 0.02, row_h - 0.01,
boxstyle="round,pad=0.01", fc=bg_col, ec="#ddd"))
ax2.text(xs[j] + (cw-0.02)/2, y + (row_h-0.01)/2,
cell, ha="center", va="center", fontsize=7, color="#333",
linespacing=1.2)
ax2.set_title("Fetal Head Diameters", fontsize=10, fontweight="bold", color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_pelvis_types():
fig, axes = plt.subplots(1, 4, figsize=(10, 3.5))
fig.patch.set_facecolor("#f0f4f8")
types = [
("Gynaecoid", "#27AE60", (1.0, 1.0), "50%\nMost favourable"),
("Android", "#C0392B", (0.8, 1.0), "20%\nUnfavourable"),
("Anthropoid","#F5A623", (0.7, 1.3), "25%\nEngagement\nin AP"),
("Platypelloid","#007A87",(1.3, 0.6), "5%\nTransverse\nengagement"),
]
for ax, (name, col, (rx, ry), note) in zip(axes, types):
ax.set_xlim(-2, 2); ax.set_ylim(-2, 2.5); ax.set_aspect("equal"); ax.axis("off")
ax.set_facecolor("#f0f4f8")
ellipse = mpatches.Ellipse((0, 0), 2*rx, 2*ry,
fc=col + "40" if len(col) == 7 else col,
ec=col, lw=2.5, alpha=0.6)
# Use patch with alpha
ell = mpatches.Ellipse((0, 0), 2*rx, 2*ry, fc=col, ec="white",
lw=2, alpha=0.35)
ell2 = mpatches.Ellipse((0, 0), 2*rx, 2*ry, fc="none", ec=col, lw=2.5)
ax.add_patch(ell); ax.add_patch(ell2)
ax.text(0, ry + 0.4, name, ha="center", va="bottom", fontsize=10,
fontweight="bold", color=col)
ax.text(0, -ry - 0.35, note, ha="center", va="top", fontsize=8,
color="#444", linespacing=1.3)
# Dimensions
ax.annotate("", xy=(rx, 0), xytext=(-rx, 0),
arrowprops=dict(arrowstyle="<->", color=col, lw=1.5))
ax.annotate("", xy=(0, ry), xytext=(0, -ry),
arrowprops=dict(arrowstyle="<->", color=col, lw=1.5))
fig.suptitle("Caldwell-Moloy Classification of Pelvis Types",
fontsize=12, fontweight="bold", color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_stages_of_labour():
fig, ax = plt.subplots(figsize=(9, 4.5))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8")
stages = [
("STAGE I\nOnset → Full Dilation", "#1A3A5C", 0, 3.8,
["Latent phase: 0-3 cm (slow)", "Active phase: 4-10 cm (fast)", "Duration: 12h primip / 6h multip"]),
("STAGE II\nFull Dilation → Delivery of Baby", "#007A87", 3.8, 2.2,
["Pushing + expulsion", "Duration: 1h primip / 30 min multip", "Cardinal movements occur"]),
("STAGE III\nDelivery → Placenta Out", "#27AE60", 6.0, 1.8,
["Placental separation & expulsion", "Duration: 15-30 min", "Active management (Oxytocin)"]),
("STAGE IV\nRecovery (1-2 hrs postpartum)", "#F5A623", 7.8, 2.2,
["Monitor vitals & uterine tone", "PPH prevention", "Bonding & breastfeeding"]),
]
y_top = 3.8
for label, col, x_start, width, notes in stages:
ax.add_patch(FancyBboxPatch((x_start + 0.05, 0.1), width - 0.1, y_top - 0.2,
boxstyle="round,pad=0.08", fc=col, ec="white", lw=2, alpha=0.9))
ax.text(x_start + width/2, y_top - 0.45, label, ha="center", va="top",
fontsize=8.5, fontweight="bold", color="white", linespacing=1.4)
for i, note in enumerate(notes):
ax.text(x_start + width/2, y_top - 1.3 - i*0.62, f"• {note}",
ha="center", va="top", fontsize=7, color="white",
linespacing=1.3, wrap=True)
ax.set_xlim(0, 10); ax.set_ylim(0, 4.5); ax.axis("off")
ax.text(5, 4.35, "Four Stages of Labour", ha="center", va="top",
fontsize=13, fontweight="bold", color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_partograph():
fig, axes = plt.subplots(2, 1, figsize=(9, 5.5), gridspec_kw={"height_ratios": [3, 1]})
fig.patch.set_facecolor("#f0f4f8")
ax = axes[0]
ax.set_facecolor("#fffef5")
# Alert and action lines
hours = np.array([0, 1, 2, 3, 4])
alert = np.array([4, 5, 6, 7, 8])
action = alert + 2
ax.plot(hours, alert, "b-o", lw=2, ms=6, label="Alert Line (1 cm/hr)")
ax.plot(hours, action, "r-s", lw=2, ms=6, label="Action Line")
# Simulated progress
progress = np.array([4, 5.5, 7, 9, 10])
ax.plot(hours, progress, "g-D", lw=2.5, ms=7, label="Cervical Dilation (Patient)")
ax.fill_between(hours, alert, action, alpha=0.12, color="orange", label="Action Zone")
ax.set_xlim(-0.2, 4.5); ax.set_ylim(3, 11)
ax.set_xlabel("Hours in Active Labour", fontsize=10); ax.set_ylabel("Cervical Dilation (cm)", fontsize=10)
ax.set_title("Partograph — Cervicograph", fontsize=11, fontweight="bold", color="#1A3A5C")
ax.legend(fontsize=8, loc="upper left")
ax.grid(True, alpha=0.3)
ax.set_yticks(range(4, 11))
ax.set_xticks(range(0, 5))
# Fetal HR
ax2 = axes[1]
ax2.set_facecolor("#f5f5ff")
fhr_t = np.linspace(0, 4, 80)
fhr = 140 + 10 * np.sin(fhr_t * 3) + np.random.normal(0, 3, 80)
ax2.plot(fhr_t, fhr, color="#2980B9", lw=1.5)
ax2.axhline(110, color="#C0392B", ls="--", lw=1, label="Min normal (110)")
ax2.axhline(160, color="#C0392B", ls="--", lw=1, label="Max normal (160)")
ax2.fill_between(fhr_t, 110, 160, alpha=0.1, color="#27AE60")
ax2.set_xlim(-0.2, 4.5); ax2.set_ylim(90, 185)
ax2.set_xlabel("Hours", fontsize=9); ax2.set_ylabel("FHR (bpm)", fontsize=9)
ax2.legend(fontsize=7, loc="upper right"); ax2.grid(True, alpha=0.3)
ax2.set_title("Fetal Heart Rate Monitoring", fontsize=9, color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_mechanism_occiput_anterior():
fig, axes = plt.subplots(1, 5, figsize=(11, 3.5))
fig.patch.set_facecolor("#f0f4f8")
steps = [
("Engagement &\nDescend", "#1A3A5C",
[(0.5, 0.3), (0.5, 0.7)], "↓"),
("Flexion", "#007A87",
[(0.5, 0.3), (0.5, 0.7)], "↓ + flex"),
("Internal\nRotation", "#F5A623",
[(0.5, 0.3), (0.5, 0.7)], "↻ OA"),
("Extension", "#27AE60",
[(0.5, 0.3), (0.5, 0.7)], "↑ extend"),
("External\nRotation &\nExpulsion", "#C0392B",
[(0.5, 0.3), (0.5, 0.7)], "↻ out"),
]
for ax, (title, col, pts, arrow_lbl) in zip(axes, steps):
ax.set_xlim(0, 1); ax.set_ylim(0, 1); ax.axis("off")
ax.set_facecolor("#f0f4f8")
# Box
ax.add_patch(FancyBboxPatch((0.05, 0.05), 0.90, 0.90,
boxstyle="round,pad=0.04",
fc="white", ec=col, lw=2))
# Pelvis arch
ax.add_patch(Arc((0.5, 0.0), 0.75, 0.65, angle=0,
theta1=20, theta2=160, color="#8B4513", lw=3))
# Head
head = Circle((0.5, 0.42), 0.14, fc=col, ec="white", lw=1.5, alpha=0.85)
ax.add_patch(head)
ax.text(0.5, 0.42, arrow_lbl, ha="center", va="center",
fontsize=10, fontweight="bold", color="white")
ax.text(0.5, 0.88, title, ha="center", va="top", fontsize=8,
fontweight="bold", color=col, linespacing=1.3)
fig.suptitle("Mechanism of Labour — Left Occipito-Anterior (LOA)",
fontsize=12, fontweight="bold", color="#1A3A5C", y=1.02)
plt.tight_layout(pad=0.2)
return fig_to_stream(fig)
def img_management_summary():
fig, ax = plt.subplots(figsize=(9, 5))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
phases = [
("ADMISSION", "#1A3A5C",
["• History & physical exam", "• Confirm true labour", "• Assess cervical dilation",
"• FHR auscultation", "• IV access if needed"]),
("STAGE I\nMONITORING", "#007A87",
["• FHR every 30 min (latent)\n every 15 min (active)", "• Contractions frequency/strength",
"• Vaginal exam every 4 hrs", "• Partograph maintenance",
"• Maternal vitals hourly"]),
("STAGE II\nDELIVERY", "#27AE60",
["• Active pushing guidance", "• Perineal support", "• Control head delivery",
"• Oxytocin 10 IU IM on\n anterior shoulder",
"• APGAR at 1 & 5 min"]),
("STAGE III &\nIV", "#F5A623",
["• Active management", "• Controlled cord traction", "• Uterine massage",
"• Inspect placenta/membranes", "• Perineal repair if needed"]),
]
x_positions = [0.05, 0.30, 0.55, 0.78]
for i, ((phase, col, items), x) in enumerate(zip(phases, x_positions)):
ax.add_patch(FancyBboxPatch((x, 0.08), 0.22, 0.88,
boxstyle="round,pad=0.02",
fc=col, ec="white", lw=2, alpha=0.9))
ax.text(x + 0.11, 0.93, phase, ha="center", va="top", fontsize=9,
fontweight="bold", color="white", linespacing=1.3)
for j, item in enumerate(items):
ax.text(x + 0.11, 0.82 - j * 0.145, item,
ha="center", va="top", fontsize=6.8, color="white", linespacing=1.3)
ax.text(0.5, 0.99, "Management of Normal Labour", ha="center", va="top",
fontsize=13, fontweight="bold", color="#1A3A5C",
transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
def img_oxytocin_uterotonic():
fig, axes = plt.subplots(1, 2, figsize=(9, 4))
fig.patch.set_facecolor("#f0f4f8")
# Left: Uterotonic comparison
ax = axes[0]
ax.set_facecolor("#f0f4f8"); ax.axis("off")
ax.set_xlim(0, 1); ax.set_ylim(0, 1)
drugs = [
("Oxytocin", "10 IU IM / IV infusion", "1st line", "#27AE60"),
("Misoprostol", "600 mcg oral / sublingual", "If oxytocin unavailable", "#F5A623"),
("Ergometrine", "0.5 mg IM", "Avoid in hypertension", "#C0392B"),
("Carboprost", "250 mcg IM q15min", "Refractory PPH", "#007A87"),
]
ax.text(0.5, 0.97, "Uterotonic Agents (Stage III)", ha="center", va="top",
fontsize=10, fontweight="bold", color="#1A3A5C")
for i, (drug, dose, note, col) in enumerate(drugs):
y = 0.80 - i * 0.20
ax.add_patch(FancyBboxPatch((0.03, y - 0.06), 0.94, 0.17,
boxstyle="round,pad=0.02",
fc=col, ec="white", lw=1.5, alpha=0.85))
ax.text(0.07, y + 0.03, drug, ha="left", va="center",
fontsize=9, fontweight="bold", color="white")
ax.text(0.07, y - 0.03, f"{dose} | {note}", ha="left", va="center",
fontsize=7.5, color="white")
# Right: PPH risk factors
ax2 = axes[1]
ax2.set_facecolor("#f0f4f8"); ax2.axis("off")
ax2.set_xlim(0, 1); ax2.set_ylim(0, 1)
ax2.text(0.5, 0.97, "PPH Risk Factors (4 T's)", ha="center", va="top",
fontsize=10, fontweight="bold", color="#1A3A5C")
four_t = [
("TONE", "#C0392B", "Uterine atony (80%)", ["Grand multiparity", "Macrosomia", "Prolonged labour"]),
("TRAUMA", "#E67E22", "Lacerations/rupture", ["Instrumental delivery", "Precipitate labour"]),
("TISSUE", "#F5A623", "Retained products", ["Incomplete placenta", "Accessory lobe"]),
("THROMBIN", "#007A87", "Coagulopathy", ["DIC", "Anticoagulation", "Liver disease"]),
]
for i, (t, col, sub, items) in enumerate(four_t):
y = 0.78 - i * 0.195
ax2.add_patch(Circle((0.1, y), 0.065, fc=col, ec="none", alpha=0.9))
ax2.text(0.10, y, t, ha="center", va="center", fontsize=7.5,
fontweight="bold", color="white")
ax2.text(0.20, y + 0.03, sub, ha="left", va="center",
fontsize=8.5, fontweight="bold", color=col)
ax2.text(0.20, y - 0.04, " | ".join(items), ha="left", va="center",
fontsize=7, color="#444")
plt.tight_layout()
return fig_to_stream(fig)
def img_episiotomy():
fig, axes = plt.subplots(1, 2, figsize=(9, 4))
fig.patch.set_facecolor("#f0f4f8")
ax = axes[0]
ax.set_xlim(-1, 6); ax.set_ylim(-1, 5); ax.axis("off"); ax.set_facecolor("#f0f4f8")
ax.set_title("Types of Episiotomy", fontsize=11, fontweight="bold", color="#1A3A5C")
# Perineum schematic
ax.add_patch(mpatches.Ellipse((2.5, 2.5), 4, 3, fc="#FFDAB9", ec="#8B4513", lw=2, alpha=0.6))
# Introitus
ax.add_patch(mpatches.Ellipse((2.5, 2.5), 1.2, 0.8, fc="#CC8866", ec="#8B4513", lw=1.5))
ax.text(2.5, 2.5, "Introitus", ha="center", va="center", fontsize=7, color="white")
# Midline incision
ax.plot([2.5, 2.5], [1.9, 0.3], color="#C0392B", lw=3, label="Midline")
# Mediolateral incision
ax.plot([2.5, 4.1], [1.9, 0.5], color="#007A87", lw=3, ls="--", label="Mediolateral")
ax.legend(loc="lower right", fontsize=8)
ax2 = axes[1]
ax2.set_xlim(0, 1); ax2.set_ylim(0, 1); ax2.axis("off"); ax2.set_facecolor("#f0f4f8")
ax2.set_title("Comparison", fontsize=11, fontweight="bold", color="#1A3A5C")
rows = [
("Feature", "Midline", "Mediolateral"),
("Blood loss", "Less", "More"),
("3rd/4th deg\nextension", "Common", "Rare"),
("Healing", "Better", "Slower"),
("Dyspareunia", "Rare", "More common"),
("Indication", "Primipara", "Shoulder\ndystocia etc"),
]
hdrs = [0.02, 0.35, 0.68]
for i, row in enumerate(rows):
bg = "#1A3A5C" if i == 0 else ("white" if i % 2 == 1 else "#EBF5FB")
tc = "white" if i == 0 else "#333"
y = 0.93 - i * 0.14
for j, (cell, cw) in enumerate(zip(row, [0.30, 0.30, 0.30])):
ax2.add_patch(FancyBboxPatch((hdrs[j], y - 0.11), cw - 0.01, 0.12,
boxstyle="round,pad=0.01", fc=bg, ec="#ddd"))
ax2.text(hdrs[j] + (cw-0.01)/2, y - 0.055, cell,
ha="center", va="center", fontsize=7.5, color=tc,
fontweight="bold" if i == 0 else "normal", linespacing=1.2)
plt.tight_layout()
return fig_to_stream(fig)
def img_complications():
fig, ax = plt.subplots(figsize=(9, 4.5))
fig.patch.set_facecolor("#f0f4f8"); ax.axis("off"); ax.set_facecolor("#f0f4f8")
comp_data = {
"Maternal Complications": {
"color": "#C0392B",
"items": ["Prolonged labour (>18h primip, >12h multip)",
"Obstructed labour", "Uterine rupture",
"PPH (atony, trauma, retained placenta)",
"Perineal tears (1st–4th degree)",
"Infection / chorioamnionitis",
"Amniotic fluid embolism"],
},
"Fetal Complications": {
"color": "#1A3A5C",
"items": ["Birth asphyxia / HIE",
"Meconium aspiration syndrome",
"Birth injuries (fracture, nerve palsy)",
"Cord compression / prolapse",
"Shoulder dystocia",
"Intracranial haemorrhage",
"Stillbirth"],
}
}
ax.text(0.5, 0.97, "Complications of Labour", ha="center", va="top",
fontsize=14, fontweight="bold", color="#1A3A5C",
transform=ax.transAxes)
for i, (title, info) in enumerate(comp_data.items()):
x = 0.02 + i * 0.50
ax.add_patch(FancyBboxPatch((x, 0.04), 0.46, 0.86,
boxstyle="round,pad=0.02",
fc=info["color"], ec="white", lw=2, alpha=0.9,
transform=ax.transAxes))
ax.text(x + 0.23, 0.88, title, ha="center", va="top", fontsize=10,
fontweight="bold", color="white", transform=ax.transAxes)
for j, item in enumerate(info["items"]):
ax.text(x + 0.04, 0.78 - j * 0.115, f"• {item}",
ha="left", va="top", fontsize=8, color="white",
transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
def img_newborn_apgar():
fig, ax = plt.subplots(figsize=(9, 4))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
headers = ["Sign", "0", "1", "2"]
rows = [
["Appearance\n(Color)", "Blue/pale\nall over", "Blue\nextremities", "Pink\nall over"],
["Pulse\n(Heart Rate)", "Absent", "< 100\nbpm", "≥ 100\nbpm"],
["Grimace\n(Reflex)", "No\nresponse", "Weak\ncry/grimace", "Cough,\ncry, sneeze"],
["Activity\n(Muscle Tone)", "Limp", "Some\nflexion", "Active\nmotion"],
["Respiration", "Absent", "Slow,\nirregular", "Good,\ncrying"],
]
score_cols = ["#C0392B", "#F5A623", "#27AE60"]
col_widths = [0.20, 0.22, 0.22, 0.22]
xs = [0.02, 0.22, 0.44, 0.66]
# Title
ax.text(0.5, 0.97, "APGAR Score", ha="center", va="top",
fontsize=14, fontweight="bold", color="#1A3A5C",
transform=ax.transAxes)
for j, (h, x, cw) in enumerate(zip(headers, xs, col_widths)):
col = "#1A3A5C" if j == 0 else score_cols[j-1]
ax.add_patch(FancyBboxPatch((x, 0.80), cw - 0.01, 0.13,
boxstyle="round,pad=0.01", fc=col, ec="none",
transform=ax.transAxes))
ax.text(x + (cw-0.01)/2, 0.865, h, ha="center", va="center",
fontsize=10, fontweight="bold", color="white",
transform=ax.transAxes)
for i, row in enumerate(rows):
y = 0.78 - i * 0.155
bg = "white" if i % 2 == 0 else "#EBF5FB"
for j, (cell, x, cw) in enumerate(zip(row, xs, col_widths)):
ax.add_patch(FancyBboxPatch((x, y - 0.13), cw - 0.01, 0.14,
boxstyle="round,pad=0.01",
fc=bg, ec="#ddd", transform=ax.transAxes))
ax.text(x + (cw-0.01)/2, y - 0.06, cell, ha="center", va="center",
fontsize=8, color="#333", linespacing=1.3,
transform=ax.transAxes)
# Score interpretation
interp = [(0.02, "7-10: Normal"), (0.35, "4-6: Moderate depression"), (0.65, "0-3: Severe depression")]
for x, lbl in interp:
col = "#27AE60" if "Normal" in lbl else ("#F5A623" if "Moderate" in lbl else "#C0392B")
ax.add_patch(FancyBboxPatch((x, 0.01), 0.30, 0.07,
boxstyle="round,pad=0.01", fc=col, ec="none",
alpha=0.85, transform=ax.transAxes))
ax.text(x + 0.15, 0.045, lbl, ha="center", va="center",
fontsize=8, fontweight="bold", color="white",
transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
def img_active_management():
fig, ax = plt.subplots(figsize=(8, 4))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
# Title
ax.text(0.5, 0.97, "Active Management of Third Stage (AMTSL)", ha="center",
va="top", fontsize=12, fontweight="bold", color="#1A3A5C",
transform=ax.transAxes)
steps = [
("1", "Uterotonic\nAdministration", "Oxytocin 10 IU IM\nWithin 1 min of delivery\nof anterior shoulder", "#27AE60"),
("2", "Controlled Cord\nTraction (CCT)", "Brandt-Andrews manoeuvre\nCounter-pressure on uterus\nUmbilical cord traction", "#007A87"),
("3", "Uterine\nMassage", "Fundal massage\nafter placenta delivered\n30-60 sec circular motion", "#1A3A5C"),
]
for i, (num, title, desc, col) in enumerate(steps):
x = 0.05 + i * 0.32
# Arrow between steps
if i > 0:
ax.annotate("", xy=(x - 0.02, 0.5),
xytext=(x - 0.07, 0.5),
arrowprops=dict(arrowstyle="-|>", color=col, lw=2.5),
xycoords="axes fraction", textcoords="axes fraction")
ax.add_patch(FancyBboxPatch((x, 0.10), 0.28, 0.78,
boxstyle="round,pad=0.03",
fc=col, ec="white", lw=2, alpha=0.9,
transform=ax.transAxes))
ax.add_patch(Circle((x + 0.14, 0.80), 0.065, fc="white", ec=col, lw=0,
transform=ax.transAxes))
ax.text(x + 0.14, 0.80, num, ha="center", va="center",
fontsize=14, fontweight="bold", color=col,
transform=ax.transAxes)
ax.text(x + 0.14, 0.67, title, ha="center", va="center",
fontsize=9, fontweight="bold", color="white", linespacing=1.4,
transform=ax.transAxes)
ax.text(x + 0.14, 0.38, desc, ha="center", va="center",
fontsize=8, color="white", linespacing=1.5,
transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
def img_fetal_positions():
fig, axes = plt.subplots(2, 4, figsize=(10, 4.5))
fig.patch.set_facecolor("#f0f4f8")
positions = [
("LOA", "#27AE60", "Left Occiput\nAnterior\n(Most common)"),
("ROA", "#007A87", "Right Occiput\nAnterior"),
("LOP", "#F5A623", "Left Occiput\nPosterior"),
("ROP", "#C0392B", "Right Occiput\nPosterior"),
("LOT", "#8E44AD", "Left Occiput\nTransverse"),
("ROT", "#2980B9", "Right Occiput\nTransverse"),
("LSA", "#16A085", "Left Sacro\nAnterior\n(Breech)"),
("RMA", "#E67E22", "Right Mento\nAnterior\n(Face)"),
]
for ax, (pos, col, desc) in zip(axes.flat, positions):
ax.set_xlim(-1, 1); ax.set_ylim(-1, 1.3); ax.set_aspect("equal"); ax.axis("off")
ax.set_facecolor("white")
# Maternal pelvis (circle)
ax.add_patch(Circle((0, 0), 0.85, fc="#FFDAB9", ec="#8B4513", lw=1.5, alpha=0.4))
# Fetal head
ax.add_patch(Circle((0, 0), 0.35, fc=col, ec="white", lw=1.5, alpha=0.85))
ax.text(0, 0, pos, ha="center", va="center",
fontsize=8, fontweight="bold", color="white")
ax.text(0, 1.1, desc, ha="center", va="center",
fontsize=6.5, color=col, linespacing=1.3, fontweight="bold")
fig.suptitle("Fetal Positions in Labour", fontsize=12,
fontweight="bold", color="#1A3A5C")
plt.tight_layout(pad=0.2)
return fig_to_stream(fig)
# ─────────────────────────────────────────────────────────────────────────────
# BUILD THE PRESENTATION
# ─────────────────────────────────────────────────────────────────────────────
def build_ppt():
prs = Presentation()
prs.slide_width = Inches(W_IN)
prs.slide_height = Inches(H_IN)
# ── TITLE SLIDE ──────────────────────────────────────────────────────────
sl = prs.slides.add_slide(prs.slide_layouts[6])
sw, sh = prs.slide_width, prs.slide_height
bg = sl.shapes.add_shape(1, 0, 0, sw, sh)
bg.fill.solid(); bg.fill.fore_color.rgb = NAVY; bg.line.fill.background()
grad = sl.shapes.add_shape(1, 0, Inches(3.5), sw, sh - Inches(3.5))
grad.fill.solid(); grad.fill.fore_color.rgb = RGBColor(0x0D, 0x24, 0x3B)
grad.line.fill.background()
stripe1 = sl.shapes.add_shape(1, 0, Inches(3.2), sw, Inches(0.12))
stripe1.fill.solid(); stripe1.fill.fore_color.rgb = GOLD; stripe1.line.fill.background()
tx_main = sl.shapes.add_textbox(Inches(0.8), Inches(0.8), Inches(11.7), Inches(2.2))
tf = tx_main.text_frame; tf.word_wrap = True
p = tf.paragraphs[0]; p.text = "NORMAL LABOUR"
p.font.bold = True; p.font.size = Pt(54); p.font.color.rgb = WHITE
p.alignment = PP_ALIGN.CENTER
tx_sub = sl.shapes.add_textbox(Inches(0.8), Inches(2.9), Inches(11.7), Inches(0.8))
tf2 = tx_sub.text_frame
p2 = tf2.paragraphs[0]
p2.text = "Mechanisms, Management & Clinical Essentials"
p2.font.size = Pt(22); p2.font.color.rgb = GOLD; p2.font.bold = True
p2.alignment = PP_ALIGN.CENTER
refs = sl.shapes.add_textbox(Inches(0.8), Inches(3.5), Inches(11.7), Inches(0.7))
tf3 = refs.text_frame
p3 = tf3.paragraphs[0]
p3.text = "Based on: Oxorn-Foote Human Labour & Birth | D.C. Dutta's Textbook of Obstetrics"
p3.font.size = Pt(16); p3.font.color.rgb = RGBColor(0xAA, 0xCC, 0xFF)
p3.alignment = PP_ALIGN.CENTER
bullets_title = [
"• Definition & Stages of Labour",
"• Uterine Contractions & Cervical Changes",
"• Fetal Skull & Pelvis",
"• Cardinal Movements (Mechanism of Labour)",
"• Partograph & Monitoring",
"• Management of Each Stage",
"• Episiotomy & Perineal Repair",
"• Active Management of Third Stage",
"• APGAR Score & Newborn Care",
"• Complications & Prevention",
]
tx_bullets = sl.shapes.add_textbox(Inches(1.5), Inches(4.2), Inches(10.3), Inches(3.0))
tf4 = tx_bullets.text_frame; tf4.word_wrap = True
for i in range(0, len(bullets_title), 2):
p = tf4.paragraphs[0] if i == 0 else tf4.add_paragraph()
# Put 2 items side by side in one paragraph
line = bullets_title[i]
if i + 1 < len(bullets_title):
line += " " + bullets_title[i+1]
p.text = line
p.font.size = Pt(13); p.font.color.rgb = RGBColor(0xCC, 0xE0, 0xFF)
print("Slide 1 (Title) done")
# ── SLIDE 2: Definition & Stages ─────────────────────────────────────────
sl2 = add_slide(prs, "Definition & Stages of Normal Labour",
"Oxorn-Foote Ch.1 | DC Dutta Ch.11")
bullets = [
"DEFINITION (DC Dutta):",
" Labour is the process by which the products of conception are expelled from",
" the uterus after the period of viability (28 weeks / 1000 g).",
"",
"NORMAL LABOUR (Eutocia):",
" • Spontaneous onset at term (37–42 weeks)",
" • Single fetus in vertex presentation",
" • Delivered within 18 hrs (primipara) or 12 hrs (multipara)",
" • No complications to mother or baby",
"",
"FOUR STAGES:",
" Stage I: Onset of labour → Full cervical dilation (10 cm)",
" Stage II: Full dilation → Delivery of baby",
" Stage III: Delivery of baby → Expulsion of placenta & membranes",
" Stage IV: 1–2 hours postpartum (recovery)",
]
add_bullets(sl2, bullets, 0.35, 1.55, 6.8, 5.7, font_size=15)
insert_image(sl2, img_definition_timeline(), 7.2, 1.55, 5.8, 3.5)
insert_image(sl2, img_stages_of_labour(), 7.2, 5.05, 5.8, 2.35)
print("Slide 2 done")
# ── SLIDE 3: True vs False Labour ─────────────────────────────────────────
sl3 = add_slide(prs, "True Labour vs False Labour (Braxton-Hicks)",
"DC Dutta Ch.11 | Oxorn-Foote Ch.1")
bullets3 = [
"CAUSES OF LABOUR ONSET:",
" • Progesterone withdrawal theory",
" • Oxytocin surge (increased receptors at term)",
" • Prostaglandin release (arachidonic acid)",
" • Fetal cortisol → CRH → prostaglandins",
" • Stretch theory — uterine overdistension",
"",
"CLINICAL DISTINCTION:",
" • Always perform vaginal exam to confirm",
" • Serial exams 1–2 hrs apart if uncertain",
" • Cervical change is the gold standard",
]
add_bullets(sl3, bullets3, 0.35, 1.55, 6.5, 5.7, font_size=15)
insert_image(sl3, img_true_vs_false(), 7.0, 1.55, 6.0, 5.7)
print("Slide 3 done")
# ── SLIDE 4: Uterine Contractions ─────────────────────────────────────────
sl4 = add_slide(prs, "Uterine Contractions in Labour",
"Oxorn-Foote Ch.2 | DC Dutta Ch.11")
bullets4 = [
"PROPERTIES (Oxorn-Foote):",
" • Fundal dominance — strongest at fundus",
" • Triple descending gradient",
" • Polarity — fundus contracts down",
" • Retraction — fibres shorten permanently",
"",
"NORMAL PARAMETERS:",
" Frequency: 3–5 per 10 minutes",
" Duration: 45–60 seconds",
" Intensity: 40–60 mmHg (active phase)",
" Baseline: 8–12 mmHg resting tone",
"",
"ACTIVE PHASE CRITERIA:",
" • Regular contractions ≥ 3 in 10 min",
" • Cervix ≥ 4 cm dilated",
" • Cervix effaced ≥ 50%",
"",
"PAIN PATHWAY:",
" • T10–L1 (1st stage): visceral pain",
" • S2–S4 (2nd stage): somatic pain",
]
add_bullets(sl4, bullets4, 0.35, 1.55, 6.5, 5.7, font_size=14)
insert_image(sl4, img_uterine_contractions(), 7.0, 1.6, 6.0, 3.2)
insert_image(sl4, img_cervical_dilation(), 7.0, 4.85, 6.0, 2.5)
print("Slide 4 done")
# ── SLIDE 5: Fetal Skull ───────────────────────────────────────────────────
sl5 = add_slide(prs, "Fetal Skull — Anatomy, Sutures & Diameters",
"DC Dutta Ch.2 | Oxorn-Foote Ch.3")
bullets5 = [
"BONES: 2 Frontal, 2 Parietal, 2 Temporal, 1 Occipital, 1 Sphenoid",
"",
"SUTURES:",
" Sagittal: between 2 parietal bones (L–R)",
" Coronal: frontal & parietal (transverse)",
" Lambdoid: parietal & occipital (posterior)",
" Frontal: between 2 frontal bones",
"",
"FONTANELLES:",
" Anterior (Bregma): diamond-shaped, closes 18 months",
" Posterior (Lambda): triangular, closes 6–8 weeks",
"",
"IMPORTANT LANDMARKS:",
" Nasion, Glabella, Sinciput, Bregma,",
" Vertex, Lambda, Occiput, Mentum",
]
add_bullets(sl5, bullets5, 0.35, 1.55, 6.5, 5.7, font_size=14)
insert_image(sl5, img_fetal_skull(), 7.0, 1.55, 6.0, 5.7)
print("Slide 5 done")
# ── SLIDE 6: Pelvis ───────────────────────────────────────────────────────
sl6 = add_slide(prs, "Female Pelvis — Types & Dimensions",
"DC Dutta Ch.3 | Oxorn-Foote Ch.4")
bullets6 = [
"PELVIC PLANES:",
" Inlet (Brim): AP 11 cm, Transverse 13 cm, Oblique 12 cm",
" Cavity: AP 12 cm, Transverse 12 cm (round)",
" Outlet: AP 13 cm, Transverse 11 cm",
"",
"PELVIC LANDMARKS:",
" • Ischial spines — station reference (0 station)",
" • Sacral promontory — obstetric conjugate",
" • Symphysis pubis",
"",
"STATION:",
" -5 to -1: above ischial spines",
" 0: at ischial spines (engaged)",
" +1 to +5: below ischial spines",
"",
"ENGAGEMENT:",
" Biparietal diameter below pelvic inlet",
" Primip: 36 wks | Multip: onset of labour",
]
add_bullets(sl6, bullets6, 0.35, 1.55, 6.5, 5.7, font_size=14)
insert_image(sl6, img_pelvis_types(), 7.0, 1.55, 6.0, 5.7)
print("Slide 6 done")
# ── SLIDE 7: Cardinal Movements ───────────────────────────────────────────
sl7 = add_slide(prs, "Cardinal Movements (Mechanism) of Normal Labour",
"Oxorn-Foote Ch.6 | DC Dutta Ch.12")
bullets7 = [
"DEFINITION:",
" Positional changes of fetus during descent",
" through birth canal to navigate the pelvis",
"",
"LOA (Left Occipito-Anterior) — Most Common",
"",
"SEQUENCE:",
" 1. ENGAGEMENT — BPD enters pelvic brim",
" 2. DESCENT — continuous throughout",
" 3. FLEXION — chin meets chest (9.5 cm)",
" 4. INT. ROTATION — occiput → pubic symphysis",
" 5. EXTENSION — head delivered under arch",
" 6. RESTITUTION — 45° return to oblique",
" 7. EXT. ROTATION — shoulders align AP",
" 8. EXPULSION — anterior then posterior",
]
add_bullets(sl7, bullets7, 0.35, 1.55, 5.8, 5.7, font_size=14)
insert_image(sl7, img_cardinal_movements(), 6.2, 1.55, 6.8, 5.7)
print("Slide 7 done")
# ── SLIDE 8: Mechanism Detail ──────────────────────────────────────────────
sl8 = add_slide(prs, "Step-by-Step Mechanism — LOA Presentation",
"Oxorn-Foote Ch.6 | DC Dutta Ch.12")
bullets8 = [
"ENGAGEMENT:",
" Head enters at left oblique diameter",
" Sagittal suture in left oblique of inlet",
"",
"INTERNAL ROTATION:",
" Occiput rotates from left to OA (1/8 turn)",
" Sagittal suture now in AP diameter",
" Aided by: pelvic floor muscles (levator ani)",
"",
"EXTENSION:",
" Occiput under pubic symphysis = pivot point",
" Head extended: brow, face, chin born",
" Diameter = suboccipito-bregmatic (9.5 cm)",
"",
"RESTITUTION & EXT. ROTATION:",
" Head rotates 45° back to original oblique",
" Then shoulders rotate into AP: LSA → LOA → OA",
"",
"EXPULSION:",
" Anterior shoulder then posterior shoulder",
" Rest of body follows easily",
]
add_bullets(sl8, bullets8, 0.35, 1.55, 6.5, 5.7, font_size=13)
insert_image(sl8, img_mechanism_occiput_anterior(), 7.0, 1.55, 6.0, 5.7)
print("Slide 8 done")
# ── SLIDE 9: Fetal Positions ───────────────────────────────────────────────
sl9 = add_slide(prs, "Fetal Lie, Presentation, Position & Attitude",
"DC Dutta Ch.11 | Oxorn-Foote Ch.5")
bullets9 = [
"LIE: Longitudinal (99.5%), Transverse, Oblique",
"",
"PRESENTATION:",
" Vertex (96%), Breech (3%), Face, Brow, Shoulder",
"",
"POSITION — relation of denominator to maternal pelvis:",
" Vertex → Occiput",
" Face → Mentum (chin)",
" Breech → Sacrum",
" Shoulder → Acromion",
"",
"ATTITUDE — relation of fetal parts to each other:",
" Full flexion (best), Deflexion, Extension",
"",
"DENOMINATOR positions:",
" OA, OP, OT (L or R), with LOA most common",
]
add_bullets(sl9, bullets9, 0.35, 1.55, 6.5, 5.7, font_size=14)
insert_image(sl9, img_fetal_positions(), 7.0, 1.55, 6.0, 5.7)
print("Slide 9 done")
# ── SLIDE 10: Partograph ──────────────────────────────────────────────────
sl10 = add_slide(prs, "Partograph — WHO Labour Monitoring Tool",
"DC Dutta Ch.13 | Oxorn-Foote Ch.8")
bullets10 = [
"DEFINITION: Graphical record of labour progress",
" Introduced by Philpott & Castle (1972)",
" Modified WHO partograph (1994)",
"",
"COMPONENTS:",
" 1. Cervicograph — dilation vs time",
" 2. Alert line — 1 cm/hr from 4 cm",
" 3. Action line — 4 hrs to right of alert",
" 4. Fetal heart rate (every 30 min)",
" 5. Liquor, moulding, caput",
" 6. Contractions per 10 min",
" 7. Oxytocin, drugs, IVF",
" 8. Maternal vitals, urine output",
"",
"ACTION:",
" Left of alert = normal",
" Alert–Action zone = increased surveillance",
" Beyond action line = intervention needed",
]
add_bullets(sl10, bullets10, 0.35, 1.55, 6.5, 5.7, font_size=14)
insert_image(sl10, img_partograph(), 7.0, 1.55, 6.0, 5.7)
print("Slide 10 done")
# ── SLIDE 11: Management Overview ────────────────────────────────────────
sl11 = add_slide(prs, "Management of Normal Labour — Overview",
"DC Dutta Ch.13 | Oxorn-Foote Ch.8")
bullets11 = [
"ON ADMISSION:",
" History: GA, GBS status, antenatal records",
" Examination: fundal height, presentation, FHR",
" Vaginal exam: dilation, effacement, station",
" Investigations: CBC, urine, Group & screen",
"",
"GENERAL CARE:",
" • Supportive environment (doula/birth partner)",
" • Oral intake: sips of clear fluids (active labour)",
" • IV access if high risk",
" • Bladder care — void every 2 hrs",
" • Ambulation encouraged in 1st stage",
" • Pain relief: epidural, opioids, N₂O",
"",
"MONITORING:",
" • FHR: q30 min (latent), q15 min (active), q5 min (2nd stage)",
" • Contractions: frequency, duration, strength",
" • Vaginal exam: every 4 hrs (or if concern)",
" • Maternal vitals: BP, pulse, temperature",
]
add_bullets(sl11, bullets11, 0.35, 1.55, 6.5, 5.7, font_size=13)
insert_image(sl11, img_management_summary(), 7.0, 1.55, 6.0, 5.7)
print("Slide 11 done")
# ── SLIDE 12: First Stage ──────────────────────────────────────────────────
sl12 = add_slide(prs, "First Stage of Labour — Management",
"DC Dutta Ch.13 | Oxorn-Foote Ch.8")
bullets12 = [
"PHASES OF FIRST STAGE:",
"",
" LATENT PHASE (0–3 cm):",
" • Cervix effaces, slowly dilates to 3 cm",
" • Duration: ≤ 8 hrs (primip), ≤ 4 hrs (multip)",
" • Irregular, mild contractions",
"",
" ACTIVE PHASE (4–10 cm):",
" • Rate: ≥ 1 cm/hr (WHO 2018: active phase starts at 5 cm)",
" • Regular, stronger contractions 3–5/10 min",
" • Mucus show and rupture of membranes",
"",
" TRANSITION (8–10 cm):",
" • Most intense contractions",
" • Urge to push may develop",
"",
"NORMAL DURATION:",
" Primipara: 12–18 hours total 1st stage",
" Multipara: 6–12 hours total 1st stage",
"",
"PROLONGED FIRST STAGE:",
" Arrest: no progress for ≥ 2 hrs in active phase",
]
add_bullets(sl12, bullets12, 0.35, 1.55, 6.5, 5.7, font_size=13)
# Friedman curve
fig_f, ax_f = plt.subplots(figsize=(5.5, 4))
fig_f.patch.set_facecolor("#f0f4f8"); ax_f.set_facecolor("#f0f4f8")
hrs_p = [0, 2, 4, 6, 8, 10, 12, 14]
dil_p = [0, 1, 2, 3, 5, 7, 9, 10]
hrs_m = [0, 1, 2, 3, 4, 5, 6]
dil_m = [0, 2, 4, 6, 8, 9.5, 10]
ax_f.plot(hrs_p, dil_p, "b-o", lw=2.5, ms=6, label="Primipara (Friedman)")
ax_f.plot(hrs_m, dil_m, "g-s", lw=2.5, ms=6, label="Multipara")
ax_f.axhline(10, color="#C0392B", ls="--", lw=1.5, label="Full dilation (10 cm)")
ax_f.axvline(4, color="#8E44AD", ls=":", lw=1.5, label="Active phase start")
ax_f.fill_betweenx([0, 3], 0, 6, alpha=0.08, color="orange", label="Latent phase")
ax_f.fill_betweenx([3, 10], 4, 14, alpha=0.06, color="blue", label="Active phase")
ax_f.set_xlabel("Time (hours)", fontsize=10); ax_f.set_ylabel("Cervical Dilation (cm)", fontsize=10)
ax_f.set_title("Friedman Labour Curve", fontsize=11, fontweight="bold", color="#1A3A5C")
ax_f.legend(fontsize=7, loc="upper left"); ax_f.grid(True, alpha=0.3)
ax_f.set_ylim(0, 11); ax_f.set_xlim(0, 15)
plt.tight_layout()
insert_image(sl12, fig_to_stream(fig_f), 7.0, 1.55, 6.0, 5.7)
print("Slide 12 done")
# ── SLIDE 13: Second Stage ──────────────────────────────────────────────────
sl13 = add_slide(prs, "Second Stage of Labour — Delivery of Baby",
"DC Dutta Ch.14 | Oxorn-Foote Ch.9")
bullets13 = [
"DEFINITION: Full cervical dilation → delivery of baby",
"",
"DIAGNOSIS:",
" • Irresistible urge to push (Ferguson reflex)",
" • Perineal bulging, anal dilatation",
" • Presenting part visible",
"",
"DURATION:",
" Primipara: up to 2 hours (3 hrs with epidural)",
" Multipara: up to 1 hour (2 hrs with epidural)",
"",
"MANAGEMENT:",
" • Encourage pushing with contractions",
" • Valsalva technique: closed-glottis pushing",
" • Support perineum — modified Ritgen manoeuvre",
" • Control delivery of head — 'guard perineum'",
" • Check for cord around neck",
" • Oxytocin 10 IU IM on anterior shoulder",
" • Clamp and cut cord after 1–3 min (delayed)",
"",
"CROWNING:",
" BPD at pelvic outlet — maximum distension",
]
add_bullets(sl13, bullets13, 0.35, 1.55, 6.5, 5.7, font_size=13)
# Delivery stages diagram
fig_d, axes_d = plt.subplots(1, 3, figsize=(6, 3.5))
fig_d.patch.set_facecolor("#f0f4f8")
del_stages = [
("Crowning\n& Head\nDelivery", "#1A3A5C"),
("Restitution\n& External\nRotation", "#007A87"),
("Shoulder &\nBody\nExpulsion", "#27AE60"),
]
for dax, (lbl, col) in zip(axes_d, del_stages):
dax.set_xlim(0, 1); dax.set_ylim(0, 1); dax.axis("off")
dax.set_facecolor("#f0f4f8")
dax.add_patch(FancyBboxPatch((0.05, 0.08), 0.90, 0.82,
boxstyle="round,pad=0.04",
fc=col, ec="white", lw=2, alpha=0.88))
dax.add_patch(Circle((0.5, 0.62), 0.22, fc="white", ec=col, lw=0))
dax.text(0.5, 0.62, "👶", ha="center", va="center", fontsize=18)
dax.text(0.5, 0.28, lbl, ha="center", va="center", fontsize=9,
fontweight="bold", color="white", linespacing=1.4)
fig_d.suptitle("Sequence of Delivery", fontsize=10, fontweight="bold", color="#1A3A5C")
plt.tight_layout()
insert_image(sl13, fig_to_stream(fig_d), 7.0, 1.55, 6.0, 5.7)
print("Slide 13 done")
# ── SLIDE 14: Third Stage ──────────────────────────────────────────────────
sl14 = add_slide(prs, "Third Stage of Labour — Placental Delivery",
"DC Dutta Ch.15 | Oxorn-Foote Ch.10")
bullets14 = [
"DEFINITION: Delivery of baby → Expulsion of placenta",
"DURATION: Normally 5–30 minutes",
"",
"PLACENTAL SEPARATION:",
" • Retroplacental haematoma forms",
" • Duncan mechanism: edge first (marginal sinus)",
" • Schultze mechanism: centre first (most common)",
"",
"SIGNS OF SEPARATION:",
" • Uterus rises, becomes globular & firm",
" • Gush of blood (retroplacental clot expelled)",
" • Umbilical cord lengthens (Allis sign / Strassman)",
" • Placenta sinks into lower segment",
"",
"ACTIVE MANAGEMENT (AMTSL):",
" 1. Oxytocin 10 IU IM (within 1 min of delivery)",
" 2. Controlled cord traction (CCT)",
" 3. Uterine massage after delivery",
"",
"INSPECTION OF PLACENTA:",
" • Completeness of cotyledons & membranes",
" • Number of cord vessels (2 arteries, 1 vein)",
" • Abnormal lobes (succenturiate)",
]
add_bullets(sl14, bullets14, 0.35, 1.55, 6.5, 5.7, font_size=13)
insert_image(sl14, img_active_management(), 7.0, 1.55, 6.0, 3.5)
insert_image(sl14, img_oxytocin_uterotonic(), 7.0, 5.1, 6.0, 2.2)
print("Slide 14 done")
# ── SLIDE 15: Episiotomy ──────────────────────────────────────────────────
sl15 = add_slide(prs, "Episiotomy — Indications, Types & Repair",
"DC Dutta Ch.16 | Oxorn-Foote Ch.11")
bullets15 = [
"DEFINITION: Surgical incision of perineum to enlarge",
" the vaginal outlet during delivery",
"",
"INDICATIONS:",
" • Imminent perineal tear",
" • Large baby / shoulder dystocia",
" • Instrumental delivery (forceps/vacuum)",
" • Fetal distress needing rapid delivery",
" • Preterm delivery (protection from trauma)",
"",
"TYPES:",
" Midline (median): along midline raphe",
" Mediolateral: 45° angle (right preferred)",
" Lateral: rarely used",
" J-shaped: combines both",
"",
"REPAIR (Layers):",
" 1. Vaginal mucosa — continuous 2-0 Vicryl",
" 2. Deep perineal muscles — interrupted",
" 3. Superficial perineal muscles",
" 4. Skin — subcuticular or interrupted",
]
add_bullets(sl15, bullets15, 0.35, 1.55, 6.5, 5.7, font_size=13)
insert_image(sl15, img_episiotomy(), 7.0, 1.55, 6.0, 5.7)
print("Slide 15 done")
# ── SLIDE 16: APGAR Score ──────────────────────────────────────────────────
sl16 = add_slide(prs, "APGAR Score & Immediate Newborn Care",
"DC Dutta Ch.18 | Oxorn-Foote Ch.12")
bullets16 = [
"APGAR SCORE (Virginia Apgar, 1952):",
" Assessed at 1 minute and 5 minutes of life",
" A = Appearance (colour)",
" P = Pulse (heart rate)",
" G = Grimace (reflex irritability)",
" A = Activity (muscle tone)",
" R = Respiration",
"",
"INTERPRETATION:",
" 8–10 = Normal (routine care)",
" 4–7 = Moderate depression (stimulate, O₂)",
" 0–3 = Severe (immediate resuscitation)",
"",
"IMMEDIATE NEWBORN CARE:",
" • Dry and stimulate (first 30 seconds)",
" • Clear airway if meconium",
" • Warmth — radiant warmer",
" • Vitamin K 1 mg IM",
" • Eye prophylaxis",
" • Delayed cord clamping 1–3 minutes",
" • Skin-to-skin (kangaroo care)",
" • Initiate breastfeeding within 1 hour",
]
add_bullets(sl16, bullets16, 0.35, 1.55, 6.5, 5.7, font_size=13)
insert_image(sl16, img_newborn_apgar(), 7.0, 1.55, 6.0, 5.7)
print("Slide 16 done")
# ── SLIDE 17: Complications ───────────────────────────────────────────────
sl17 = add_slide(prs, "Complications of Normal Labour",
"DC Dutta Ch.22-25 | Oxorn-Foote Ch.16-20")
bullets17 = [
"MATERNAL:",
" • Prolonged labour (>18h primip, >12h multip)",
" • PPH (primary <24h, secondary 24h–12 wks)",
" • Uterine rupture — scar dehiscence",
" • Perineal tears (1st–4th degree)",
" • Obstetric fistula (VVF, RVF)",
" • Infection / sepsis",
"",
"FETAL/NEONATAL:",
" • Fetal distress / birth asphyxia",
" • Shoulder dystocia (Erb's palsy, fractures)",
" • Cord prolapse / compression",
" • Meconium aspiration",
" • Intracranial haemorrhage",
"",
"PREVENTION:",
" • WHO Safe Childbirth Checklist",
" • Skilled birth attendance",
" • Partograph monitoring",
" • AMTSL for all deliveries",
]
add_bullets(sl17, bullets17, 0.35, 1.55, 6.5, 5.7, font_size=13)
insert_image(sl17, img_complications(), 7.0, 1.55, 6.0, 5.7)
print("Slide 17 done")
# ── SLIDE 18: Key Points Summary ──────────────────────────────────────────
sl18 = add_slide(prs, "Key Examination Points — Normal Labour",
"High-Yield Summary")
fig_kp, ax_kp = plt.subplots(figsize=(8.5, 5.5))
fig_kp.patch.set_facecolor("#f0f4f8"); ax_kp.set_facecolor("#f0f4f8"); ax_kp.axis("off")
boxes = [
(0.02, 0.66, "#1A3A5C", "DEFINITIONS",
["Normal labour: Spontaneous, vertex, term, ≤18h, no complications",
"Engagement = BPD below pelvic inlet",
"Station = presenting part vs ischial spines"]),
(0.52, 0.66, "#007A87", "STAGES & DURATION",
["Stage I: 12h primip / 6h multip",
"Stage II: 1h primip / 30min multip",
"Stage III: 5–30 minutes; Stage IV: 2h"]),
(0.02, 0.32, "#27AE60", "MECHANISM (LOA)",
["Engagement → Descent → Flexion",
"→ Internal rotation → Extension",
"→ Restitution → Ext. rotation → Expulsion"]),
(0.52, 0.32, "#C0392B", "AMTSL (3 Steps)",
["1. Oxytocin 10 IU IM on ant. shoulder",
"2. Controlled cord traction (CCT)",
"3. Uterine massage after placenta"]),
(0.02, 0.01, "#8E44AD", "MONITORING",
["Partograph: alert (1 cm/hr) + action line",
"FHR q30min (latent), q15min (active)",
"VE every 4 hrs"]),
(0.52, 0.01, "#F5A623", "APGAR & NEWBORN",
["Score at 1 min and 5 min",
"7–10 normal; 0–3 needs resuscitation",
"Vit K, delayed cord clamp, skin-to-skin"]),
]
for x, y, col, title, items in boxes:
ax_kp.add_patch(FancyBboxPatch((x, y), 0.47, 0.30,
boxstyle="round,pad=0.02",
fc=col, ec="white", lw=1.5, alpha=0.9,
transform=ax_kp.transAxes))
ax_kp.text(x + 0.235, y + 0.26, title, ha="center", va="top",
fontsize=10, fontweight="bold", color="white",
transform=ax_kp.transAxes)
for i, item in enumerate(items):
ax_kp.text(x + 0.025, y + 0.18 - i * 0.082, f"• {item}",
ha="left", va="top", fontsize=8, color="white",
transform=ax_kp.transAxes)
plt.tight_layout()
insert_image(sl18, fig_to_stream(fig_kp), 0.25, 1.5, 12.8, 5.8)
print("Slide 18 done")
# ── SLIDE 19: Thank You / References ─────────────────────────────────────
sl19 = prs.slides.add_slide(prs.slide_layouts[6])
sw, sh = prs.slide_width, prs.slide_height
bg19 = sl19.shapes.add_shape(1, 0, 0, sw, sh)
bg19.fill.solid(); bg19.fill.fore_color.rgb = NAVY; bg19.line.fill.background()
stripe19 = sl19.shapes.add_shape(1, 0, Inches(3.8), sw, Inches(0.1))
stripe19.fill.solid(); stripe19.fill.fore_color.rgb = GOLD; stripe19.line.fill.background()
ty = sl19.shapes.add_textbox(Inches(0.5), Inches(0.8), Inches(12.3), Inches(2.5))
tf_ty = ty.text_frame
p_ty = tf_ty.paragraphs[0]
p_ty.text = "Thank You"
p_ty.font.size = Pt(52); p_ty.font.bold = True; p_ty.font.color.rgb = WHITE
p_ty.alignment = PP_ALIGN.CENTER
sub19 = sl19.shapes.add_textbox(Inches(0.5), Inches(2.7), Inches(12.3), Inches(0.9))
tf_s = sub19.text_frame
p_s = tf_s.paragraphs[0]
p_s.text = "Normal Labour — Mechanisms, Management & Clinical Essentials"
p_s.font.size = Pt(20); p_s.font.color.rgb = GOLD; p_s.font.bold = True
p_s.alignment = PP_ALIGN.CENTER
refs19 = sl19.shapes.add_textbox(Inches(1.5), Inches(4.0), Inches(10.3), Inches(3.0))
tf_r = refs19.text_frame; tf_r.word_wrap = True
ref_lines = [
"REFERENCES",
"1. Oxorn H, Foote WR. Human Labor and Birth, 5th ed. Appleton-Century-Crofts.",
"2. Dutta DC. Textbook of Obstetrics, 9th ed. New Central Book Agency.",
"3. WHO Recommendations: Intrapartum Care for a Positive Childbirth Experience (2018).",
"4. FIGO/ICM Active Management of Third Stage of Labour Guidelines.",
"5. RCOG Green-top Guideline: Management of Third- and Fourth-Degree Perineal Tears.",
]
for i, line in enumerate(ref_lines):
p = tf_r.paragraphs[0] if i == 0 else tf_r.add_paragraph()
p.text = line
p.font.size = Pt(14 if i == 0 else 12)
p.font.color.rgb = RGBColor(0xCC, 0xE0, 0xFF)
p.font.bold = (i == 0)
p.space_after = Pt(3)
print("Slide 19 (Thank You) done")
# ── SAVE ─────────────────────────────────────────────────────────────────
out_path = OUT / "Normal_Labour.pptx"
prs.save(str(out_path))
print(f"\n✅ Presentation saved to: {out_path}")
return str(out_path)
if __name__ == "__main__":
path = build_ppt()
print(f"File size: {os.path.getsize(path) / 1024:.1f} KB")
cd /tmp/workspace/normal-labour-ppt && python3 build_ppt.py 2>&1
Normal Labour
Presentation · PPTX
| # | Slide | Custom Diagram / Image |
|---|---|---|
| 1 | Title Slide | Topic list with navy/gold theme |
| 2 | Definition & Stages | Timeline bar + 4-stage colour blocks |
| 3 | True vs False Labour | Side-by-side comparison cards |
| 4 | Uterine Contractions | Intrauterine pressure waveform + cervical dilation circles |
| 5 | Fetal Skull | Annotated skull diagram + diameters table |
| 6 | Pelvis Types | Caldwell-Moloy 4 types (Gynaecoid/Android/Anthropoid/Platypelloid) |
| 7 | Cardinal Movements | 8-cell illustrated grid with icons |
| 8 | LOA Mechanism Step-by-Step | 5-step delivery sequence diagram |
| 9 | Fetal Lie/Position | 8-position grid (LOA, ROA, LOP, ROP, etc.) |
| 10 | Partograph | WHO cervicograph + FHR monitoring chart |
| 11 | Management Overview | 4-column stage management boxes |
| 12 | First Stage | Friedman labour curve (primip vs multip) |
| 13 | Second Stage | Delivery sequence with crowning → expulsion |
| 14 | Third Stage | AMTSL 3-step diagram + uterotonic table |
| 15 | Episiotomy | Perineum schematic + midline vs mediolateral table |
| 16 | APGAR Score | Full APGAR table with colour-coded scoring |
| 17 | Complications | Maternal vs Fetal two-column layout |
| 18 | Key Exam Points | 6-box high-yield summary grid |
| 19 | Thank You / References | Full reference list (Oxorn-Foote, DC Dutta, WHO 2018) |
Can we add video in this ppt showing mechanism of labour step wise
mechanism.mp4Ppt on normal labour from any standard source with pictures in it
python /tmp/skills/shared/scripts/fetch_images.py \ "https://cdn.orris.care/cdss_images/301413f4f35165b4beae6977a8707abf2e1907c3c68db33d90d73fd6d99cc149.png" \ "https://cdn.orris.care/cdss_images/fc91d7d5e71e4f60c6da434116a8babc61de461999dacbbc30eec457e2bec1d2.png" \ "https://cdn.orris.care/cdss_images/083c770538011e907f4d95e79940098bd93a9fde3a1adcf6c6f9b51207df8ae1.png" \ "https://cdn.orris.care/cdss_images/9d44afe1c71dee9e7c2ea234d9c63df3119717fb4bdb52f11de14dcf0c631b0a.png" \ 2>&1 | python3 -c "import sys,json; d=json.load(sys.stdin); [print(i['url'][:60], 'OK' if i['base64'] else 'FAIL', i.get('error','')) for i in d]"
~/normal-labour-ppt/build_v2.py
"""
Normal Labour PPT v2 — with REAL textbook images from medical library
Sources: Creasy & Resnik, Family Medicine (Textbook), Tintinalli's EM, Roberts & Hedges
"""
import os, io, json, subprocess, base64, math
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, Arc, Circle
import numpy as np
from pathlib import Path
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
OUT = Path("/tmp/workspace/normal-labour-ppt")
OUT.mkdir(parents=True, exist_ok=True)
PPTX_PATH = OUT / "Normal_Labour_v2.pptx"
# ── Colour palette ─────────────────────────────────────────────────────
NAVY = RGBColor(0x1A, 0x3A, 0x5C)
TEAL = RGBColor(0x00, 0x7A, 0x87)
GOLD = RGBColor(0xF5, 0xA6, 0x23)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
LGRAY = RGBColor(0xF0, 0xF4, 0xF8)
DKGRAY = RGBColor(0x33, 0x33, 0x33)
RED = RGBColor(0xC0, 0x39, 0x2B)
GREEN = RGBColor(0x27, 0xAE, 0x60)
W_IN, H_IN = 13.333, 7.5
# ── Fetch images via skill helper ───────────────────────────────────────
IMAGE_URLS = {
"labour_curve": "https://cdn.orris.care/cdss_images/301413f4f35165b4beae6977a8707abf2e1907c3c68db33d90d73fd6d99cc149.png",
"composite_curves": "https://cdn.orris.care/cdss_images/fc91d7d5e71e4f60c6da434116a8babc61de461999dacbbc30eec457e2bec1d2.png",
"cardinal_movements": "https://cdn.orris.care/cdss_images/083c770538011e907f4d95e79940098bd93a9fde3a1adcf6c6f9b51207df8ae1.png",
"shoulder_dystocia": "https://cdn.orris.care/cdss_images/9d44afe1c71dee9e7c2ea234d9c63df3119717fb4bdb52f11de14dcf0c631b0a.png",
}
print("Fetching textbook images...")
result = json.loads(subprocess.check_output(
["python", "/tmp/skills/shared/scripts/fetch_images.py"] + list(IMAGE_URLS.values())
))
IMAGES = {}
for key, item in zip(IMAGE_URLS.keys(), result):
if item["base64"]:
raw = base64.b64decode(item["base64"].split(",", 1)[1])
IMAGES[key] = io.BytesIO(raw)
print(f" ✓ {key}")
else:
print(f" ✗ {key}: {item.get('error')}")
def get_img(key):
buf = IMAGES[key]
buf.seek(0)
return buf
# ── Matplotlib helpers ──────────────────────────────────────────────────
def fig_to_stream(fig):
buf = io.BytesIO()
fig.savefig(buf, format="png", bbox_inches="tight", dpi=130)
buf.seek(0)
plt.close(fig)
return buf
def add_slide(prs, title_text, subtitle_text=""):
slide = prs.slides.add_slide(prs.slide_layouts[6])
sw, sh = prs.slide_width, prs.slide_height
# BG
bg = slide.shapes.add_shape(1, 0, 0, sw, sh)
bg.fill.solid(); bg.fill.fore_color.rgb = LGRAY; bg.line.fill.background()
# Header band
hb = slide.shapes.add_shape(1, 0, 0, sw, Inches(1.3))
hb.fill.solid(); hb.fill.fore_color.rgb = NAVY; hb.line.fill.background()
# Gold stripe
gs = slide.shapes.add_shape(1, 0, Inches(1.3), sw, Inches(0.07))
gs.fill.solid(); gs.fill.fore_color.rgb = GOLD; gs.line.fill.background()
# Title
tx = slide.shapes.add_textbox(Inches(0.3), Inches(0.1), Inches(12.7), Inches(0.95))
tf = tx.text_frame; tf.word_wrap = True
p = tf.paragraphs[0]; p.text = title_text
p.font.bold = True; p.font.size = Pt(30); p.font.color.rgb = WHITE
if subtitle_text:
tx2 = slide.shapes.add_textbox(Inches(0.3), Inches(0.95), Inches(12.7), Inches(0.38))
p2 = tx2.text_frame.paragraphs[0]
p2.text = subtitle_text
p2.font.size = Pt(14); p2.font.color.rgb = GOLD; p2.font.bold = True
return slide
def add_bullets(slide, bullets, left, top, width, height, font_size=15):
tx = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
tf = tx.text_frame; tf.word_wrap = True
for i, b in enumerate(bullets):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
indent = b.startswith(" ")
p.text = b.strip()
p.font.size = Pt(font_size - (1.5 if indent else 0))
p.font.color.rgb = DKGRAY
p.space_after = Pt(3)
p.level = 1 if indent else 0
def insert_img(slide, stream, left, top, width, height):
stream.seek(0)
slide.shapes.add_picture(stream, Inches(left), Inches(top), Inches(width), Inches(height))
def add_caption(slide, text, left, top, width):
tx = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(0.45))
tf = tx.text_frame; tf.word_wrap = True
p = tf.paragraphs[0]; p.text = text
p.font.size = Pt(9); p.font.italic = True; p.font.color.rgb = RGBColor(0x55,0x55,0x55)
p.alignment = PP_ALIGN.CENTER
# ── Generated images ────────────────────────────────────────────────────
def img_stages_banner():
fig, ax = plt.subplots(figsize=(8.5, 1.8))
fig.patch.set_facecolor("#f0f4f8"); ax.axis("off"); ax.set_facecolor("#f0f4f8")
stages = [
("STAGE I\nOnset → 10 cm", "#1A3A5C", "12h primip\n6h multip"),
("STAGE II\n10cm → Baby", "#007A87", "1h primip\n30min multip"),
("STAGE III\nBaby → Placenta", "#27AE60", "5–30 min"),
("STAGE IV\nRecovery", "#F5A623", "1–2 hours"),
]
for i, (lbl, col, dur) in enumerate(stages):
x = i * 0.25
ax.add_patch(FancyBboxPatch((x+0.005, 0.1), 0.24, 0.82,
boxstyle="round,pad=0.02", fc=col, ec="white", lw=1.5,
transform=ax.transAxes, alpha=0.92))
ax.text(x+0.125, 0.70, lbl, ha="center", va="center", fontsize=9,
fontweight="bold", color="white", linespacing=1.3,
transform=ax.transAxes)
ax.text(x+0.125, 0.30, dur, ha="center", va="center", fontsize=8.5,
color="white", linespacing=1.3, transform=ax.transAxes)
plt.tight_layout(pad=0)
return fig_to_stream(fig)
def img_true_false():
fig, axes = plt.subplots(1, 2, figsize=(8, 4.2))
fig.patch.set_facecolor("#f0f4f8")
data = [
("TRUE LABOUR", "#27AE60", [
"Regular, rhythmic contractions",
"Progressive — frequency & intensity ↑",
"Pain: lumbosacral → abdomen",
"Cervix dilates & effaces",
"Bloody show present",
"Walking intensifies contractions",
"Analgesics do NOT stop labour",
]),
("FALSE LABOUR\n(Braxton-Hicks)", "#C0392B", [
"Irregular contractions",
"No progressive change",
"Pain mainly lower abdomen",
"No cervical change",
"Show usually absent",
"Walking may relieve pain",
"Analgesics may stop contractions",
]),
]
for ax, (title, col, items) in zip(axes, data):
ax.set_xlim(0,1); ax.set_ylim(0,1); ax.axis("off"); ax.set_facecolor("#f0f4f8")
ax.add_patch(FancyBboxPatch((0.03,0.03),0.94,0.94, boxstyle="round,pad=0.03",
fc="white", ec=col, lw=2.5))
ax.text(0.5,0.92, title, ha="center", va="top", fontsize=11,
fontweight="bold", color=col, linespacing=1.3)
for i, item in enumerate(items):
ax.text(0.08, 0.80-i*0.107, f"• {item}", ha="left", va="top",
fontsize=8.2, color="#333")
fig.suptitle("True vs False Labour", fontsize=13, fontweight="bold",
color="#1A3A5C", y=1.01)
plt.tight_layout()
return fig_to_stream(fig)
def img_uterine_contractions():
fig, ax = plt.subplots(figsize=(7, 3.5))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8")
t = np.linspace(0, 10, 1000)
y = np.zeros_like(t)
for pk in [1.5, 4.0, 7.0]:
y += 65 * np.exp(-((t-pk)**2)/0.28)
y += 10; y = np.clip(y, 0, 100)
ax.fill_between(t, 10, y, where=(y>10), alpha=0.3, color="#007A87")
ax.plot(t, y, color="#1A3A5C", lw=2.5)
ax.axhline(10, color="#C0392B", lw=1.2, ls="--", label="Resting tone (10 mmHg)")
ax.axhline(25, color="#F5A623", lw=1.2, ls=":", label="Pain threshold (~25 mmHg)")
for pk, nm in [(1.5,"1st"),(4.0,"2nd"),(7.0,"3rd")]:
ax.annotate(f"{nm}\n~60–80 mmHg", xy=(pk,75), xytext=(pk+0.5,95),
arrowprops=dict(arrowstyle="->", color="#1A3A5C"),
fontsize=7.5, color="#1A3A5C")
ax.set_xlabel("Time (min)", fontsize=10); ax.set_ylabel("IUP (mmHg)", fontsize=10)
ax.set_title("Uterine Contractions in Active Labour\n(3–5/10 min, 45–60 sec, 40–60 mmHg)",
fontsize=10, fontweight="bold", color="#1A3A5C")
ax.legend(fontsize=8, loc="upper right"); ax.set_ylim(0,110); ax.grid(True, alpha=0.25)
plt.tight_layout()
return fig_to_stream(fig)
def img_fetal_skull():
fig, axes = plt.subplots(1, 2, figsize=(9, 4))
fig.patch.set_facecolor("#f0f4f8")
ax = axes[0]
ax.set_xlim(0,10); ax.set_ylim(0,8); ax.axis("off"); ax.set_facecolor("#f0f4f8")
skull = mpatches.Ellipse((5,4), 7, 5.5, fc="#FFE0B2", ec="#8B4513", lw=2, alpha=0.8)
ax.add_patch(skull)
ax.plot([5,5],[1.5,6.5], color="#8B4513", lw=1.5, ls="--")
ax.plot([1.8,8.2],[4,4], color="#8B4513", lw=1.5, ls="--")
ax.plot([2.2,7.8],[5.8,5.8], color="#4A2C6E", lw=1.2, ls=":")
labels = [
(5,6.5,"Anterior Fontanelle\n(Bregma) — Diamond","#27AE60"),
(5,1.8,"Posterior Fontanelle\n(Lambda) — Triangular","#1A3A5C"),
(2.8,4.0,"Coronal\nSuture","#8B4513"),
(5,4.3,"Sagittal\nSuture","#C0392B"),
(2.0,5.8,"Lambdoid\nSuture","#4A2C6E"),
]
for x,y,lbl,col in labels:
ax.text(x,y,lbl,ha="center",va="center",fontsize=7,color=col,fontweight="bold",
bbox=dict(fc="white",ec=col,boxstyle="round,pad=0.2",alpha=0.85))
ax.set_title("Fetal Skull — Sutures & Fontanelles", fontsize=10, fontweight="bold", color="#1A3A5C")
ax2 = axes[1]
ax2.set_xlim(0,1); ax2.set_ylim(0,1); ax2.axis("off"); ax2.set_facecolor("#f0f4f8")
rows = [
("Diameter","Cm","Presentation"),
("Suboccipito-bregmatic","9.5","Vertex (full flexion)"),
("Suboccipito-frontal","10.0","Vertex (partial flex)"),
("Occipito-frontal","11.5","Deflexed vertex"),
("Mento-vertical","13.5","Brow"),
("Submento-bregmatic","9.5","Face"),
("Biparietal","9.5","Transverse"),
]
xs=[0.02,0.52,0.70]; cws=[0.48,0.16,0.28]
for i,row in enumerate(rows):
bg="#1A3A5C" if i==0 else ("white" if i%2 else "#EBF5FB")
tc="white" if i==0 else "#333"
y=0.94-i*0.128
for j,(cell,cw) in enumerate(zip(row,cws)):
ax2.add_patch(FancyBboxPatch((xs[j],y-0.11),cw-0.01,0.115,
boxstyle="round,pad=0.01",fc=bg,ec="#ddd"))
ax2.text(xs[j]+(cw-0.01)/2,y-0.055,cell,ha="center",va="center",
fontsize=7.5 if i>0 else 8,color=tc,
fontweight="bold" if i==0 else "normal",
transform=ax2.transAxes)
ax2.set_title("Fetal Head Diameters", fontsize=10, fontweight="bold", color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_pelvis_types():
fig, axes = plt.subplots(1, 4, figsize=(10, 3.2))
fig.patch.set_facecolor("#f0f4f8")
types = [
("Gynaecoid","#27AE60",(1.0,1.0),"50%\nMost common\nFavourable"),
("Android","#C0392B",(0.8,1.0),"20%\nHeart-shaped\nUnfavourable"),
("Anthropoid","#F5A623",(0.7,1.3),"25%\nAP oval\nModerate"),
("Platypelloid","#007A87",(1.3,0.6),"5%\nFlat\nUnfavourable"),
]
for ax,(name,col,(rx,ry),note) in zip(axes,types):
ax.set_xlim(-2,2); ax.set_ylim(-2,2.5); ax.set_aspect("equal"); ax.axis("off")
ax.set_facecolor("#f0f4f8")
ax.add_patch(mpatches.Ellipse((0,0),2*rx,2*ry,fc=col,ec="white",lw=2,alpha=0.35))
ax.add_patch(mpatches.Ellipse((0,0),2*rx,2*ry,fc="none",ec=col,lw=2.5))
ax.text(0,ry+0.35,name,ha="center",va="bottom",fontsize=10,fontweight="bold",color=col)
ax.text(0,-ry-0.3,note,ha="center",va="top",fontsize=8,color="#444",linespacing=1.3)
ax.annotate("",xy=(rx,0),xytext=(-rx,0),
arrowprops=dict(arrowstyle="<->",color=col,lw=1.5))
ax.annotate("",xy=(0,ry),xytext=(0,-ry),
arrowprops=dict(arrowstyle="<->",color=col,lw=1.5))
fig.suptitle("Caldwell-Moloy Classification — Pelvis Types",
fontsize=12,fontweight="bold",color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_partograph():
fig, axes = plt.subplots(2, 1, figsize=(8.5, 5.5),
gridspec_kw={"height_ratios":[3,1.2]})
fig.patch.set_facecolor("#fffef5")
ax = axes[0]; ax.set_facecolor("#fffef5")
hrs = np.array([0,1,2,3,4])
alert = np.array([4,5,6,7,8])
action = alert + 4
progress = np.array([4,5.5,7.5,9,10])
ax.plot(hrs, alert, "b-o", lw=2.2, ms=7, label="Alert Line (1 cm/hr)")
ax.plot(hrs, action, "r-s", lw=2.2, ms=7, label="Action Line (+4 hrs)")
ax.plot(hrs, progress, "g-D", lw=2.5, ms=8, label="Patient — cervical dilation")
ax.fill_between(hrs, alert, action, alpha=0.15, color="orange", label="Warning zone")
ax.fill_between(hrs, np.zeros(5)+4, alert, alpha=0.07, color="green")
ax.set_xlim(-0.2,4.5); ax.set_ylim(3,11)
ax.set_xlabel("Hours in Active Labour", fontsize=10)
ax.set_ylabel("Cervical Dilation (cm)", fontsize=10)
ax.set_title("WHO Partograph — Cervicograph", fontsize=11, fontweight="bold", color="#1A3A5C")
ax.legend(fontsize=8, loc="upper left"); ax.grid(True, alpha=0.3)
ax.set_yticks(range(4,11)); ax.set_xticks(range(0,5))
ax2 = axes[1]; ax2.set_facecolor("#f0f0ff")
fhr_t = np.linspace(0,4,100)
np.random.seed(42)
fhr = 140 + 8*np.sin(fhr_t*3.5) + np.random.normal(0,3,100)
ax2.plot(fhr_t, fhr, color="#2980B9", lw=1.8)
ax2.axhline(110, color="#C0392B", ls="--", lw=1.2, label="Min (110 bpm)")
ax2.axhline(160, color="#C0392B", ls="--", lw=1.2, label="Max (160 bpm)")
ax2.fill_between(fhr_t, 110, 160, alpha=0.08, color="#27AE60")
ax2.set_xlim(-0.2,4.5); ax2.set_ylim(90,185)
ax2.set_xlabel("Hours", fontsize=9); ax2.set_ylabel("FHR (bpm)", fontsize=9)
ax2.legend(fontsize=7.5, loc="upper right"); ax2.grid(True, alpha=0.3)
ax2.set_title("Fetal Heart Rate Monitoring", fontsize=9, color="#1A3A5C")
plt.tight_layout()
return fig_to_stream(fig)
def img_management_4cols():
fig, ax = plt.subplots(figsize=(9, 4.5))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
phases = [
("STAGE I\nMONITORING", "#1A3A5C",
["FHR q30 min (latent)\nq15 min (active)",
"VE every 4 hrs", "Contractions: freq/strength",
"Maternal BP, pulse, temp", "Maintain partograph"]),
("STAGE II\nDELIVERY", "#007A87",
["Guide pushing with contractions",
"Support perineum (Ritgen)", "Control head delivery",
"Check cord at neck", "Oxytocin 10 IU IM"]),
("STAGE III\nAMTSL", "#27AE60",
["Oxytocin 10 IU IM", "Controlled cord traction",
"Uterine massage", "Inspect placenta/membranes",
"Measure blood loss"]),
("STAGE IV\nRECOVERY", "#F5A623",
["Monitor vitals q15 min", "Uterine tone assessment",
"Perineal repair", "Breastfeeding initiation",
"Bonding — skin-to-skin"]),
]
for i, (phase, col, items) in enumerate(phases):
x = 0.02 + i*0.245
ax.add_patch(FancyBboxPatch((x, 0.05), 0.235, 0.90,
boxstyle="round,pad=0.02", fc=col, ec="white", lw=2, alpha=0.9,
transform=ax.transAxes))
ax.text(x+0.1175, 0.93, phase, ha="center", va="top", fontsize=9,
fontweight="bold", color="white", linespacing=1.3,
transform=ax.transAxes)
for j, item in enumerate(items):
ax.text(x+0.015, 0.78-j*0.148, f"• {item}", ha="left", va="top",
fontsize=7.2, color="white", linespacing=1.3,
transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
def img_amtsl():
fig, ax = plt.subplots(figsize=(8, 3.8))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
steps = [
("1","Uterotonic\nAdministration","Oxytocin 10 IU IM\nWithin 1 min of\nanterior shoulder","#27AE60"),
("2","Controlled Cord\nTraction (CCT)","Brandt-Andrews\nmanoeuvre\nCounter-pressure","#007A87"),
("3","Uterine\nMassage","After placenta\ndelivered\n30–60 sec","#1A3A5C"),
]
for i, (num, title, desc, col) in enumerate(steps):
x = 0.05 + i*0.32
if i > 0:
ax.annotate("", xy=(x-0.015, 0.5), xytext=(x-0.085, 0.5),
arrowprops=dict(arrowstyle="-|>", color=col, lw=3),
xycoords="axes fraction", textcoords="axes fraction")
ax.add_patch(FancyBboxPatch((x, 0.08), 0.28, 0.84,
boxstyle="round,pad=0.03", fc=col, ec="white", lw=2, alpha=0.9,
transform=ax.transAxes))
ax.add_patch(Circle((x+0.14, 0.82), 0.07, fc="white", ec=col, lw=0,
transform=ax.transAxes))
ax.text(x+0.14, 0.82, num, ha="center", va="center",
fontsize=15, fontweight="bold", color=col, transform=ax.transAxes)
ax.text(x+0.14, 0.67, title, ha="center", va="center", fontsize=9,
fontweight="bold", color="white", linespacing=1.4,
transform=ax.transAxes)
ax.text(x+0.14, 0.38, desc, ha="center", va="center", fontsize=8.5,
color="white", linespacing=1.5, transform=ax.transAxes)
ax.text(0.5, 0.97, "Active Management of Third Stage Labour (AMTSL)",
ha="center", va="top", fontsize=11, fontweight="bold", color="#1A3A5C",
transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
def img_apgar():
fig, ax = plt.subplots(figsize=(9, 4))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
headers = ["Sign","Score 0","Score 1","Score 2"]
rows = [
["Appearance (Colour)","Blue/pale all over","Blue extremities","Pink all over"],
["Pulse (Heart Rate)","Absent","< 100 bpm","≥ 100 bpm"],
["Grimace (Reflex)","No response","Grimace only","Cough/cry/sneeze"],
["Activity (Tone)","Limp","Some flexion","Active motion"],
["Respiration","Absent","Slow/irregular","Good, crying"],
]
score_cols=["#C0392B","#F5A623","#27AE60"]
xs=[0.02,0.30,0.55,0.78]; cws=[0.27,0.24,0.22,0.20]
ax.text(0.5,0.97,"APGAR Score (Virginia Apgar, 1952)",ha="center",va="top",
fontsize=13,fontweight="bold",color="#1A3A5C",transform=ax.transAxes)
for j,(h,x,cw) in enumerate(zip(headers,xs,cws)):
col="#1A3A5C" if j==0 else score_cols[j-1]
ax.add_patch(FancyBboxPatch((x,0.80),cw-0.01,0.14,
boxstyle="round,pad=0.01",fc=col,ec="none",transform=ax.transAxes))
ax.text(x+(cw-0.01)/2,0.87,h,ha="center",va="center",
fontsize=10,fontweight="bold",color="white",transform=ax.transAxes)
for i,row in enumerate(rows):
y=0.78-i*0.148
bg="white" if i%2==0 else "#EBF5FB"
for j,(cell,x,cw) in enumerate(zip(row,xs,cws)):
ax.add_patch(FancyBboxPatch((x,y-0.13),cw-0.01,0.14,
boxstyle="round,pad=0.01",fc=bg,ec="#ddd",transform=ax.transAxes))
ax.text(x+(cw-0.01)/2,y-0.06,cell,ha="center",va="center",
fontsize=8,color="#333",transform=ax.transAxes)
interp=[(0.02,"7–10: Normal","#27AE60"),(0.36,"4–6: Moderate","#F5A623"),(0.67,"0–3: Severe","#C0392B")]
for x,lbl,col in interp:
ax.add_patch(FancyBboxPatch((x,0.01),0.30,0.08,boxstyle="round,pad=0.01",
fc=col,ec="none",alpha=0.85,transform=ax.transAxes))
ax.text(x+0.15,0.05,lbl,ha="center",va="center",fontsize=9,
fontweight="bold",color="white",transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
def img_complications():
fig, ax = plt.subplots(figsize=(9, 4.5))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
cols_data = [
("MATERNAL\nCOMPLICATIONS","#C0392B",[
"Prolonged labour (>18h primip)","PPH — atony, trauma, tissue",
"Uterine rupture","Perineal tears (1°–4°)",
"Infection / sepsis","Amniotic fluid embolism","Obstetric fistula (VVF)"]),
("FETAL\nCOMPLICATIONS","#1A3A5C",[
"Birth asphyxia / HIE","Meconium aspiration syndrome",
"Shoulder dystocia","Cord prolapse / compression",
"Birth injuries (Erb's palsy)","Intracranial haemorrhage","Stillbirth"]),
("PREVENTION","#27AE60",[
"Skilled birth attendant","WHO Safe Childbirth Checklist",
"Partograph monitoring","AMTSL for all deliveries",
"Continuous support in labour","Delayed cord clamping","Vitamin K prophylaxis"]),
]
for i,(title,col,items) in enumerate(cols_data):
x=0.02+i*0.325
ax.add_patch(FancyBboxPatch((x,0.04),0.315,0.91,
boxstyle="round,pad=0.02",fc=col,ec="white",lw=2,alpha=0.9,
transform=ax.transAxes))
ax.text(x+0.157,0.93,title,ha="center",va="top",fontsize=9,
fontweight="bold",color="white",linespacing=1.3,
transform=ax.transAxes)
for j,item in enumerate(items):
ax.text(x+0.025,0.78-j*0.12,f"• {item}",ha="left",va="top",
fontsize=7.8,color="white",transform=ax.transAxes)
ax.text(0.5,0.99,"Complications of Labour",ha="center",va="top",
fontsize=14,fontweight="bold",color="#1A3A5C",transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
def img_key_points():
fig, ax = plt.subplots(figsize=(9, 5))
fig.patch.set_facecolor("#f0f4f8"); ax.set_facecolor("#f0f4f8"); ax.axis("off")
boxes = [
(0.02,0.67,"#1A3A5C","DEFINITION",
["Spontaneous onset, vertex, term (37–42 wks)",
"Completed within 18h primip / 12h multip","No complications to mother or baby"]),
(0.52,0.67,"#007A87","MECHANISM",
["LOA: Engagement→Descent→Flexion",
"Internal Rotation→Extension→Restitution",
"External Rotation→Expulsion"]),
(0.02,0.34,"#27AE60","MONITORING",
["FHR q30 min latent / q15 min active","Partograph: Alert (1cm/hr) + Action line",
"VE every 4 hrs; Maternal vitals hourly"]),
(0.52,0.34,"#C0392B","AMTSL",
["1. Oxytocin 10 IU IM on ant. shoulder","2. Controlled cord traction (CCT)",
"3. Uterine massage after placenta"]),
(0.02,0.02,"#8E44AD","DURATIONS",
["Stage I: 12h (P) / 6h (M) | Stage II: 1h (P) / 30min (M)",
"Stage III: 5–30 min | Stage IV: 1–2 hours","FHR: 110–160 bpm; Normal EBL <500 mL"]),
(0.52,0.02,"#F5A623","APGAR",
["Assess at 1 min and 5 min","7–10 Normal • 4–6 Moderate • 0–3 Severe",
"Vitamin K, delayed cord clamp, skin-to-skin"]),
]
for x,y,col,title,items in boxes:
ax.add_patch(FancyBboxPatch((x,y),0.47,0.295,
boxstyle="round,pad=0.02",fc=col,ec="white",lw=1.5,alpha=0.9,
transform=ax.transAxes))
ax.text(x+0.235,y+0.255,title,ha="center",va="top",fontsize=10,
fontweight="bold",color="white",transform=ax.transAxes)
for i,item in enumerate(items):
ax.text(x+0.025,y+0.185-i*0.083,f"• {item}",ha="left",va="top",
fontsize=8,color="white",transform=ax.transAxes)
plt.tight_layout()
return fig_to_stream(fig)
# ═══════════════════════════════════════════════════════════════════════
# BUILD PRESENTATION
# ═══════════════════════════════════════════════════════════════════════
def build():
prs = Presentation()
prs.slide_width = Inches(W_IN)
prs.slide_height = Inches(H_IN)
# ── SLIDE 1: TITLE ──────────────────────────────────────────────────
sl = prs.slides.add_slide(prs.slide_layouts[6])
sw, sh = prs.slide_width, prs.slide_height
bg = sl.shapes.add_shape(1,0,0,sw,sh)
bg.fill.solid(); bg.fill.fore_color.rgb=NAVY; bg.line.fill.background()
overlay = sl.shapes.add_shape(1,0,Inches(4.0),sw,sh-Inches(4.0))
overlay.fill.solid(); overlay.fill.fore_color.rgb=RGBColor(0x0D,0x24,0x3B)
overlay.line.fill.background()
stripe = sl.shapes.add_shape(1,0,Inches(3.7),sw,Inches(0.13))
stripe.fill.solid(); stripe.fill.fore_color.rgb=GOLD; stripe.line.fill.background()
tx = sl.shapes.add_textbox(Inches(0.8),Inches(0.6),Inches(11.7),Inches(2.5))
tf=tx.text_frame; p=tf.paragraphs[0]
p.text="NORMAL LABOUR"; p.font.bold=True; p.font.size=Pt(58)
p.font.color.rgb=WHITE; p.alignment=PP_ALIGN.CENTER
tx2=sl.shapes.add_textbox(Inches(0.8),Inches(2.9),Inches(11.7),Inches(0.75))
p2=tx2.text_frame.paragraphs[0]
p2.text="Physiology, Mechanisms & Clinical Management"
p2.font.size=Pt(22); p2.font.color.rgb=GOLD; p2.font.bold=True
p2.alignment=PP_ALIGN.CENTER
tx3=sl.shapes.add_textbox(Inches(0.8),Inches(3.65),Inches(11.7),Inches(0.55))
p3=tx3.text_frame.paragraphs[0]
p3.text="Sources: Creasy & Resnik's MFM | Tintinalli's EM | Roberts & Hedges | Family Medicine"
p3.font.size=Pt(14.5); p3.font.color.rgb=RGBColor(0xAA,0xCC,0xFF)
p3.alignment=PP_ALIGN.CENTER
# Topics list on title slide
topics=[
"• Definition & Stages • True vs False Labour • Uterine Contractions",
"• Fetal Skull & Diameters • Pelvis Types • Cardinal Movements of Labour",
"• Friedman Curve & Partograph • Management of All Stages",
"• AMTSL • APGAR Score • Complications & Prevention",
]
tx4=sl.shapes.add_textbox(Inches(1.2),Inches(4.35),Inches(10.9),Inches(2.9))
tf4=tx4.text_frame; tf4.word_wrap=True
for i,line in enumerate(topics):
p=tf4.paragraphs[0] if i==0 else tf4.add_paragraph()
p.text=line; p.font.size=Pt(14.5); p.font.color.rgb=RGBColor(0xCC,0xE0,0xFF)
p.space_after=Pt(4)
print("Slide 1 (Title) ✓")
# ── SLIDE 2: DEFINITION & STAGES ───────────────────────────────────
sl2=add_slide(prs,"Definition & Stages of Normal Labour",
"Creasy & Resnik's MFM, p.936 | Tintinalli's EM | Family Medicine")
bullets=[
"DEFINITION (Creasy & Resnik):",
" Onset of painful uterine contractions associated with",
" effacement & dilation of the cervix.",
"",
"NORMAL LABOUR (Eutocia):",
" • Spontaneous onset at term (37–42 wks)",
" • Single fetus, vertex presentation",
" • Completed within 18h (primip) / 12h (multip)",
" • No complications to mother or baby",
"",
"FOUR STAGES (Pritchard & MacDonald):",
" Stage I: Onset → Full dilation (10 cm)",
" Stage II: Full dilation → Delivery of baby",
" Stage III: Baby → Delivery of placenta",
" Stage IV: 1–2 hrs postpartum recovery",
]
add_bullets(sl2,bullets,0.3,1.5,6.5,5.8,font_size=15)
insert_img(sl2,img_stages_banner(),6.9,1.5,6.1,2.1)
insert_img(sl2,img_true_false(),6.9,3.65,6.1,3.7)
print("Slide 2 ✓")
# ── SLIDE 3: UTERINE CONTRACTIONS & PHYSIOLOGY ──────────────────────
sl3=add_slide(prs,"Uterine Contractions — Physiology of Labour",
"Morgan & Mikhail Clinical Anesthesiology | Creasy & Resnik MFM")
bullets3=[
"ONSET OF LABOUR (Morgan & Mikhail):",
" • Uterine distension + enhanced oxytocin sensitivity",
" • Rapid increase in myometrial oxytocin receptors",
" • Altered prostaglandin synthesis (PGE2, PGF2α)",
"",
"NORMAL CONTRACTION PARAMETERS:",
" Frequency: 3–5 per 10 minutes",
" Duration: 45–60 seconds (active phase)",
" Intensity: 40–60 mmHg above baseline",
" Resting tone: 8–12 mmHg",
"",
"PROPERTIES (Fundal Dominance):",
" • Triple descending gradient",
" • Retraction — fibres shorten permanently",
" • Upper uterine segment thickens",
" • Lower segment passively thins",
"",
"PAIN PATHWAY:",
" 1st stage: T10–L1 (visceral — uterus/cervix)",
" 2nd stage: S2–S4 (somatic — perineum)",
]
add_bullets(sl3,bullets3,0.3,1.5,6.6,5.8,font_size=14)
insert_img(sl3,img_uterine_contractions(),7.0,1.55,6.0,3.2)
# Labour curve from textbook
insert_img(sl3,get_img("labour_curve"),7.0,4.85,6.0,2.55)
add_caption(sl3,"FIGURE: Course of normal labour — cervical dilation & fetal descent (Morgan & Mikhail, Anesthesiology)",7.0,7.35,6.0)
print("Slide 3 ✓")
# ── SLIDE 4: FRIEDMAN CURVE + COMPOSITE ─────────────────────────────
sl4=add_slide(prs,"Friedman Labour Curve — First Stage of Labour",
"Creasy & Resnik MFM p.937 | Family Medicine Fig 20-10")
bullets4=[
"FRIEDMAN CURVE (1954):",
" Sigmoid relationship between dilation & time",
"",
"LATENT PHASE:",
" • Cervical preparation (effacement + ripening)",
" • Duration: ≤ 20h nullipara, ≤ 14h multipara",
" • Minor dilation (0–3 cm)",
"",
"ACTIVE PHASE (begins ~4–5 cm):",
" • Phase of maximum slope (rapid dilation)",
" • Nullipara: ≥ 1.2 cm/hr",
" • Multipara: ≥ 1.5 cm/hr",
" • Deceleration phase: 8–10 cm",
"",
"ARREST DISORDERS:",
" Protracted active phase: < 1.2 cm/hr (nullip)",
" Secondary arrest: No change ≥ 2 hours",
" Prolonged deceleration: > 3h (nullip)",
]
add_bullets(sl4,bullets4,0.3,1.5,5.8,5.8,font_size=14)
insert_img(sl4,get_img("labour_curve"),6.2,1.5,7.0,3.4)
add_caption(sl4,"Fig: Course of normal labour — latent phase, acceleration, phase of max slope, deceleration. (Morgan & Mikhail)",6.2,4.88,7.0)
insert_img(sl4,get_img("composite_curves"),6.2,5.1,7.0,2.32)
add_caption(sl4,"Fig 20-10: Composite curves — normal & abnormal labour (primigravid & multiparous). (Family Medicine Textbook)",6.2,7.38,7.0)
print("Slide 4 ✓")
# ── SLIDE 5: FETAL SKULL ────────────────────────────────────────────
sl5=add_slide(prs,"Fetal Skull — Anatomy, Sutures & Diameters",
"Creasy & Resnik MFM | Roberts & Hedges Clinical Procedures")
bullets5=[
"BONES (6):",
" 2 Frontal, 2 Parietal, 2 Temporal,",
" 1 Occipital, 1 Sphenoid",
"",
"SUTURES:",
" Sagittal — between 2 parietal bones",
" Coronal — frontal & parietal",
" Lambdoid — parietal & occipital",
" Frontal — between 2 frontal bones",
"",
"FONTANELLES:",
" Anterior (Bregma): Diamond, closes 18 months",
" Posterior (Lambda): Triangle, closes 6–8 weeks",
"",
"DENOMINATOR:",
" Vertex → Occiput (most common)",
" Face → Mentum | Breech → Sacrum",
"",
"MOULDING: Overlapping of bones at sutures",
" Reduces presenting diameter by up to 1 cm",
]
add_bullets(sl5,bullets5,0.3,1.5,6.0,5.8,font_size=14)
insert_img(sl5,img_fetal_skull(),6.4,1.5,6.6,5.8)
print("Slide 5 ✓")
# ── SLIDE 6: PELVIS ─────────────────────────────────────────────────
sl6=add_slide(prs,"Female Pelvis — Types, Dimensions & Station",
"Creasy & Resnik MFM | Roberts & Hedges")
bullets6=[
"PELVIC PLANES & CONJUGATES:",
" Obstetric conjugate (inlet): 11 cm",
" Transverse diameter (inlet): 13 cm",
" Bi-ischial (outlet): 10.5 cm",
"",
"STATION (ischial spines = 0):",
" -5 to -1: presenting part above spines",
" 0: engaged (BPD at pelvic inlet)",
" +1 to +5: below spines (crowning = +5)",
"",
"ENGAGEMENT:",
" BPD through pelvic inlet",
" Primipara: 36 weeks gestation",
" Multipara: onset of labour",
"",
"ASSESSMENT:",
" Palpation: 5ths palpable above brim",
" <2/5 = engaged",
" VE: presenting part at / above spines",
]
add_bullets(sl6,bullets6,0.3,1.5,6.3,5.8,font_size=14)
insert_img(sl6,img_pelvis_types(),6.5,1.5,6.5,5.8)
print("Slide 6 ✓")
# ── SLIDE 7: CARDINAL MOVEMENTS ─────────────────────────────────────
sl7=add_slide(prs,"Cardinal Movements of Labour (Mechanism)",
"Roberts & Hedges Clinical Procedures Fig 56.5 | Tintinalli's EM")
bullets7=[
"MECHANISM (Roberts & Hedges):",
" The fetus follows path of least resistance",
" adapting smallest diameter to birth canal.",
"",
"LOA — Most Common Presentation",
"",
"8 CARDINAL MOVEMENTS:",
" 1. ENGAGEMENT — BPD at pelvic inlet",
" 2. DESCENT — continuous throughout",
" 3. FLEXION — chin to chest (9.5 cm)",
" 4. INTERNAL ROTATION — occiput → pubic symphysis",
" 5. EXTENSION — head born under arch",
" 6. RESTITUTION — 45° back to oblique",
" 7. EXTERNAL ROTATION — shoulders to AP",
" 8. EXPULSION — ant. then post. shoulder",
"",
"Key muscle: Levator ani (pelvic floor)",
"aids internal rotation",
]
add_bullets(sl7,bullets7,0.3,1.5,5.5,5.8,font_size=14)
insert_img(sl7,get_img("cardinal_movements"),5.9,1.5,7.1,5.8)
add_caption(sl7,"Fig 56.5: Cardinal movements A–H (From Gabbe's Obstetrics). [Roberts & Hedges Clinical Procedures in Emergency Medicine]",5.9,7.28,7.1)
print("Slide 7 ✓")
# ── SLIDE 8: MECHANISM DETAIL ───────────────────────────────────────
sl8=add_slide(prs,"Mechanism in Detail — LOA Step by Step",
"Tintinalli's EM | Family Medicine Textbook | Creasy & Resnik")
bullets8=[
"1. ENGAGEMENT:",
" Head enters left oblique of pelvic inlet",
" Sagittal suture in left oblique diameter",
"",
"2. DESCENT + FLEXION:",
" Passive flexion minimizes presenting diameter",
" Suboccipito-bregmatic = 9.5 cm (smallest)",
"",
"3. INTERNAL ROTATION:",
" Occiput rotates from LT to OA (1/8 turn)",
" Sagittal suture → AP diameter of outlet",
" Aided by levator ani pelvic floor muscles",
"",
"4. EXTENSION:",
" Pivot = inferior border of pubic symphysis",
" Occiput, bregma, forehead, nose, chin born",
"",
"5. RESTITUTION & EXTERNAL ROTATION:",
" Head rotates 45° back to oblique (restitution)",
" Then shoulders align AP → delivery",
"",
"6. EXPULSION:",
" Anterior shoulder then posterior shoulder",
]
add_bullets(sl8,bullets8,0.3,1.5,6.3,5.8,font_size=13)
# Mechanism diagram
fig_m, axes_m = plt.subplots(1,6,figsize=(10,3.2))
fig_m.patch.set_facecolor("#f0f4f8")
steps_m=[
("Engagement\n& Descent","#1A3A5C","↓"),
("Flexion","#007A87","⤵"),
("Internal\nRotation","#F5A623","↻ OA"),
("Extension","#27AE60","↑"),
("Restitution","#8E44AD","↺"),
("Expulsion","#C0392B","✓"),
]
for ax,(title,col,sym) in zip(axes_m,steps_m):
ax.set_xlim(0,1); ax.set_ylim(0,1); ax.axis("off"); ax.set_facecolor("#f0f4f8")
ax.add_patch(FancyBboxPatch((0.04,0.04),0.92,0.92,
boxstyle="round,pad=0.04",fc="white",ec=col,lw=2))
ax.add_patch(Arc((0.5,-0.1),0.75,0.6,angle=0,theta1=15,theta2=165,
color="#8B4513",lw=2.5))
ax.add_patch(Circle((0.5,0.52),0.18,fc=col,ec="white",lw=1.5,alpha=0.88))
ax.text(0.5,0.52,sym,ha="center",va="center",fontsize=10,
fontweight="bold",color="white")
ax.text(0.5,0.88,title,ha="center",va="top",fontsize=7.5,
fontweight="bold",color=col,linespacing=1.3)
fig_m.suptitle("Step-by-step Mechanism — LOA", fontsize=10,
fontweight="bold",color="#1A3A5C",y=1.0)
plt.tight_layout(pad=0.15)
insert_img(sl8,fig_to_stream(fig_m),6.5,1.5,6.6,3.8)
insert_img(sl8,get_img("cardinal_movements"),6.5,5.35,6.6,2.05)
print("Slide 8 ✓")
# ── SLIDE 9: PARTOGRAPH ─────────────────────────────────────────────
sl9=add_slide(prs,"Partograph — WHO Labour Monitoring Tool",
"Creasy & Resnik MFM | Tintinalli's EM | Family Medicine")
bullets9=[
"DEFINITION: Graphical record of labour",
" (Philpott & Castle, 1972; WHO modified 1994)",
"",
"COMPONENTS:",
" 1. Cervicograph (dilation vs time)",
" 2. Alert line — 1 cm/hr from 4 cm",
" 3. Action line — 4 hrs right of alert",
" 4. Fetal HR (every 30 min)",
" 5. Liquor colour, moulding, caput",
" 6. Contractions per 10 min",
" 7. Oxytocin/drugs/IV fluids",
" 8. Maternal BP, pulse, temp, urine",
"",
"INTERPRETATION:",
" Left of alert = Progress normal",
" Alert–Action zone = Increased monitoring",
" Beyond action line = Intervention needed",
" (augmentation / caesarean section)",
"",
"VE: Every 4 hours in active labour",
]
add_bullets(sl9,bullets9,0.3,1.5,5.8,5.8,font_size=13.5)
insert_img(sl9,img_partograph(),6.2,1.5,7.0,5.8)
print("Slide 9 ✓")
# ── SLIDE 10: MANAGEMENT ────────────────────────────────────────────
sl10=add_slide(prs,"Management of Normal Labour — All Four Stages",
"Tintinalli's EM | Creasy & Resnik MFM | Family Medicine")
bullets10=[
"ON ADMISSION:",
" • History: GA, GBS, antenatal records",
" • Examination: fundal height, presentation, FHR",
" • VE: dilation, effacement, station, liquor",
" • Investigations: CBC, urine, Group & Screen",
"",
"GENERAL CARE:",
" • Supportive environment, birth partner",
" • Oral intake: clear fluids in active labour",
" • Ambulation encouraged in 1st stage",
" • Bladder care: void every 2 hours",
" • IV access if high risk",
"",
"PAIN RELIEF OPTIONS:",
" • Non-pharmacological: mobility, hydrotherapy",
" • Pharmacological: opioids, N₂O, epidural",
"",
"FHR AUSCULTATION (Tintinalli):",
" Low risk: q30 min (latent) / q15 min (active)",
" 2nd stage: every 5 minutes",
" Higher risk: continuous CTG monitoring",
]
add_bullets(sl10,bullets10,0.3,1.5,6.3,5.8,font_size=13)
insert_img(sl10,img_management_4cols(),6.6,1.5,6.5,4.5)
insert_img(sl10,img_amtsl(),6.6,6.05,6.5,1.38)
print("Slide 10 ✓")
# ── SLIDE 11: SECOND & THIRD STAGE ──────────────────────────────────
sl11=add_slide(prs,"Second Stage & Third Stage Management",
"Family Medicine Textbook | Tintinalli's EM | Creasy & Resnik")
bullets11=[
"SECOND STAGE (Full dilation → Baby):",
" • Ferguson reflex: urge to push",
" • Duration: 20–50 min multip / nullip",
" • Guide pushing with contractions",
" • Support perineum — modified Ritgen",
" • Delayed cord clamping 1–3 minutes",
" • Oxytocin 10 IU IM on anterior shoulder",
"",
"THIRD STAGE (Baby → Placenta):",
" • Avg blood loss: ~600 mL normal delivery",
" • Separation by retroplacental clot",
" • Schultze (centre-first, most common)",
" • Duncan (edge-first, less common)",
"",
"SIGNS OF SEPARATION:",
" • Uterus globular, rises above umbilicus",
" • Gush of blood at introitus",
" • Cord lengthens (Allis sign)",
"",
"PLACENTAL INSPECTION:",
" Cotyledons complete; 2 arteries, 1 vein",
]
add_bullets(sl11,bullets11,0.3,1.5,6.3,5.8,font_size=13)
insert_img(sl11,img_amtsl(),6.6,1.5,6.5,3.0)
# 3rd stage physiology diagram
fig_p, ax_p = plt.subplots(figsize=(5.8,2.5))
fig_p.patch.set_facecolor("#f0f4f8"); ax_p.axis("off"); ax_p.set_facecolor("#f0f4f8")
steps_3=[
("Uterine\nContraction","#1A3A5C"),
("Retroplacental\nHaematoma","#C0392B"),
("Placental\nSeparation","#007A87"),
("Cord\nLengthens","#F5A623"),
("Expulsion","#27AE60"),
]
for i,(lbl,col) in enumerate(steps_3):
x=0.03+i*0.194
ax_p.add_patch(FancyBboxPatch((x,0.15),0.175,0.70,
boxstyle="round,pad=0.02",fc=col,ec="white",lw=1.5,alpha=0.9,
transform=ax_p.transAxes))
ax_p.text(x+0.0875,0.50,lbl,ha="center",va="center",fontsize=7.5,
fontweight="bold",color="white",linespacing=1.3,
transform=ax_p.transAxes)
if i<4:
ax_p.annotate("",xy=(x+0.194,0.50),xytext=(x+0.175,0.50),
arrowprops=dict(arrowstyle="-|>",color=col,lw=2),
xycoords="axes fraction",textcoords="axes fraction")
ax_p.text(0.5,0.95,"Third Stage Sequence",ha="center",va="top",fontsize=10,
fontweight="bold",color="#1A3A5C",transform=ax_p.transAxes)
plt.tight_layout(pad=0.1)
insert_img(sl11,fig_to_stream(fig_p),6.6,4.6,6.5,2.5)
print("Slide 11 ✓")
# ── SLIDE 12: COMPLICATIONS + CLINICAL PHOTO ────────────────────────
sl12=add_slide(prs,"Complications of Labour",
"Tintinalli's EM | Creasy & Resnik MFM | Roberts & Hedges")
bullets12=[
"MATERNAL:",
" Prolonged labour, PPH, uterine rupture",
" Perineal tears (1°–4°), infection",
" Amniotic fluid embolism, VVF/RVF",
"",
"FETAL:",
" Birth asphyxia, meconium aspiration",
" Shoulder dystocia → Erb's palsy",
" Cord prolapse, intracranial haemorrhage",
"",
"SHOULDER DYSTOCIA (Tintinalli):",
" Impaction of ant. shoulder at pubic symphysis",
" after delivery of head",
" HELPERR mnemonic:",
" H — Call for Help",
" E — Episiotomy (if needed)",
" L — Legs (McRoberts manoeuvre)",
" P — Suprapubic Pressure",
" E — Enter (internal manoeuvres)",
" R — Remove posterior arm",
" R — Roll (all fours position)",
]
add_bullets(sl12,bullets12,0.3,1.5,6.0,5.8,font_size=12.5)
insert_img(sl12,get_img("shoulder_dystocia"),6.4,1.5,4.5,4.0)
add_caption(sl12,"Clinical photo: Shoulder dystocia — fetal head impacted at perineum\n[Tintinalli's EM, Fig 101-4, from Atlas of Emergency Medicine]",6.4,5.45,4.5)
insert_img(sl12,img_complications(),6.4,6.0,6.6,1.45)
print("Slide 12 ✓")
# ── SLIDE 13: APGAR SCORE ───────────────────────────────────────────
sl13=add_slide(prs,"APGAR Score & Immediate Newborn Care",
"Tintinalli's EM Table 101-3 | Creasy & Resnik MFM")
bullets13=[
"APGAR SCORE (Virginia Apgar, 1952):",
" Assessed at 1 minute and 5 minutes",
"",
"INTERPRETATION:",
" 7–10 = Normal (routine care)",
" 4–6 = Moderate (O₂, stimulation)",
" 0–3 = Severe (immediate resus)",
"",
"IMMEDIATE NEWBORN CARE:",
" • Dry and stimulate (first 30 sec)",
" • Clear airway if meconium present",
" • Warmth — radiant warmer",
" • Assess tone, colour, breathing",
" • Vitamin K 1 mg IM",
" • Eye prophylaxis (erythromycin)",
" • Delayed cord clamp 1–3 min",
" • Skin-to-skin kangaroo care",
" • Breastfeed within first hour",
"",
"NEONATAL RESUS (if APGAR < 4):",
" PPV → Chest compressions → Adrenaline",
]
add_bullets(sl13,bullets13,0.3,1.5,5.8,5.8,font_size=13)
insert_img(sl13,img_apgar(),6.2,1.5,6.9,5.8)
print("Slide 13 ✓")
# ── SLIDE 14: KEY POINTS SUMMARY ───────────────────────────────────
sl14=add_slide(prs,"Key Examination Points — Quick Summary",
"High-Yield Revision Points")
insert_img(sl14,img_key_points(),0.25,1.5,12.8,5.8)
print("Slide 14 ✓")
# ── SLIDE 15: THANK YOU / REFERENCES ───────────────────────────────
sl15=prs.slides.add_slide(prs.slide_layouts[6])
sw,sh=prs.slide_width,prs.slide_height
bg15=sl15.shapes.add_shape(1,0,0,sw,sh)
bg15.fill.solid(); bg15.fill.fore_color.rgb=NAVY; bg15.line.fill.background()
stripe15=sl15.shapes.add_shape(1,0,Inches(4.1),sw,Inches(0.12))
stripe15.fill.solid(); stripe15.fill.fore_color.rgb=GOLD; stripe15.line.fill.background()
ty=sl15.shapes.add_textbox(Inches(0.5),Inches(0.7),Inches(12.3),Inches(2.8))
p_ty=ty.text_frame.paragraphs[0]; p_ty.text="Thank You"
p_ty.font.size=Pt(54); p_ty.font.bold=True; p_ty.font.color.rgb=WHITE
p_ty.alignment=PP_ALIGN.CENTER
tx_sub=sl15.shapes.add_textbox(Inches(0.5),Inches(2.8),Inches(12.3),Inches(0.8))
p_sub=tx_sub.text_frame.paragraphs[0]
p_sub.text="Normal Labour — Physiology, Mechanisms & Clinical Management"
p_sub.font.size=Pt(20); p_sub.font.color.rgb=GOLD; p_sub.font.bold=True
p_sub.alignment=PP_ALIGN.CENTER
refs_box=sl15.shapes.add_textbox(Inches(1.5),Inches(4.3),Inches(10.3),Inches(3.0))
tf_r=refs_box.text_frame; tf_r.word_wrap=True
ref_lines=[
"REFERENCES",
"1. Creasy RK, Resnik R. Maternal-Fetal Medicine: Principles and Practice, 8e. Elsevier.",
"2. Tintinalli JE. Emergency Medicine: A Comprehensive Study Guide, 9e. McGraw-Hill.",
"3. Roberts JR, Hedges JR. Clinical Procedures in Emergency Medicine, 6e. Elsevier.",
"4. Rakel RE, Rakel DP. Textbook of Family Medicine, 9e. Elsevier.",
"5. Morgan GE, Mikhail MS. Clinical Anesthesiology, 7e. McGraw-Hill.",
"6. WHO. Intrapartum Care for a Positive Childbirth Experience. WHO Guidelines, 2018.",
]
for i,line in enumerate(ref_lines):
p=tf_r.paragraphs[0] if i==0 else tf_r.add_paragraph()
p.text=line; p.font.size=Pt(14 if i==0 else 12)
p.font.color.rgb=RGBColor(0xCC,0xE0,0xFF); p.font.bold=(i==0); p.space_after=Pt(3)
print("Slide 15 (Thank You) ✓")
prs.save(str(PPTX_PATH))
print(f"\n✅ Saved: {PPTX_PATH}")
print(f" Size: {os.path.getsize(PPTX_PATH)/1024:.1f} KB")
print(f" Slides: {len(prs.slides)}")
if __name__=="__main__":
build()
cd /tmp/workspace/normal-labour-ppt && python3 build_v2.py 2>&1
Normal Labour v2
Presentation · PPTX
| Real Textbook Image | Source | Used on Slide |
|---|---|---|
| Normal labour curve — cervical dilation + fetal descent with labeled phases | Morgan & Mikhail's Clinical Anesthesiology, 7e | Slides 3 & 4 |
| Composite curves — normal + abnormal labour (primigravid & multiparous) | Textbook of Family Medicine, 9e, Fig 20-10 | Slide 4 |
| Cardinal movements A–H — full illustrated sequence (engagement→expulsion) | Roberts & Hedges' Clinical Procedures, Fig 56.5 (from Gabbe's Obstetrics) | Slides 7 & 8 |
| Shoulder dystocia clinical photo — fetal head impacted at perineum | Tintinalli's Emergency Medicine, Fig 101-4 | Slide 12 |
| # | Topic |
|---|---|
| 1 | Title — sources listed |
| 2 | Definition & Stages + True vs False Labour |
| 3 | Uterine Contractions physiology + textbook labour curve |
| 4 | Friedman Curve — both textbook figures (normal + abnormal) |
| 5 | Fetal Skull — sutures, fontanelles, diameters table |
| 6 | Pelvis types (Caldwell-Moloy) + station |
| 7 | Cardinal Movements — textbook illustration A–H |
| 8 | LOA Mechanism step-by-step + cardinal movements |
| 9 | WHO Partograph with cervicograph + FHR chart |
| 10 | Management of all 4 stages |
| 11 | 2nd & 3rd stage + AMTSL sequence |
| 12 | Complications + shoulder dystocia clinical photo |
| 13 | APGAR score table + newborn care |
| 14 | Key Points summary grid |
| 15 | Thank You + full references |