Physiological changes during pregnancy & make a detailed summary downloadable photo or pdf for better visual memory
mkdir -p /home/daytona/workspace/pregnancy-physiology && ls /home/daytona/workspace/pregnancy-physiology/
~/pregnancy-physiology/make_pdf.py
#!/usr/bin/env python3
"""
Physiological Changes During Pregnancy - Visual Summary PDF
Based on Morgan & Mikhail's Clinical Anesthesiology, 7e
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame
from reportlab.lib.colors import HexColor
import reportlab.lib.colors as rcolors
# ── Color Palette ──────────────────────────────────────────────────────────
DARK_BG = HexColor('#0D1B2A') # deep navy
HEADER_BG = HexColor('#1B4F72') # dark teal-blue
ACCENT_PINK = HexColor('#E91E8C') # hot pink accent
ACCENT_CYAN = HexColor('#00BCD4') # cyan accent
ACCENT_GOLD = HexColor('#FFD600') # golden yellow
ACCENT_GREEN = HexColor('#4CAF50') # green
ACCENT_ORG = HexColor('#FF6F00') # orange
LIGHT_TEXT = HexColor('#ECEFF1') # near white
CARD_BG_1 = HexColor('#112233') # card dark navy
CARD_BG_2 = HexColor('#0A2540')
ROW_ALT = HexColor('#1A3555')
TABLE_HDR = HexColor('#1565C0')
WHITE = colors.white
BLACK = colors.black
# ── Styles ─────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
title_style = ParagraphStyle(
'TitleStyle',
fontName='Helvetica-Bold',
fontSize=26,
textColor=WHITE,
alignment=TA_CENTER,
spaceAfter=4,
leading=30,
)
subtitle_style = ParagraphStyle(
'SubtitleStyle',
fontName='Helvetica',
fontSize=12,
textColor=ACCENT_CYAN,
alignment=TA_CENTER,
spaceAfter=2,
)
source_style = ParagraphStyle(
'SourceStyle',
fontName='Helvetica-Oblique',
fontSize=9,
textColor=HexColor('#90A4AE'),
alignment=TA_CENTER,
spaceAfter=6,
)
section_title_style = ParagraphStyle(
'SectionTitle',
fontName='Helvetica-Bold',
fontSize=13,
textColor=WHITE,
alignment=TA_CENTER,
spaceAfter=2,
leading=16,
)
body_style = ParagraphStyle(
'BodyStyle',
fontName='Helvetica',
fontSize=9,
textColor=LIGHT_TEXT,
alignment=TA_JUSTIFY,
spaceAfter=4,
leading=13,
)
bullet_style = ParagraphStyle(
'BulletStyle',
fontName='Helvetica',
fontSize=8.5,
textColor=LIGHT_TEXT,
alignment=TA_LEFT,
leftIndent=10,
bulletIndent=2,
spaceAfter=2,
leading=12,
)
small_bold = ParagraphStyle(
'SmallBold',
fontName='Helvetica-Bold',
fontSize=8,
textColor=ACCENT_GOLD,
alignment=TA_CENTER,
)
footnote_style = ParagraphStyle(
'Footnote',
fontName='Helvetica-Oblique',
fontSize=7.5,
textColor=HexColor('#78909C'),
alignment=TA_CENTER,
)
card_header_style = ParagraphStyle(
'CardHeader',
fontName='Helvetica-Bold',
fontSize=10,
textColor=WHITE,
alignment=TA_CENTER,
leading=13,
)
card_body_style = ParagraphStyle(
'CardBody',
fontName='Helvetica',
fontSize=8,
textColor=LIGHT_TEXT,
alignment=TA_LEFT,
leading=11,
)
arrow_style = ParagraphStyle(
'ArrowStyle',
fontName='Helvetica-Bold',
fontSize=9,
textColor=ACCENT_GOLD,
alignment=TA_CENTER,
)
# ── Helper to make a colored section banner ────────────────────────────────
def section_banner(title, icon, bg_color):
data = [[Paragraph(f'{icon} {title}', section_title_style)]]
t = Table(data, colWidths=[17*cm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg_color),
('ROUNDEDCORNERS', [8]),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
('LEFTPADDING', (0,0), (-1,-1), 12),
]))
return t
def make_change_table(rows, accent):
"""rows: list of (Parameter, Change, Clinical Note)"""
header = [
Paragraph('Parameter', ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
Paragraph('Change', ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
Paragraph('Clinical Significance', ParagraphStyle('th', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
]
table_data = [header]
for i, (param, change, note) in enumerate(rows):
bg = ROW_ALT if i % 2 == 0 else CARD_BG_1
# Determine change color
if '+' in str(change):
chg_color = HexColor('#69F0AE') # green for increase
elif '-' in str(change):
chg_color = HexColor('#FF5252') # red for decrease
else:
chg_color = ACCENT_GOLD
row = [
Paragraph(param, ParagraphStyle('td', fontName='Helvetica', fontSize=8.5, textColor=LIGHT_TEXT, alignment=TA_LEFT)),
Paragraph(f'<b>{change}</b>', ParagraphStyle('chg', fontName='Helvetica-Bold', fontSize=9, textColor=chg_color, alignment=TA_CENTER)),
Paragraph(note, ParagraphStyle('note', fontName='Helvetica', fontSize=8, textColor=HexColor('#B0BEC5'), alignment=TA_LEFT)),
]
table_data.append(row)
t = Table(table_data, colWidths=[5.5*cm, 2.5*cm, 9*cm])
style = [
('BACKGROUND', (0,0), (-1,0), accent),
('GRID', (0,0), (-1,-1), 0.3, HexColor('#2C3E50')),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('RIGHTPADDING', (0,0), (-1,-1), 6),
('ROWBACKGROUNDS', (0,1), (-1,-1), [ROW_ALT, CARD_BG_1]),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]
t.setStyle(TableStyle(style))
return t
# ── Document setup ─────────────────────────────────────────────────────────
OUTPUT = '/home/daytona/workspace/pregnancy-physiology/Physiological_Changes_During_Pregnancy.pdf'
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=1.8*cm,
leftMargin=1.8*cm,
topMargin=1.5*cm,
bottomMargin=1.5*cm,
)
story = []
# ══════════════════════════════════════════════════════════════════════════════
# HEADER BLOCK
# ══════════════════════════════════════════════════════════════════════════════
header_data = [[
Paragraph('PHYSIOLOGICAL CHANGES<br/>DURING PREGNANCY', title_style),
]]
header_table = Table(header_data, colWidths=[17*cm])
header_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), DARK_BG),
('TOPPADDING', (0,0), (-1,-1), 18),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
('ROUNDEDCORNERS', [10]),
]))
story.append(header_table)
story.append(Paragraph('A Visual Medical Summary for Students & Clinicians', subtitle_style))
story.append(Paragraph('Source: Morgan & Mikhail\'s Clinical Anesthesiology, 7e | p. 1572–1586', source_style))
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════════════════
# QUICK REFERENCE NUMBERS TABLE
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_banner('QUICK REFERENCE: KEY NUMBERS AT A GLANCE', '📊', TABLE_HDR))
story.append(Spacer(1, 2*mm))
qr_data = [
[Paragraph('System', small_bold), Paragraph('Parameter', small_bold),
Paragraph('Change', small_bold), Paragraph('System', small_bold),
Paragraph('Parameter', small_bold), Paragraph('Change', small_bold)],
# col 1 col 2
['Neuro', 'MAC', Paragraph('<b><font color="#FF5252">-40%</font></b>', arrow_style),
'CVS', 'Blood Volume', Paragraph('<b><font color="#69F0AE">+35%</font></b>', arrow_style)],
['Resp', 'O₂ Consumption', Paragraph('<b><font color="#69F0AE">+20-50%</font></b>', arrow_style),
'CVS', 'Plasma Volume', Paragraph('<b><font color="#69F0AE">+55%</font></b>', arrow_style)],
['Resp', 'FRC', Paragraph('<b><font color="#FF5252">-20%</font></b>', arrow_style),
'CVS', 'Cardiac Output', Paragraph('<b><font color="#69F0AE">+40%</font></b>', arrow_style)],
['Resp', 'Minute Ventilation', Paragraph('<b><font color="#69F0AE">+50%</font></b>', arrow_style),
'CVS', 'Heart Rate', Paragraph('<b><font color="#69F0AE">+20%</font></b>', arrow_style)],
['Resp', 'Tidal Volume', Paragraph('<b><font color="#69F0AE">+40%</font></b>', arrow_style),
'CVS', 'Stroke Volume', Paragraph('<b><font color="#69F0AE">+30%</font></b>', arrow_style)],
['Resp', 'PaCO₂', Paragraph('<b><font color="#FF5252">-15%</font></b>', arrow_style),
'CVS', 'DBP', Paragraph('<b><font color="#FF5252">-15%</font></b>', arrow_style)],
['Resp', 'Airway Resistance', Paragraph('<b><font color="#FF5252">-35%</font></b>', arrow_style),
'Haem', 'Hemoglobin', Paragraph('<b><font color="#FF5252">-20%</font></b>', arrow_style)],
['Renal', 'GFR', Paragraph('<b><font color="#69F0AE">+50%</font></b>', arrow_style),
'Haem', 'Clotting Factors', Paragraph('<b><font color="#69F0AE">+30-250%</font></b>', arrow_style)],
]
cell_style = ParagraphStyle('cs', fontName='Helvetica', fontSize=8.5, textColor=LIGHT_TEXT, alignment=TA_LEFT)
qr_table = Table(qr_data, colWidths=[2.2*cm, 4*cm, 2.3*cm, 2.2*cm, 4*cm, 2.3*cm])
qr_style = TableStyle([
('BACKGROUND', (0,0), (-1,0), HexColor('#1565C0')),
('TEXTCOLOR', (0,0), (-1,0), WHITE),
('FONTNAME', (0,0), (-1,0), 'Helvetica-Bold'),
('FONTSIZE', (0,0), (-1,0), 8),
('ROWBACKGROUNDS', (0,1), (-1,-1), [CARD_BG_1, ROW_ALT]),
('GRID', (0,0), (-1,-1), 0.3, HexColor('#2C3E50')),
('TOPPADDING', (0,0), (-1,-1), 4),
('BOTTOMPADDING', (0,0), (-1,-1), 4),
('LEFTPADDING', (0,0), (-1,-1), 5),
('RIGHTPADDING', (0,0), (-1,-1), 5),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
('FONTNAME', (0,1), (0,-1), 'Helvetica-Bold'),
('FONTNAME', (3,1), (3,-1), 'Helvetica-Bold'),
('TEXTCOLOR', (0,1), (0,-1), ACCENT_CYAN),
('TEXTCOLOR', (3,1), (3,-1), ACCENT_CYAN),
('ALIGN', (2,0), (2,-1), 'CENTER'),
('ALIGN', (5,0), (5,-1), 'CENTER'),
])
qr_table.setStyle(qr_style)
story.append(qr_table)
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════════════════
# 1. NEUROLOGICAL
# ══════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_banner('1. NEUROLOGICAL / CNS CHANGES', '🧠', HexColor('#4A148C')),
Spacer(1, 2*mm),
make_change_table([
('MAC (Min. Alveolar Conc.)', '-40%', 'Reduced anesthetic requirement at term; MAC returns to normal by day 3 post-delivery'),
('Sensitivity to local anesthetics', 'Increased', 'Neural blockade at lower concentrations due to progesterone & engorgement of epidural veins'),
('Epidural space volume', 'Decreased', 'Engorged epidural veins -> smaller epidural/intrathecal volume -> wider block spread'),
], HexColor('#6A1B9A')),
Spacer(1, 2*mm),
Paragraph(
'<b>Mechanism:</b> Progesterone (up to 20x normal at term) increases sedation. '
'β-endorphin surge during labor contributes. Enhanced sensitivity to local anesthetics '
'is partly neuronal (progesterone effect) and partly mechanical (engorged epidural veins '
'reduce volume of epidural/subarachnoid space).',
body_style
),
Spacer(1, 3*mm),
]))
# ══════════════════════════════════════════════════════════════════════════════
# 2. RESPIRATORY
# ══════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_banner('2. RESPIRATORY CHANGES', '🫁', HexColor('#01579B')),
Spacer(1, 2*mm),
make_change_table([
('Oxygen Consumption', '+20 to 50%', 'Meets increased metabolic demands of fetus, uterus, and maternal tissue'),
('Minute Ventilation', '+50%', 'Mainly via ↑ tidal volume (progesterone-driven); PaCO₂ drops to ~32 mmHg'),
('Tidal Volume', '+40%', 'Primary driver of hyperventilation; progesterone stimulates respiratory centers'),
('Respiratory Rate', '+15%', 'Minor contributor to increased minute ventilation'),
('Functional Residual Capacity (FRC)', '-20%', 'Elevated diaphragm from gravid uterus; makes mother prone to rapid desaturation'),
('Airway Resistance', '-35%', 'Progesterone-mediated bronchodilation'),
('PaO₂', '+10%', 'Rises to ~106 mmHg near sea level; hyperventilation effect'),
('PaCO₂', '-15%', 'Drops to ~32 mmHg; compensatory metabolic acidosis (HCO₃⁻ drops to ~20 mEq/L)'),
('HCO₃⁻', '-15%', 'Renal compensation for respiratory alkalosis; normal pH ~7.44'),
('P50 (Hb-O₂ curve)', '+27→30 mmHg', 'Right shift facilitates O₂ offloading to fetus'),
], HexColor('#0277BD')),
Spacer(1, 2*mm),
Paragraph(
'<b>Key clinical points:</b> The ↓ FRC combined with ↑ O₂ consumption means that apnea '
'(e.g., during intubation) leads to much faster O₂ desaturation than in non-pregnant patients - '
'pre-oxygenation is critical. Airway edema (progesterone/estrogen) can make intubation difficult; '
'a smaller ETT (6.0-7.0 mm) is often recommended. The physiological respiratory alkalosis '
'(PaCO₂ ~32 mmHg) is the normal state; a "normal" PaCO₂ of 40 mmHg in pregnancy suggests '
'impending respiratory failure.',
body_style
),
Spacer(1, 3*mm),
]))
# ══════════════════════════════════════════════════════════════════════════════
# 3. CARDIOVASCULAR
# ══════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_banner('3. CARDIOVASCULAR CHANGES', '❤️', HexColor('#B71C1C')),
Spacer(1, 2*mm),
make_change_table([
('Blood Volume', '+35%', 'Starts rising in 1st trimester; peaks at 28-32 weeks; helps offset blood loss at delivery'),
('Plasma Volume', '+55%', 'Exceeds RBC expansion -> physiological "dilutional" anemia of pregnancy'),
('Cardiac Output', '+40%', 'Rises by 8-10 weeks; peaks near end of 2nd trimester; further increases during labor'),
('Stroke Volume', '+30%', 'Due to increased preload and decreased afterload'),
('Heart Rate', '+20%', 'Rises by ~15-20 bpm; tachycardia >100 bpm is normal in late pregnancy'),
('Systolic BP', '-5%', 'Slight decrease; drops most in 2nd trimester, rises back to baseline at term'),
('Diastolic BP', '-15%', 'More pronounced drop; nadirs around 24-28 weeks'),
('Peripheral Vascular Resistance', '-15%', 'Progesterone + placental arteriovenous shunt + vasodilation mediators'),
('Pulmonary Vascular Resistance', '-30%', 'Reduces RV afterload; important in pulmonary hypertension management'),
], HexColor('#C62828')),
Spacer(1, 2*mm),
Paragraph(
'<b>Aortocaval compression:</b> After 18-20 weeks, the gravid uterus can compress the '
'inferior vena cava and abdominal aorta when supine, reducing venous return by up to 40% '
'("supine hypotension syndrome"). Left uterine displacement (LUD) is standard of care during '
'labor and all obstetric procedures. Cardiac output further increases during labor '
'(+10-25% in first stage, up to +45% in second stage) due to autotransfusion from uterine '
'contractions, pain, and anxiety.',
body_style
),
Spacer(1, 3*mm),
]))
# ══════════════════════════════════════════════════════════════════════════════
# 4. HEMATOLOGICAL
# ══════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_banner('4. HEMATOLOGICAL CHANGES', '🩸', HexColor('#E65100')),
Spacer(1, 2*mm),
make_change_table([
('Hemoglobin', '-20%', 'Physiological dilutional anemia; Hgb <11 g/dL in 1st trimester or <10.5 g/dL in 2nd trimester is pathological'),
('Hematocrit', 'Decreases', 'Falls from ~40% to ~34%; nadirs at ~30-34 weeks'),
('Platelets', '-10%', 'Mild thrombocytopenia is normal; <70,000 warrants investigation'),
('WBC', 'Increased', 'Leukocytosis up to 12,000/μL; during labor may reach 20,000-30,000/μL'),
('Fibrinogen', 'Doubles', 'Rises from ~300 to 600 mg/dL; ESR increases markedly'),
('Clotting Factors (II,VII,VIII,X,XII)', '+30 to 250%', 'Pregnancy is a hypercoagulable state; risk of DVT/PE increases 4-5x'),
('Protein C & S', 'Decreased', 'Further tilts balance toward thrombosis; fibrinolytic activity decreased'),
('Serum albumin', 'Decreased', 'From ~4.0 to ~3.0 g/dL; affects protein-bound drug pharmacokinetics'),
], HexColor('#E64A19')),
Spacer(1, 2*mm),
Paragraph(
'<b>Coagulation implications:</b> The hypercoagulable state protects against hemorrhage at delivery '
'but increases thromboembolism risk 4-5 fold. Virchow\'s triad is complete in pregnancy: '
'hypercoagulability (clotting factor excess), venous stasis (uterine compression), and '
'endothelial injury (delivery). VTE prophylaxis should be considered in high-risk patients.',
body_style
),
Spacer(1, 3*mm),
]))
# ══════════════════════════════════════════════════════════════════════════════
# 5. RENAL
# ══════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_banner('5. RENAL & METABOLIC CHANGES', '🫘', HexColor('#1B5E20')),
Spacer(1, 2*mm),
make_change_table([
('Glomerular Filtration Rate (GFR)', '+50%', 'Starts rising in 1st trimester; renal plasma flow increases 70%; kidneys enlarge ~1 cm'),
('Serum Creatinine', 'Decreased', 'Normal creatinine 0.5-0.8 mg/dL in pregnancy; "normal" 1.0 mg/dL suggests renal impairment'),
('Serum BUN', 'Decreased', 'Lower due to increased GFR; normal ~8-9 mg/dL'),
('Serum Uric Acid', 'Decreased early', 'Rises in 3rd trimester; elevated uric acid is a marker of preeclampsia'),
('Glucosuria', 'May be present', 'Tubular reabsorption of glucose does not increase proportionally with GFR'),
('Urinary protein', 'Up to 300 mg/day', 'Normal upper limit doubles; >300 mg/24h suggests preeclampsia'),
('Sodium retention', 'Increased', 'Despite elevated GFR; estrogen/aldosterone effect; contributes to edema'),
], HexColor('#2E7D32')),
Spacer(1, 2*mm),
Paragraph(
'<b>Drug dosing:</b> Increased renal clearance affects many drugs (penicillins, aminoglycosides, '
'digoxin) - doses may need to be higher or more frequent. Serum creatinine and BUN are normally '
'lower; values in the "normal" non-pregnant range may indicate significant renal dysfunction.',
body_style
),
Spacer(1, 3*mm),
]))
# ══════════════════════════════════════════════════════════════════════════════
# 6. GASTROINTESTINAL
# ══════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_banner('6. GASTROINTESTINAL CHANGES', '🤰', HexColor('#4E342E')),
Spacer(1, 2*mm),
make_change_table([
('Gastric emptying', 'Delayed', 'Progesterone reduces GI motility; risk of aspiration during anesthesia (Mendelson syndrome)'),
('Lower esophageal sphincter tone', 'Decreased', 'Progesterone effect; combined with increased intragastric pressure -> GERD in >80%'),
('Intragastric pressure', 'Increased', 'Gravid uterus elevates stomach; increases aspiration risk'),
('Nausea & vomiting', 'Common', 'Affects 70-80%; hyperemesis gravidarum in <2%; peaks at 8-10 weeks'),
('Liver enzymes (ALK-P)', 'Increased', 'Placenta produces alkaline phosphatase; ALT/AST should remain normal'),
('Serum albumin', 'Decreased', 'Dilutional; from 4.0 -> 3.0 g/dL; affects drug binding'),
('Gallbladder emptying', 'Delayed', 'Bile becomes more lithogenic; cholesterol gallstones 2x more common in pregnancy'),
('Constipation', 'Common', 'Reduced colonic motility; water absorption; iron supplementation worsens this'),
], HexColor('#5D4037')),
Spacer(1, 2*mm),
Paragraph(
'<b>Aspiration risk:</b> All pregnant patients beyond 12-16 weeks are considered to have a '
'"full stomach" for anesthesia purposes, regardless of fasting time. Rapid sequence '
'induction (RSI) with cricoid pressure is the standard for general anesthesia. '
'Antacid prophylaxis (sodium citrate, H2-blockers, PPI) is recommended.',
body_style
),
Spacer(1, 3*mm),
]))
# ══════════════════════════════════════════════════════════════════════════════
# 7. ENDOCRINE / HORMONAL
# ══════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_banner('7. ENDOCRINE & HORMONAL CHANGES', '⚗️', HexColor('#006064')),
Spacer(1, 2*mm),
make_change_table([
('Progesterone', 'Up to 20x normal', 'Sedation, bronchodilation, reduced GI motility, decreased LES tone, vasodilation'),
('Estrogen', 'Greatly increased', 'Airway mucosal edema, clotting factor synthesis, increased binding proteins'),
('hCG (human chorionic gonadotropin)', 'Peaks at 8-10 wks', 'Maintains corpus luteum; basis of pregnancy tests; elevated in molar pregnancy'),
('hPL (human placental lactogen)', 'Increases throughout', 'Insulin antagonist; promotes lipolysis; causes gestational insulin resistance'),
('Insulin resistance', 'Increases', 'Peaks in 3rd trimester; gestational diabetes in ~7-10% of pregnancies'),
('Thyroid hormones (total T3/T4)', 'Increased', 'TBG doubles; free T3/T4 remain normal; TSH may transiently decrease in 1st trimester'),
('Cortisol', 'Increased', '3x normal at term; partly explains glucose intolerance and striae gravidarum'),
('Aldosterone', 'Increased 10x', 'Compensates for progesterone-induced Na loss; contributes to fluid retention'),
], HexColor('#00838F')),
Spacer(1, 3*mm),
]))
# ══════════════════════════════════════════════════════════════════════════════
# 8. MUSCULOSKELETAL / OTHER
# ══════════════════════════════════════════════════════════════════════════════
story.append(KeepTogether([
section_banner('8. MUSCULOSKELETAL & OTHER CHANGES', '🦴', HexColor('#37474F')),
Spacer(1, 2*mm),
make_change_table([
('Lumbar lordosis', 'Increases', 'Compensates for anterior shift in center of gravity; causes back pain in ~50%'),
('Relaxin hormone', 'Increased', 'Relaxes pelvic ligaments (pubic symphysis, sacroiliac joints) for delivery'),
('Uterus weight', '60g -> 1000g', 'Increases 20-fold; blood flow rises from 50 ml/min to 500-700 ml/min at term'),
('Skin changes', 'Striae, linea nigra', 'Hyperpigmentation (MSH from placenta), spider angiomata, palmar erythema'),
('Eye changes', 'Corneal thickening', 'Avoid new contact lens prescriptions; IOP decreases; visual changes common'),
('Body weight', '+11-16 kg average', 'Fetus ~3.3 kg; placenta ~0.6 kg; amniotic fluid ~0.8 kg; rest is maternal tissue'),
('Total body water', '+6-8 L', 'Distributed: plasma, interstitial, amniotic fluid; dependent edema in 80% is normal'),
], HexColor('#455A64')),
Spacer(1, 3*mm),
]))
# ══════════════════════════════════════════════════════════════════════════════
# CLINICAL PEARLS BOX
# ══════════════════════════════════════════════════════════════════════════════
pearls_data = [[
Paragraph(
'<b><font color="#FFD600">🏆 HIGH-YIELD CLINICAL PEARLS</font></b><br/><br/>'
'<font color="#69F0AE"><b>1.</b></font> <font color="#ECEFF1">Rapid desaturation during apnea</font> '
'- ↓FRC + ↑O₂ consumption = short safe apnea time. Pre-oxygenate 3-5 min before induction.<br/>'
'<font color="#69F0AE"><b>2.</b></font> <font color="#ECEFF1">Supine hypotension</font> '
'- IVC compression after 18-20 wks. Always use LEFT LATERAL TILT 15°.<br/>'
'<font color="#69F0AE"><b>3.</b></font> <font color="#ECEFF1">Full stomach risk</font> '
'- Use RSI for all GA in pregnancy >12-16 wks regardless of fasting status.<br/>'
'<font color="#69F0AE"><b>4.</b></font> <font color="#ECEFF1">Dilutional anemia</font> '
'- Hgb 10-11 g/dL is normal in pregnancy (not pathological if other causes excluded).<br/>'
'<font color="#69F0AE"><b>5.</b></font> <font color="#ECEFF1">PaCO₂ "normal" = danger</font> '
'- Normal non-pregnant PaCO₂ (40 mmHg) in pregnancy = respiratory FAILURE (normal = 32 mmHg).<br/>'
'<font color="#69F0AE"><b>6.</b></font> <font color="#ECEFF1">Creatinine "normal" = impaired</font> '
'- Normal creatinine in pregnancy = 0.5-0.8 mg/dL. Value of 1.0 mg/dL warrants investigation.<br/>'
'<font color="#69F0AE"><b>7.</b></font> <font color="#ECEFF1">Hypercoagulability</font> '
'- VTE risk 4-5x higher. Immobility + surgery = thromboprophylaxis is essential.<br/>'
'<font color="#69F0AE"><b>8.</b></font> <font color="#ECEFF1">Reduced MAC</font> '
'- Need 40% less volatile anesthetic; also more sensitive to IV sedatives and opioids.',
ParagraphStyle('pearls', fontName='Helvetica', fontSize=8.5, textColor=LIGHT_TEXT,
leading=14, alignment=TA_LEFT)
)
]]
pearls_table = Table(pearls_data, colWidths=[17*cm])
pearls_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), HexColor('#0A1628')),
('BOX', (0,0), (-1,-1), 1.5, ACCENT_GOLD),
('TOPPADDING', (0,0), (-1,-1), 12),
('BOTTOMPADDING', (0,0), (-1,-1), 12),
('LEFTPADDING', (0,0), (-1,-1), 14),
('RIGHTPADDING', (0,0), (-1,-1), 14),
('ROUNDEDCORNERS', [8]),
]))
story.append(pearls_table)
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════════════════
# TRIMESTER TIMELINE
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_banner('TIMELINE: WHEN CHANGES OCCUR', '📅', HexColor('#1A237E')))
story.append(Spacer(1, 2*mm))
timeline_data = [
[
Paragraph('<b>1st Trimester (0-12 wks)</b>', ParagraphStyle('t1h', fontName='Helvetica-Bold', fontSize=9, textColor=ACCENT_GOLD, alignment=TA_CENTER)),
Paragraph('<b>2nd Trimester (13-26 wks)</b>', ParagraphStyle('t2h', fontName='Helvetica-Bold', fontSize=9, textColor=ACCENT_GREEN, alignment=TA_CENTER)),
Paragraph('<b>3rd Trimester (27-40 wks)</b>', ParagraphStyle('t3h', fontName='Helvetica-Bold', fontSize=9, textColor=ACCENT_PINK, alignment=TA_CENTER)),
],
[
Paragraph(
'• ↑hCG peaks 8-10 wks\n• Nausea/vomiting peak\n• ↑Progesterone & estrogen\n'
'• Plasma volume starts ↑\n• ↑Heart rate begins\n• GFR starts increasing\n'
'• Progesterone -> ↑ventilation',
ParagraphStyle('t1b', fontName='Helvetica', fontSize=8, textColor=LIGHT_TEXT, leading=12)
),
Paragraph(
'• BP nadir at 24-28 wks\n• Plasma volume peaks (+55%)\n• CO peaks (late 2nd trimester)\n'
'• FRC decreases (diaphragm rises)\n• IVC compression risk begins\n'
'• Gestational DM screening (24-28 wks)\n• Fetal movements felt',
ParagraphStyle('t2b', fontName='Helvetica', fontSize=8, textColor=LIGHT_TEXT, leading=12)
),
Paragraph(
'• MAC -40% at term\n• Uterus 1000g; blood flow 700ml/min\n• Insulin resistance peaks\n'
'• Clotting factors max elevation\n• BP returns near baseline\n'
'• Lumbar lordosis maximal\n• RBC mass peaks',
ParagraphStyle('t3b', fontName='Helvetica', fontSize=8, textColor=LIGHT_TEXT, leading=12)
),
]
]
timeline_table = Table(timeline_data, colWidths=[5.6*cm, 5.6*cm, 5.6*cm])
timeline_table.setStyle(TableStyle([
('BACKGROUND', (0,0), (0,0), HexColor('#3E2700')),
('BACKGROUND', (1,0), (1,0), HexColor('#004D00')),
('BACKGROUND', (2,0), (2,0), HexColor('#4A0020')),
('BACKGROUND', (0,1), (0,1), CARD_BG_1),
('BACKGROUND', (1,1), (1,1), CARD_BG_2),
('BACKGROUND', (2,1), (2,1), CARD_BG_1),
('GRID', (0,0), (-1,-1), 0.5, HexColor('#2C3E50')),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
('LEFTPADDING', (0,0), (-1,-1), 8),
('VALIGN', (0,0), (-1,-1), 'TOP'),
]))
story.append(timeline_table)
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════════════════
# FOOTER
# ══════════════════════════════════════════════════════════════════════════════
story.append(HRFlowable(width='100%', thickness=0.5, color=HexColor('#37474F')))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
'Source: Morgan & Mikhail\'s Clinical Anesthesiology, 7e, Chapter 40 (pp. 1572-1587) | '
'Compiled for educational purposes | Orris Medical Summary',
footnote_style
))
# ══════════════════════════════════════════════════════════════════════════════
# BUILD
# ══════════════════════════════════════════════════════════════════════════════
def add_background(canvas, doc):
canvas.saveState()
canvas.setFillColor(DARK_BG)
canvas.rect(0, 0, A4[0], A4[1], fill=1, stroke=0)
canvas.restoreState()
doc.build(story, onFirstPage=add_background, onLaterPages=add_background)
print(f"PDF created: {OUTPUT}")
cd /home/daytona/workspace/pregnancy-physiology && python make_pdf.py 2>&1
Physiological Changes During Pregnancy
PDF Document · PDF
| System | Parameter | Change |
|---|---|---|
| Neuro | MAC | -40% |
| Resp | O₂ Consumption | +20-50% |
| Resp | FRC | -20% |
| Resp | Minute Ventilation | +50% |
| CVS | Plasma Volume | +55% |
| CVS | Cardiac Output | +40% |
| CVS | Heart Rate | +20% |
| Renal | GFR | +50% |
| Haem | Clotting Factors | +30-250% |
Can you make this pdf with more diagrams and flowchart for better visual memory
~/pregnancy-physiology/make_pdf_v2.py
#!/usr/bin/env python3
"""
Physiological Changes During Pregnancy - ENHANCED Visual PDF v2
With bar charts, flowcharts, body diagrams, and system maps
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.lib.colors import HexColor
from reportlab.graphics.shapes import (
Drawing, Rect, String, Line, Circle, Polygon, Ellipse,
Arrow, Group, Path
)
from reportlab.graphics import renderPDF
from reportlab.graphics.charts.barcharts import VerticalBarChart, HorizontalBarChart
from reportlab.graphics.charts.piecharts import Pie
from reportlab.platypus import Flowable
import math
# ── Palette ────────────────────────────────────────────────────────────────
DARK_BG = HexColor('#0A0F1E')
NAVY = HexColor('#0D1B2A')
CARD_DARK = HexColor('#0F1F35')
CARD_MID = HexColor('#112233')
ROW_ALT = HexColor('#1A3555')
HEADER_BG = HexColor('#1B4F72')
TABLE_HDR = HexColor('#1565C0')
PINK = HexColor('#E91E8C')
CYAN = HexColor('#00BCD4')
GOLD = HexColor('#FFD600')
GREEN = HexColor('#4CAF50')
ORANGE = HexColor('#FF6F00')
RED = HexColor('#EF5350')
PURPLE = HexColor('#7B1FA2')
TEAL = HexColor('#00897B')
BLUE = HexColor('#1E88E5')
LIME = HexColor('#C6FF00')
WHITE = colors.white
LIGHT_TEXT = HexColor('#ECEFF1')
DIM_TEXT = HexColor('#90A4AE')
W, H = A4 # 595.27 x 841.89 pts
# ── Reusable styles ────────────────────────────────────────────────────────
def S(name, **kw):
return ParagraphStyle(name, **kw)
title_s = S('T', fontName='Helvetica-Bold', fontSize=28, textColor=WHITE,
alignment=TA_CENTER, leading=34, spaceAfter=4)
sub_s = S('S', fontName='Helvetica', fontSize=11, textColor=CYAN,
alignment=TA_CENTER, spaceAfter=2)
src_s = S('SR', fontName='Helvetica-Oblique', fontSize=8.5, textColor=DIM_TEXT,
alignment=TA_CENTER, spaceAfter=6)
sec_s = S('SEC', fontName='Helvetica-Bold', fontSize=13, textColor=WHITE,
alignment=TA_CENTER, leading=16)
body_s = S('B', fontName='Helvetica', fontSize=8.5, textColor=LIGHT_TEXT,
alignment=TA_JUSTIFY, leading=13, spaceAfter=3)
bullet_s = S('BU', fontName='Helvetica', fontSize=8.5, textColor=LIGHT_TEXT,
alignment=TA_LEFT, leading=12, leftIndent=10, spaceAfter=2)
small_b = S('SB', fontName='Helvetica-Bold', fontSize=8, textColor=GOLD,
alignment=TA_CENTER)
note_s = S('N', fontName='Helvetica-Oblique', fontSize=7.5, textColor=DIM_TEXT,
alignment=TA_CENTER)
lbl_s = S('L', fontName='Helvetica-Bold', fontSize=8, textColor=WHITE,
alignment=TA_CENTER, leading=10)
lbl_sm = S('LS', fontName='Helvetica', fontSize=7, textColor=LIGHT_TEXT,
alignment=TA_CENTER, leading=9)
CONTENT_W = 17 * cm
# ═══════════════════════════════════════════════════════════════════════════
# DRAWING HELPERS
# ═══════════════════════════════════════════════════════════════════════════
class DrawingFlowable(Flowable):
def __init__(self, drawing):
Flowable.__init__(self)
self.drawing = drawing
self.width = drawing.width
self.height = drawing.height
def draw(self):
renderPDF.draw(self.drawing, self.canv, 0, 0)
def hex2rgb(h):
h = h.hexval()[1:]
return tuple(int(h[i:i+2], 16)/255 for i in (0,2,4))
def rounded_rect(d, x, y, w, h, r, fill_color, stroke_color=None, stroke_width=0.5):
p = Path()
p.moveTo(x+r, y)
p.lineTo(x+w-r, y)
p.curveTo(x+w-r, y, x+w, y, x+w, y+r)
p.lineTo(x+w, y+h-r)
p.curveTo(x+w, y+h-r, x+w, y+h, x+w-r, y+h)
p.lineTo(x+r, y+h)
p.curveTo(x+r, y+h, x, y+h, x, y+h-r)
p.lineTo(x, y+r)
p.curveTo(x, y+r, x, y, x+r, y)
p.closePath()
p.fillColor = fill_color
p.strokeColor = stroke_color
p.strokeWidth = stroke_width
d.add(p)
def arrow_line(d, x1, y1, x2, y2, color=GOLD, width=1.5):
"""Draw a line with an arrowhead at (x2,y2)."""
d.add(Line(x1, y1, x2, y2, strokeColor=color, strokeWidth=width))
# arrowhead
angle = math.atan2(y2-y1, x2-x1)
size = 6
ax1 = x2 - size*math.cos(angle-0.4)
ay1 = y2 - size*math.sin(angle-0.4)
ax2 = x2 - size*math.cos(angle+0.4)
ay2 = y2 - size*math.sin(angle+0.4)
d.add(Polygon([x2, y2, ax1, ay1, ax2, ay2],
fillColor=color, strokeColor=color, strokeWidth=0.3))
def text_box(d, x, y, w, h, text, bg, fg=WHITE, font='Helvetica-Bold', size=9, r=5):
rounded_rect(d, x, y, w, h, r, bg, HexColor('#2C3E50'), 0.5)
d.add(String(x+w/2, y+h/2-size*0.38, text,
textAnchor='middle', fontSize=size,
fontName=font, fillColor=fg))
# ═══════════════════════════════════════════════════════════════════════════
# CHART 1: Horizontal bar chart of % changes
# ═══════════════════════════════════════════════════════════════════════════
def make_bar_chart():
DW, DH = CONTENT_W, 7.2*cm
d = Drawing(DW, DH)
# Background
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
# Title
d.add(String(DW/2, DH-14, 'KEY PARAMETER CHANGES IN PREGNANCY (% change from baseline)',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=GOLD))
params = [
('Plasma Volume', +55, CYAN),
('Min. Ventilation', +50, BLUE),
('GFR', +50, GREEN),
('Cardiac Output', +40, PINK),
('Tidal Volume', +40, PINK),
('O₂ Consumption', +35, ORANGE),
('Blood Volume', +35, ORANGE),
('Heart Rate', +20, HexColor('#FF9800')),
('Clotting Factors', +30, RED),
('Stroke Volume', +30, LIME),
('FRC', -20, HexColor('#FF5252')),
('Hemoglobin', -20, HexColor('#FF5252')),
('MAC', -40, PURPLE),
('PaCO₂', -15, HexColor('#FF7043')),
('Pulm Resistance', -30, HexColor('#CE93D8')),
]
bar_area_x = 130
bar_area_w = DW - bar_area_x - 60
zero_x = bar_area_x + bar_area_w * 0.45
row_h = 14
start_y = DH - 32
max_val = 60
for i, (name, val, clr) in enumerate(params):
y = start_y - i * row_h
if y < 8:
break
# label
d.add(String(bar_area_x - 4, y-3, name,
textAnchor='end', fontSize=7, fontName='Helvetica',
fillColor=LIGHT_TEXT))
# bar
bar_len = abs(val) / max_val * bar_area_w * 0.45
if val > 0:
bx = zero_x
else:
bx = zero_x - bar_len
r2 = Rect(bx, y-2, bar_len if val > 0 else bar_len,
row_h - 4, fillColor=clr, strokeColor=None)
r2.rx = 2
d.add(r2)
# value text
tx = zero_x + bar_len + 3 if val > 0 else zero_x - bar_len - 3
anchor = 'start' if val > 0 else 'end'
sign = '+' if val > 0 else ''
d.add(String(tx, y-3, f'{sign}{val}%',
textAnchor=anchor, fontSize=7, fontName='Helvetica-Bold',
fillColor=clr))
# zero line
d.add(Line(zero_x, 12, zero_x, DH-22,
strokeColor=HexColor('#546E7A'), strokeWidth=1, strokeDashArray=[3,2]))
d.add(String(zero_x, 6, '0%', textAnchor='middle', fontSize=7,
fontName='Helvetica', fillColor=DIM_TEXT))
# Legend
d.add(String(bar_area_x+bar_area_w*0.1, 6, '← DECREASE',
textAnchor='middle', fontSize=7, fontName='Helvetica-Bold',
fillColor=HexColor('#FF5252')))
d.add(String(bar_area_x+bar_area_w*0.8, 6, 'INCREASE →',
textAnchor='middle', fontSize=7, fontName='Helvetica-Bold',
fillColor=HexColor('#69F0AE')))
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# CHART 2: Body system diagram
# ═══════════════════════════════════════════════════════════════════════════
def make_body_diagram():
DW, DH = CONTENT_W, 9.5*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-14, 'BODY SYSTEMS AFFECTED IN PREGNANCY',
textAnchor='middle', fontSize=10, fontName='Helvetica-Bold', fillColor=GOLD))
# Draw simplified body silhouette
cx = DW/2
# Head
d.add(Ellipse(cx, DH-50, 22, 22, fillColor=HexColor('#1C3A5E'), strokeColor=CYAN, strokeWidth=1.2))
d.add(String(cx, DH-54, 'CNS', textAnchor='middle', fontSize=7, fontName='Helvetica-Bold', fillColor=CYAN))
# Neck
d.add(Rect(cx-8, DH-80, 16, 14, fillColor=HexColor('#1C3A5E'), strokeColor=None))
# Torso
rounded_rect(d, cx-40, DH-185, 80, 100, 10, HexColor('#0F2840'), CYAN, 1.2)
# Heart (left chest)
d.add(Ellipse(cx-15, DH-125, 16, 16,
fillColor=HexColor('#7B0000'), strokeColor=RED, strokeWidth=1.5))
d.add(String(cx-15, DH-129, '❤', textAnchor='middle', fontSize=10, fillColor=RED))
# Lungs (both sides)
d.add(Ellipse(cx-28, DH-130, 14, 22,
fillColor=HexColor('#01435A'), strokeColor=BLUE, strokeWidth=1.2))
d.add(Ellipse(cx+10, DH-130, 14, 22,
fillColor=HexColor('#01435A'), strokeColor=BLUE, strokeWidth=1.2))
# Uterus (lower torso)
d.add(Ellipse(cx, DH-170, 28, 24,
fillColor=HexColor('#880E4F'), strokeColor=PINK, strokeWidth=1.5))
d.add(String(cx, DH-174, 'Uterus', textAnchor='middle', fontSize=7,
fontName='Helvetica-Bold', fillColor=PINK))
# Kidneys
d.add(Ellipse(cx-38, DH-148, 10, 16,
fillColor=HexColor('#1B5E20'), strokeColor=GREEN, strokeWidth=1.2))
d.add(Ellipse(cx+38, DH-148, 10, 16,
fillColor=HexColor('#1B5E20'), strokeColor=GREEN, strokeWidth=1.2))
# Arms
rounded_rect(d, cx-72, DH-185, 28, 80, 8, HexColor('#0D2035'), CYAN, 0.8)
rounded_rect(d, cx+44, DH-185, 28, 80, 8, HexColor('#0D2035'), CYAN, 0.8)
# Legs
rounded_rect(d, cx-35, DH-270, 28, 80, 8, HexColor('#0D2035'), CYAN, 0.8)
rounded_rect(d, cx+7, DH-270, 28, 80, 8, HexColor('#0D2035'), CYAN, 0.8)
# ── CALLOUT BOXES ──────────────────────────────────────────────────────
# Left side callouts
box_w, box_h = 105, 38
lx = 4
# CNS box
rounded_rect(d, lx, DH-70, box_w, box_h, 5, HexColor('#1A0A3E'), PURPLE, 0.8)
d.add(String(lx+box_w/2, DH-56, '🧠 CNS / NEURO',
textAnchor='middle', fontSize=8, fontName='Helvetica-Bold', fillColor=PURPLE))
d.add(String(lx+box_w/2, DH-68, 'MAC ↓40% | Local anesthetic sensitivity ↑',
textAnchor='middle', fontSize=6.5, fontName='Helvetica', fillColor=LIGHT_TEXT))
arrow_line(d, lx+box_w, DH-50, cx-22, DH-46, PURPLE)
# Cardiovascular
rounded_rect(d, lx, DH-125, box_w, 46, 5, HexColor('#1A0808'), RED, 0.8)
d.add(String(lx+box_w/2, DH-108, '❤️ CARDIOVASCULAR',
textAnchor='middle', fontSize=8, fontName='Helvetica-Bold', fillColor=RED))
for ii, txt in enumerate(['CO ↑40% HR ↑20% SV ↑30%',
'Blood vol ↑35% Plasma ↑55%',
'SVR ↓15% DBP ↓15%']):
d.add(String(lx+box_w/2, DH-118-ii*9, txt,
textAnchor='middle', fontSize=6, fontName='Helvetica', fillColor=LIGHT_TEXT))
arrow_line(d, lx+box_w, DH-102, cx-32, DH-118, RED)
# Respiratory
rounded_rect(d, lx, DH-180, box_w, 46, 5, HexColor('#001A2E'), BLUE, 0.8)
d.add(String(lx+box_w/2, DH-163, '🫁 RESPIRATORY',
textAnchor='middle', fontSize=8, fontName='Helvetica-Bold', fillColor=BLUE))
for ii, txt in enumerate(['MV ↑50% TV ↑40% FRC ↓20%',
'PaCO₂ ↓15% PaO₂ ↑10%',
'Airway resistance ↓35%']):
d.add(String(lx+box_w/2, DH-172-ii*9, txt,
textAnchor='middle', fontSize=6, fontName='Helvetica', fillColor=LIGHT_TEXT))
arrow_line(d, lx+box_w, DH-158, cx-42, DH-128, BLUE)
# Right side callouts
rx = cx + 48
# Renal
rounded_rect(d, rx, DH-125, box_w, 38, 5, HexColor('#0A2E0A'), GREEN, 0.8)
d.add(String(rx+box_w/2, DH-109, '🫘 RENAL',
textAnchor='middle', fontSize=8, fontName='Helvetica-Bold', fillColor=GREEN))
for ii, txt in enumerate(['GFR ↑50% RPF ↑70%',
'Creat ↓ BUN ↓ Uric acid ↓',
'Normal Cr = 0.5-0.8 mg/dL']):
d.add(String(rx+box_w/2, DH-118-ii*9, txt,
textAnchor='middle', fontSize=6, fontName='Helvetica', fillColor=LIGHT_TEXT))
arrow_line(d, rx, DH-106, cx+48, DH-135, GREEN)
# Uterus/Hormonal
rounded_rect(d, rx, DH-185, box_w, 52, 5, HexColor('#2E0A2E'), PINK, 0.8)
d.add(String(rx+box_w/2, DH-166, '🤰 UTERUS / HORMONAL',
textAnchor='middle', fontSize=8, fontName='Helvetica-Bold', fillColor=PINK))
for ii, txt in enumerate(['Progesterone ↑20x',
'Blood flow 50→700 ml/min',
'hCG peaks 8-10 wks',
'Relaxin: ligament laxity']):
d.add(String(rx+box_w/2, DH-175-ii*9, txt,
textAnchor='middle', fontSize=6, fontName='Helvetica', fillColor=LIGHT_TEXT))
arrow_line(d, rx, DH-162, cx+28, DH-162, PINK)
# Hematology (bottom left)
rounded_rect(d, lx, DH-235, box_w, 46, 5, HexColor('#1A0F00'), ORANGE, 0.8)
d.add(String(lx+box_w/2, DH-218, '🩸 HEMATOLOGY',
textAnchor='middle', fontSize=8, fontName='Helvetica-Bold', fillColor=ORANGE))
for ii, txt in enumerate(['Hgb ↓20% Plt ↓10% WBC ↑',
'Fibrinogen doubles',
'Clotting factors ↑30-250%']):
d.add(String(lx+box_w/2, DH-228-ii*9, txt,
textAnchor='middle', fontSize=6, fontName='Helvetica', fillColor=LIGHT_TEXT))
arrow_line(d, lx+box_w, DH-212, cx-30, DH-230, ORANGE)
# GI (bottom right)
rounded_rect(d, rx, DH-250, box_w, 55, 5, HexColor('#1A0A00'), HexColor('#FF8F00'), 0.8)
d.add(String(rx+box_w/2, DH-230, '🍽️ GASTROINTESTINAL',
textAnchor='middle', fontSize=8, fontName='Helvetica-Bold', fillColor=HexColor('#FF8F00')))
for ii, txt in enumerate(['Gastric emptying delayed',
'LES tone ↓ → GERD ↑',
'Aspiration risk ↑↑',
'Gallstones 2x more common']):
d.add(String(rx+box_w/2, DH-240-ii*9, txt,
textAnchor='middle', fontSize=6, fontName='Helvetica', fillColor=LIGHT_TEXT))
arrow_line(d, rx, DH-222, cx+20, DH-200, HexColor('#FF8F00'))
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# FLOWCHART: Aspiration risk chain
# ═══════════════════════════════════════════════════════════════════════════
def make_aspiration_flowchart():
DW, DH = CONTENT_W, 4.8*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-13, '⚠️ ASPIRATION RISK FLOWCHART IN PREGNANCY',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=RED))
boxes = [
('Progesterone ↑', HexColor('#4A148C'), 22),
('LES tone ↓\nGastric motility ↓', HexColor('#1A237E'), 22),
('Delayed emptying\n+ Gravid uterus', HexColor('#1B5E20'), 22),
('↑ Intragastric\npressure', HexColor('#BF360C'), 22),
('FULL STOMACH\nRISK ↑↑', RED, 22),
('RSI + Cricoid\npressure MANDATORY', HexColor('#006064'), 22),
]
bw = 73
bh = 38
gap = 10
total = len(boxes)*bw + (len(boxes)-1)*gap
start_x = (DW - total) / 2
y_center = DH/2 - 10
for i, (label, bg, _) in enumerate(boxes):
bx = start_x + i*(bw+gap)
rounded_rect(d, bx, y_center-bh/2, bw, bh, 6, bg, HexColor('#37474F'), 0.8)
lines = label.split('\n')
for j, ln in enumerate(lines):
d.add(String(bx+bw/2, y_center + (len(lines)-1)*5 - j*10,
ln, textAnchor='middle', fontSize=7,
fontName='Helvetica-Bold', fillColor=WHITE))
if i < len(boxes)-1:
arrow_line(d, bx+bw, y_center, bx+bw+gap, y_center, GOLD, 1.5)
# Bottom note
d.add(String(DW/2, 6, 'All pregnant patients >12-16 weeks treated as "full stomach" regardless of fasting time',
textAnchor='middle', fontSize=7, fontName='Helvetica-Oblique', fillColor=DIM_TEXT))
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# FLOWCHART: Cardiovascular cascade
# ═══════════════════════════════════════════════════════════════════════════
def make_cvs_flowchart():
DW, DH = CONTENT_W, 6.5*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-13, '❤️ CARDIOVASCULAR CHANGES - CAUSE & EFFECT',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=RED))
# Column 1: Causes
causes = [
('Estrogen\n+ Relaxin', HexColor('#4A0E82'), 50),
('Progesterone', HexColor('#1A237E'), 50),
('Placental\nA-V shunt', HexColor('#004D40'), 50),
]
# Column 2: Mechanisms
mechs = [
('SVR ↓ 15%', HexColor('#1B5E20'), 50),
('Plasma vol\n↑ 55%', HexColor('#0D47A1'), 50),
('O₂ demand ↑', HexColor('#E65100'), 50),
]
# Column 3: Effect
effects = [
('HR ↑ 20%', HexColor('#B71C1C'), 50),
('SV ↑ 30%', HexColor('#880E4F'), 50),
]
# Column 4: Outcome
outcomes = [
('CARDIAC OUTPUT\n↑ 40%', RED, 80),
]
col_x = [10, 140, 290, 400]
bw_c = [110, 110, 90, 130]
bh = 30
gap_v = 8
def draw_col(items, col_idx, start_y):
n = len(items)
total_h = n*bh + (n-1)*gap_v
y0 = start_y + (DH-50 - total_h) / 2
cx_arr = col_x[col_idx] + bw_c[col_idx]/2
for i, (lbl, bg, _) in enumerate(items):
by = y0 + i*(bh+gap_v)
rounded_rect(d, col_x[col_idx], by, bw_c[col_idx], bh, 5, bg, HexColor('#37474F'), 0.6)
lines = lbl.split('\n')
for j, ln in enumerate(lines):
d.add(String(cx_arr, by+bh/2+(len(lines)-1)*4-j*9,
ln, textAnchor='middle', fontSize=7.5,
fontName='Helvetica-Bold', fillColor=WHITE))
return y0, bh, n, gap_v
y0_c, _, n_c, gv_c = draw_col(causes, 0, 12)
y0_m, _, n_m, gv_m = draw_col(mechs, 1, 12)
y0_e, _, n_e, gv_e = draw_col(effects, 2, 12)
y0_o, _, n_o, gv_o = draw_col(outcomes, 3, 12)
# Arrows: causes -> mechs
for i in range(3):
sy = y0_c + i*(bh+gv_c) + bh/2
ey = y0_m + i*(bh+gv_m) + bh/2
arrow_line(d, col_x[0]+bw_c[0], sy, col_x[1], ey, CYAN, 1.2)
# mechs -> effects (fan in)
effect_ys = [y0_e + i*(bh+gv_e) + bh/2 for i in range(n_e)]
mech_ys = [y0_m + i*(bh+gv_m) + bh/2 for i in range(n_m)]
pairs = [(0,0),(1,1),(2,0),(2,1)]
for mi, ei in pairs:
arrow_line(d, col_x[1]+bw_c[1], mech_ys[mi], col_x[2], effect_ys[ei], GREEN, 1)
# effects -> outcome
out_y = y0_o + bh/2
for i in range(n_e):
sy = y0_e + i*(bh+gv_e) + bh/2
arrow_line(d, col_x[2]+bw_c[2], sy, col_x[3], out_y, GOLD, 1.5)
# Labels
for i, lbl in enumerate(['TRIGGERS', 'MECHANISMS', 'EFFECTS', 'RESULT']):
d.add(String(col_x[i]+bw_c[i]/2, DH-24, lbl,
textAnchor='middle', fontSize=7, fontName='Helvetica-Bold', fillColor=DIM_TEXT))
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# FLOWCHART: Respiratory cascade
# ═══════════════════════════════════════════════════════════════════════════
def make_resp_flowchart():
DW, DH = CONTENT_W, 5.2*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-13, '🫁 RESPIRATORY CHANGES - MECHANISM FLOW',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=BLUE))
# Row 1: progesterone chain
r1 = [
('Progesterone\n↑ 20x', PURPLE, True),
('Stimulates\nmedullary\nresp. center', HexColor('#1A237E'), True),
('Tidal Volume\n↑ 40%', BLUE, True),
('Minute Vent.\n↑ 50%', BLUE, True),
('PaCO₂\n↓ 15%\n(→32 mmHg)', HexColor('#1B5E20'), True),
]
bw, bh = 82, 42
gap = 9
total = len(r1)*bw + (len(r1)-1)*gap
start_x = (DW - total) / 2
y1 = DH - 70
for i, (lbl, bg, _) in enumerate(r1):
bx = start_x + i*(bw+gap)
rounded_rect(d, bx, y1, bw, bh, 5, bg, HexColor('#37474F'), 0.8)
lines = lbl.split('\n')
for j, ln in enumerate(lines):
d.add(String(bx+bw/2, y1+bh/2+(len(lines)-1)*5-j*9,
ln, textAnchor='middle', fontSize=7.5,
fontName='Helvetica-Bold', fillColor=WHITE))
if i < len(r1)-1:
arrow_line(d, bx+bw, y1+bh/2, bx+bw+gap, y1+bh/2, GOLD, 1.5)
# Bottom row: FRC danger chain
r2 = [
('Uterus ↑\ndiaphragm', HexColor('#37474F'), False),
('FRC\n↓ 20%', RED, False),
('+ O₂ demand\n↑ 35%', ORANGE, False),
('→ Rapid\ndesaturation\non apnea', RED, False),
('Pre-O₂\n3-5 min\nMANDATORY', HexColor('#004D40'), False),
]
y2 = 8
for i, (lbl, bg, _) in enumerate(r2):
bx = start_x + i*(bw+gap)
rounded_rect(d, bx, y2, bw, bh-4, 5, bg, HexColor('#37474F'), 0.8)
lines = lbl.split('\n')
for j, ln in enumerate(lines):
d.add(String(bx+bw/2, y2+(bh-4)/2+(len(lines)-1)*5-j*9,
ln, textAnchor='middle', fontSize=7.5,
fontName='Helvetica-Bold', fillColor=WHITE))
if i < len(r2)-1:
arrow_line(d, bx+bw, y2+(bh-4)/2, bx+bw+gap, y2+(bh-4)/2, HexColor('#FF5252'), 1.5)
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# COAGULATION cascade diagram
# ═══════════════════════════════════════════════════════════════════════════
def make_coag_diagram():
DW, DH = CONTENT_W, 4.5*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-13, '🩸 VIRCHOW\'S TRIAD IN PREGNANCY (VTE Risk ↑4-5x)',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=ORANGE))
# Triangle arrangement
cx = DW/2
tri_y_top = DH - 52
tri_y_bot = 14
# 3 nodes
nodes = [
(cx, tri_y_top + 18, 'HYPERCOAGULABILITY', 'Clotting factors ↑30-250%\nFibrinogen doubles\nProtein C/S ↓', HexColor('#7B0000')),
(cx - 140, tri_y_bot + 18, 'VENOUS STASIS', 'IVC compression\nReduced mobility\nPelvic veins dilated', HexColor('#01579B')),
(cx + 140, tri_y_bot + 18, 'ENDOTHELIAL INJURY', 'Delivery trauma\nCervical dilation\nSurgical delivery', HexColor('#1B5E20')),
]
node_w, node_h = 155, 42
node_positions = []
for (nx, ny, title, sub, bg) in nodes:
bx = nx - node_w/2
by = ny - node_h/2
node_positions.append((nx, ny))
rounded_rect(d, bx, by, node_w, node_h, 6, bg, HexColor('#37474F'), 1)
d.add(String(nx, ny+8, title, textAnchor='middle', fontSize=7.5,
fontName='Helvetica-Bold', fillColor=WHITE))
for i, ln in enumerate(sub.split('\n')):
d.add(String(nx, ny-2-i*9, ln, textAnchor='middle', fontSize=6.5,
fontName='Helvetica', fillColor=LIGHT_TEXT))
# Lines connecting them
for i in range(3):
for j in range(i+1, 3):
x1, y1 = node_positions[i]
x2, y2 = node_positions[j]
d.add(Line(x1, y1, x2, y2, strokeColor=GOLD, strokeWidth=1.5, strokeDashArray=[4,3]))
# Center label
d.add(String(cx, (tri_y_top+tri_y_bot)/2+10, 'VTE',
textAnchor='middle', fontSize=12, fontName='Helvetica-Bold', fillColor=RED))
d.add(String(cx, (tri_y_top+tri_y_bot)/2-4, 'RISK',
textAnchor='middle', fontSize=10, fontName='Helvetica-Bold', fillColor=RED))
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# TIMELINE diagram
# ═══════════════════════════════════════════════════════════════════════════
def make_timeline():
DW, DH = CONTENT_W, 5.8*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-13, '📅 TIMELINE OF KEY PHYSIOLOGICAL CHANGES',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=GOLD))
# Timeline line
line_y = DH/2 - 5
tl_x1, tl_x2 = 35, DW-15
d.add(Line(tl_x1, line_y, tl_x2, line_y, strokeColor=CYAN, strokeWidth=2))
# Arrow at end
d.add(Polygon([tl_x2+10, line_y, tl_x2, line_y+5, tl_x2, line_y-5],
fillColor=CYAN, strokeColor=CYAN))
# Week markers
weeks = [0, 8, 12, 16, 24, 28, 32, 40]
w_labels = ['0', '8', '12', '16', '24', '28', '32', '40 wks']
total_wks = 40
for wk, lbl in zip(weeks, w_labels):
px = tl_x1 + (wk/total_wks)*(tl_x2-tl_x1)
d.add(Line(px, line_y-5, px, line_y+5, strokeColor=CYAN, strokeWidth=1))
d.add(String(px, line_y-14, lbl, textAnchor='middle', fontSize=6.5,
fontName='Helvetica', fillColor=DIM_TEXT))
# Events - above line
events_above = [
(8, 'hCG peaks', PINK, 1),
(10, 'Nausea peak', HexColor('#FF8F00'), 1),
(12, 'Plasma vol ↑\nbegins', CYAN, 2),
(16, 'IVC risk\nstarts', RED, 1),
(24, 'BP nadir\nDGDM screen', GREEN, 2),
(32, 'Plasma vol\npeak +55%', BLUE, 1),
(40, 'MAC ↓40%\nmax term', PURPLE, 2),
]
# Events - below line
events_below = [
(6, 'GFR ↑\nbegins', GREEN, 1),
(8, 'CO ↑ starts', RED, 2),
(20, 'Fetal\nmovements', HexColor('#FF8F00'), 1),
(28, 'CO peaks', RED, 2),
(36, 'Clotting\nfactors max', ORANGE, 2),
(40, 'Uterus 1kg\n700ml/min', PINK, 1),
]
tick_h = 18
for (wk, lbl, clr, rows) in events_above:
px = tl_x1 + (wk/total_wks)*(tl_x2-tl_x1)
stem_h = tick_h + (rows-1)*10
d.add(Line(px, line_y+5, px, line_y+stem_h+4, strokeColor=clr, strokeWidth=0.8))
for i, ln in enumerate(lbl.split('\n')):
d.add(String(px, line_y+stem_h+4+i*8, ln,
textAnchor='middle', fontSize=6, fontName='Helvetica-Bold', fillColor=clr))
for (wk, lbl, clr, rows) in events_below:
px = tl_x1 + (wk/total_wks)*(tl_x2-tl_x1)
stem_h = tick_h + (rows-1)*10
d.add(Line(px, line_y-5, px, line_y-stem_h-4, strokeColor=clr, strokeWidth=0.8))
for i, ln in enumerate(lbl.split('\n')):
d.add(String(px, line_y-stem_h-12-i*8, ln,
textAnchor='middle', fontSize=6, fontName='Helvetica-Bold', fillColor=clr))
# Trimester shading bands
t1_end = tl_x1 + (12/40)*(tl_x2-tl_x1)
t2_end = tl_x1 + (28/40)*(tl_x2-tl_x1)
for (bx, bw2, lbl, clr) in [
(tl_x1, t1_end-tl_x1, 'T1', HexColor('#3E2700')),
(t1_end, t2_end-t1_end, 'T2', HexColor('#004D00')),
(t2_end, tl_x2-t2_end, 'T3', HexColor('#4A0020')),
]:
r = Rect(bx, line_y-3, bw2, 6, fillColor=clr, strokeColor=None)
d.add(r)
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# SUPINE HYPOTENSION DIAGRAM
# ═══════════════════════════════════════════════════════════════════════════
def make_supine_diagram():
DW, DH = CONTENT_W, 4.2*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-13, '⚠️ AORTOCAVAL COMPRESSION SYNDROME (Supine Hypotension)',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=RED))
steps = [
('Supine\nposition', HexColor('#37474F')),
('Uterus\ncompresses\nIVC + Aorta', RED),
('Venous return\n↓ up to 40%', HexColor('#7B0000')),
('Cardiac output\n↓ sharply', HexColor('#880E4F')),
('Maternal\nhypotension\n+ Fetal distress', RED),
('FIX: Left\nuterine tilt 15°', HexColor('#006064')),
]
bw, bh = 75, 44
gap = 8
total = len(steps)*bw + (len(steps)-1)*gap
start_x = (DW - total) / 2
y = DH/2 - bh/2 - 8
for i, (lbl, bg) in enumerate(steps):
bx = start_x + i*(bw+gap)
rounded_rect(d, bx, y, bw, bh, 5, bg, HexColor('#37474F'), 0.8)
lines = lbl.split('\n')
for j, ln in enumerate(lines):
d.add(String(bx+bw/2, y+bh/2+(len(lines)-1)*4.5-j*9,
ln, textAnchor='middle', fontSize=7,
fontName='Helvetica-Bold', fillColor=WHITE))
if i < len(steps)-1:
col = HexColor('#FF5252') if i < len(steps)-2 else GOLD
arrow_line(d, bx+bw, y+bh/2, bx+bw+gap, y+bh/2, col, 1.5)
d.add(String(DW/2, 6, 'Onset after 18-20 weeks gestation | Always use left lateral tilt for all procedures',
textAnchor='middle', fontSize=7, fontName='Helvetica-Oblique', fillColor=DIM_TEXT))
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# BLOOD VOLUME VISUAL (proportional circles)
# ═══════════════════════════════════════════════════════════════════════════
def make_volume_diagram():
DW, DH = CONTENT_W, 3.8*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-13, '📊 BLOOD VOLUME EXPANSION IN PREGNANCY',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=CYAN))
items = [
('Total Blood\nVolume', 35, CYAN, 55),
('Plasma\nVolume', 55, BLUE, 65),
('RBC\nMass', 25, RED, 45),
('Cardiac\nOutput', 40, PINK, 55),
('GFR', 50, GREEN, 60),
]
section_w = DW / len(items)
cy = DH/2 - 8
for i, (lbl, pct, clr, base_r) in enumerate(items):
cx2 = section_w*i + section_w/2
r_non = 18
r_preg = r_non * math.sqrt(1 + pct/100)
# Non-pregnant (dim)
d.add(Circle(cx2-12, cy, r_non, fillColor=HexColor('#1A3555'),
strokeColor=clr, strokeWidth=0.8))
d.add(String(cx2-12, cy-4, 'Pre', textAnchor='middle',
fontSize=6, fontName='Helvetica', fillColor=DIM_TEXT))
# Pregnant (bright)
d.add(Circle(cx2+16, cy, r_preg, fillColor=clr.__class__(
min(255, int(clr.red*255*0.4)), min(255, int(clr.green*255*0.4)),
min(255, int(clr.blue*255*0.4))),
strokeColor=clr, strokeWidth=1.5))
d.add(String(cx2+16, cy-4, 'Preg', textAnchor='middle',
fontSize=6, fontName='Helvetica-Bold', fillColor=WHITE))
# Arrow between
arrow_line(d, cx2-12+r_non+2, cy, cx2+16-r_preg-2, cy, GOLD, 1)
# Label and %
sign = '+' if pct > 0 else ''
d.add(String(cx2+2, DH-24, lbl.replace('\n', ' '),
textAnchor='middle', fontSize=6.5, fontName='Helvetica-Bold', fillColor=LIGHT_TEXT))
d.add(String(cx2+2, 5, f'{sign}{pct}%',
textAnchor='middle', fontSize=8, fontName='Helvetica-Bold', fillColor=clr))
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# SECTION BANNER helper
# ═══════════════════════════════════════════════════════════════════════════
def banner(title, icon, bg):
data = [[Paragraph(f'{icon} {title}', sec_s)]]
t = Table(data, colWidths=[CONTENT_W])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), bg),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
('LEFTPADDING', (0,0), (-1,-1), 14),
]))
return t
def make_table(rows, accent):
header = [
Paragraph('Parameter', S('h', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
Paragraph('Change', S('h', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
Paragraph('Clinical Significance', S('h', fontName='Helvetica-Bold', fontSize=9, textColor=WHITE, alignment=TA_CENTER)),
]
data = [header]
for param, change, note in rows:
clr = HexColor('#69F0AE') if '+' in str(change) else HexColor('#FF5252') if '-' in str(change) else GOLD
data.append([
Paragraph(param, S('p', fontName='Helvetica', fontSize=8.5, textColor=LIGHT_TEXT, alignment=TA_LEFT)),
Paragraph(f'<b>{change}</b>', S('c', fontName='Helvetica-Bold', fontSize=9, textColor=clr, alignment=TA_CENTER)),
Paragraph(note, S('n', fontName='Helvetica', fontSize=8, textColor=HexColor('#B0BEC5'), alignment=TA_LEFT)),
])
t = Table(data, colWidths=[5.5*cm, 2.5*cm, 9*cm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,0), accent),
('GRID', (0,0), (-1,-1), 0.3, HexColor('#2C3E50')),
('TOPPADDING', (0,0), (-1,-1), 5),
('BOTTOMPADDING', (0,0), (-1,-1), 5),
('LEFTPADDING', (0,0), (-1,-1), 6),
('ROWBACKGROUNDS', (0,1), (-1,-1), [ROW_ALT, CARD_MID]),
('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))
return t
# ═══════════════════════════════════════════════════════════════════════════
# CLINICAL PEARLS
# ═══════════════════════════════════════════════════════════════════════════
def make_pearls():
pearls = [
('🔴', 'Rapid desaturation', 'FRC ↓ + O₂ consumption ↑ = very short safe apnea time. Pre-oxygenate 3-5 min.', RED),
('🟡', 'Supine hypotension', 'IVC compression after 18-20 wks. Always use LEFT LATERAL TILT 15°.', GOLD),
('🟠', 'Full stomach risk', 'RSI mandatory for all GA >12-16 weeks regardless of fasting.', ORANGE),
('🩸', 'Dilutional anemia', 'Hgb 10-11 g/dL is NORMAL in pregnancy (plasma ↑ more than RBC).', HexColor('#EF9A9A')),
('💨', 'PaCO₂ "normal" = danger', 'Normal PaCO₂ in pregnancy = 32 mmHg. A reading of 40 mmHg = respiratory failure.', BLUE),
('🫘', 'Creatinine "normal" = impaired', 'Normal Cr in pregnancy = 0.5-0.8 mg/dL. Cr of 1.0 warrants investigation.', GREEN),
('🩺', 'Hypercoagulability', 'VTE risk 4-5x higher. Immobility + surgery = thromboprophylaxis essential.', ORANGE),
('💉', 'Reduced MAC', 'Need 40% less volatile anesthetic. Also more sensitive to IV sedatives and opioids.', PURPLE),
]
rows = []
for i in range(0, len(pearls), 2):
row = []
for j in range(2):
if i+j < len(pearls):
ico, title, body, clr = pearls[i+j]
cell_content = Paragraph(
f'<b><font color="#{clr.hexval()[1:]}">{ico} {title}</font></b><br/>'
f'<font color="#B0BEC5">{body}</font>',
S('pc', fontName='Helvetica', fontSize=8, textColor=LIGHT_TEXT,
leading=12, alignment=TA_LEFT)
)
row.append(cell_content)
else:
row.append('')
rows.append(row)
t = Table(rows, colWidths=[CONTENT_W/2 - 2*mm, CONTENT_W/2 - 2*mm])
t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), CARD_MID),
('GRID', (0,0), (-1,-1), 0.3, HexColor('#2C3E50')),
('TOPPADDING', (0,0), (-1,-1), 8),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
('LEFTPADDING', (0,0), (-1,-1), 10),
('VALIGN', (0,0), (-1,-1), 'TOP'),
('ROWBACKGROUNDS', (0,0), (-1,-1), [CARD_MID, ROW_ALT]),
]))
return t
# ═══════════════════════════════════════════════════════════════════════════
# HORMONAL EFFECTS diagram
# ═══════════════════════════════════════════════════════════════════════════
def make_hormones_diagram():
DW, DH = CONTENT_W, 5*cm
d = Drawing(DW, DH)
rounded_rect(d, 0, 0, DW, DH, 8, CARD_DARK, HexColor('#1A3555'), 0.8)
d.add(String(DW/2, DH-13, '⚗️ HORMONAL CHANGES & THEIR EFFECTS',
textAnchor='middle', fontSize=9, fontName='Helvetica-Bold', fillColor=CYAN))
hormones = [
('PROGESTERONE\n↑ up to 20x', HexColor('#4A148C'), 55, DH-48,
['MAC ↓', 'Bronchodilation', 'LES tone ↓', 'GI motility ↓', 'Vasodilation', 'Ventilation ↑']),
('ESTROGEN\n↑ markedly', HexColor('#880E4F'), 55, DH-48,
['Airway edema', 'Clotting factors ↑', 'TBG ↑', 'Binding proteins ↑', 'Na retention', 'Mucosal hyperemia']),
('hPL\n↑ throughout', HexColor('#E65100'), 55, DH-48,
['Insulin resistance', 'Lipolysis ↑', 'Glucose ↑', 'GDM risk ↑', 'Fetal nutrition', 'Anti-insulin']),
('CORTISOL\n↑ 3x', HexColor('#1B5E20'), 55, DH-48,
['Glucose tolerance ↓', 'Striae gravidarum', 'Immune modulation', 'Bone turnover ↑']),
('ALDOSTERONE\n↑ 10x', HexColor('#0D47A1'), 55, DH-48,
['Na retention', 'Fluid retention', 'Edema', 'K excretion ↑']),
]
n = len(hormones)
section_w = DW / n
hormone_y = DH - 55
h_bw = 85
h_bh = 30
for i, (name, bg, _, _, effects) in enumerate(hormones):
cx2 = section_w*i + section_w/2
# Hormone box
bx = cx2 - h_bw/2
rounded_rect(d, bx, hormone_y, h_bw, h_bh, 5, bg, HexColor('#37474F'), 0.8)
for j, ln in enumerate(name.split('\n')):
d.add(String(cx2, hormone_y+h_bh/2+5-j*11, ln,
textAnchor='middle', fontSize=7, fontName='Helvetica-Bold', fillColor=WHITE))
# Effect boxes below
eff_bh = 11
eff_gap = 3
for k, eff in enumerate(effects):
ey = hormone_y - (k+1)*(eff_bh+eff_gap)
if ey < 5:
break
eff_bg = HexColor(f'#{max(0, int(bg.red*255)-30):02x}'
f'{max(0, int(bg.green*255)-30):02x}'
f'{max(0, int(bg.blue*255)-30):02x}')
d.add(Rect(cx2-40, ey, 80, eff_bh, fillColor=eff_bg,
strokeColor=bg, strokeWidth=0.5))
d.add(String(cx2, ey+3.5, eff, textAnchor='middle', fontSize=6,
fontName='Helvetica', fillColor=LIGHT_TEXT))
# connector line
if k == 0:
d.add(Line(cx2, hormone_y, cx2, ey+eff_bh,
strokeColor=bg, strokeWidth=0.6, strokeDashArray=[2,2]))
return DrawingFlowable(d)
# ═══════════════════════════════════════════════════════════════════════════
# BUILD PDF
# ═══════════════════════════════════════════════════════════════════════════
OUTPUT = '/home/daytona/workspace/pregnancy-physiology/Physiological_Changes_Pregnancy_v2.pdf'
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
rightMargin=1.8*cm, leftMargin=1.8*cm,
topMargin=1.5*cm, bottomMargin=1.5*cm,
)
story = []
def add_bg(canvas, doc):
canvas.saveState()
canvas.setFillColor(DARK_BG)
canvas.rect(0, 0, A4[0], A4[1], fill=1, stroke=0)
canvas.restoreState()
# ── PAGE 1: Cover + Bar Chart + Body Diagram ──────────────────────────────
hdr_data = [[Paragraph('PHYSIOLOGICAL CHANGES<br/>DURING PREGNANCY', title_s)]]
hdr_t = Table(hdr_data, colWidths=[CONTENT_W])
hdr_t.setStyle(TableStyle([
('BACKGROUND', (0,0), (-1,-1), NAVY),
('TOPPADDING', (0,0), (-1,-1), 18),
('BOTTOMPADDING', (0,0), (-1,-1), 8),
]))
story.append(hdr_t)
story.append(Paragraph('A Visual Medical Summary — Systems, Flowcharts & Diagrams', sub_s))
story.append(Paragraph('Source: Morgan & Mikhail\'s Clinical Anesthesiology, 7e (pp. 1572–1587)', src_s))
story.append(Spacer(1, 3*mm))
# Bar chart
story.append(banner('MAGNITUDE OF CHANGES — AT A GLANCE', '📊', TABLE_HDR))
story.append(Spacer(1, 2*mm))
story.append(make_bar_chart())
story.append(Spacer(1, 4*mm))
# Body diagram
story.append(banner('BODY SYSTEMS MAP', '🏃', HexColor('#1A237E')))
story.append(Spacer(1, 2*mm))
story.append(make_body_diagram())
story.append(PageBreak())
# ── PAGE 2: CVS Flowchart + Volume diagram + Supine ──────────────────────
story.append(banner('CARDIOVASCULAR CHANGES — FLOWCHART', '❤️', HexColor('#B71C1C')))
story.append(Spacer(1, 2*mm))
story.append(make_cvs_flowchart())
story.append(Spacer(1, 4*mm))
story.append(banner('BLOOD VOLUME EXPANSION', '🔵', HexColor('#0D47A1')))
story.append(Spacer(1, 2*mm))
story.append(make_volume_diagram())
story.append(Spacer(1, 4*mm))
story.append(banner('SUPINE HYPOTENSION / AORTOCAVAL COMPRESSION', '⚠️', HexColor('#7B1515')))
story.append(Spacer(1, 2*mm))
story.append(make_supine_diagram())
story.append(Spacer(1, 4*mm))
# CVS detail table
story.append(make_table([
('Blood Volume', '+35%', 'Starts 1st trimester; peaks 28-32 weeks; buffers delivery blood loss'),
('Plasma Volume', '+55%', 'Exceeds RBC expansion → physiological dilutional anemia'),
('Cardiac Output', '+40%', 'Rises by 8-10 weeks; further ↑ during labor contractions (+10-45%)'),
('Stroke Volume', '+30%', 'Increased preload + decreased afterload'),
('Heart Rate', '+20%', 'Tachycardia >100 bpm is normal in late pregnancy'),
('Systolic BP', '-5%', 'Slight decrease; returns to near baseline at term'),
('Diastolic BP', '-15%', 'Nadir at 24-28 weeks; used to diagnose PIH/preeclampsia'),
('SVR', '-15%', 'Progesterone + placental AV shunt + prostacyclin'),
('Pulmonary resistance', '-30%', 'Reduces RV afterload; important in pulmonary hypertension'),
], HexColor('#C62828')))
story.append(PageBreak())
# ── PAGE 3: Respiratory ────────────────────────────────────────────────────
story.append(banner('RESPIRATORY CHANGES — MECHANISM FLOW', '🫁', HexColor('#01579B')))
story.append(Spacer(1, 2*mm))
story.append(make_resp_flowchart())
story.append(Spacer(1, 4*mm))
story.append(banner('ASPIRATION RISK FLOWCHART', '⚠️', HexColor('#7B1515')))
story.append(Spacer(1, 2*mm))
story.append(make_aspiration_flowchart())
story.append(Spacer(1, 4*mm))
story.append(make_table([
('O₂ Consumption', '+20–50%', 'Increased metabolic demands of fetus, uterus, placenta, breasts'),
('Minute Ventilation', '+50%', 'Progesterone stimulates medullary respiratory center'),
('Tidal Volume', '+40%', 'Primary driver; RR also rises +15%'),
('FRC', '-20%', 'Elevated diaphragm; ↓ ERV and RV; predisposes to atelectasis & rapid desaturation'),
('Airway Resistance', '-35%', 'Progesterone-mediated bronchodilation'),
('PaO₂', '+10%', 'Rises to ~106 mmHg at sea level; hyperventilation effect'),
('PaCO₂', '-15%', 'Drops to ~32 mmHg; HCO₃⁻ drops to ~20 mEq/L; pH ~7.44'),
('P50', '+27→30 mmHg', 'Right-shifted O₂-Hb curve; facilitates O₂ offloading to fetus'),
], HexColor('#0277BD')))
story.append(PageBreak())
# ── PAGE 4: Hematology + Coagulation ──────────────────────────────────────
story.append(banner('VTE RISK — VIRCHOW\'S TRIAD IN PREGNANCY', '🩸', HexColor('#7B1515')))
story.append(Spacer(1, 2*mm))
story.append(make_coag_diagram())
story.append(Spacer(1, 4*mm))
story.append(banner('HEMATOLOGICAL CHANGES', '🩸', HexColor('#E65100')))
story.append(Spacer(1, 2*mm))
story.append(make_table([
('Hemoglobin', '-20%', 'Hgb <11 g/dL (1st tri) or <10.5 g/dL (2nd tri) is pathological'),
('Hematocrit', 'Decreases', 'Falls from ~40% to ~34%; nadir at 30-34 weeks'),
('Platelets', '-10%', 'Mild thrombocytopenia is normal; <70,000 warrants investigation'),
('WBC', 'Increases', 'Up to 12,000/μL; in labor may reach 20,000-30,000/μL'),
('Fibrinogen', 'Doubles', 'Rises from 300 → 600 mg/dL; ESR increases markedly'),
('Clotting factors II, VII, VIII, X', '+30–250%', 'Pregnancy is strongly hypercoagulable'),
('Protein C & S', 'Decreased', 'Further tilts balance toward thrombosis'),
('Serum albumin', 'Decreased', '4.0 → 3.0 g/dL; affects protein-bound drug pharmacokinetics'),
], HexColor('#E64A19')))
story.append(PageBreak())
# ── PAGE 5: Hormones + Renal + GI ─────────────────────────────────────────
story.append(banner('HORMONAL CHANGES & THEIR EFFECTS', '⚗️', HexColor('#006064')))
story.append(Spacer(1, 2*mm))
story.append(make_hormones_diagram())
story.append(Spacer(1, 4*mm))
story.append(banner('RENAL CHANGES', '🫘', HexColor('#1B5E20')))
story.append(Spacer(1, 2*mm))
story.append(make_table([
('GFR', '+50%', 'Starts 1st trimester; renal plasma flow ↑70%; kidneys enlarge ~1 cm'),
('Serum Creatinine', 'Decreased', 'Normal = 0.5–0.8 mg/dL; "normal" 1.0 mg/dL suggests impairment'),
('Serum BUN', 'Decreased', 'Normal ~8-9 mg/dL; lower due to ↑GFR'),
('Glycosuria', 'May occur', 'Tubular reabsorption does not scale with GFR; not pathological alone'),
('Urine protein', 'Up to 300 mg/day', '>300 mg/24h = pathological; marker for preeclampsia'),
], HexColor('#2E7D32')))
story.append(Spacer(1, 4*mm))
story.append(banner('GASTROINTESTINAL CHANGES', '🤰', HexColor('#4E342E')))
story.append(Spacer(1, 2*mm))
story.append(make_table([
('Gastric emptying', 'Delayed', 'Progesterone ↓ GI motility; aspiration risk (Mendelson syndrome)'),
('LES tone', 'Decreased', 'Combined with ↑ intragastric pressure → GERD in >80%'),
('Nausea & vomiting', 'Common', 'Affects 70-80%; hyperemesis gravidarum in <2%; peak 8-10 wks'),
('Alkaline phosphatase', 'Increased', 'Placenta produces ALP; ALT/AST should remain normal'),
('Gallbladder', 'Delayed emptying', 'Cholesterol gallstones 2x more common in pregnancy'),
], HexColor('#5D4037')))
story.append(PageBreak())
# ── PAGE 6: Timeline + Clinical Pearls ────────────────────────────────────
story.append(banner('TIMELINE OF KEY PHYSIOLOGICAL CHANGES', '📅', HexColor('#1A237E')))
story.append(Spacer(1, 2*mm))
story.append(make_timeline())
story.append(Spacer(1, 5*mm))
story.append(banner('⭐ HIGH-YIELD CLINICAL PEARLS', '🏆', HexColor('#1A3020')))
story.append(Spacer(1, 2*mm))
story.append(make_pearls())
story.append(Spacer(1, 5*mm))
# Footer
story.append(HRFlowable(width='100%', thickness=0.5, color=HexColor('#37474F')))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
'Source: Morgan & Mikhail\'s Clinical Anesthesiology, 7e, Chapter 40 (pp. 1572–1587) | '
'Compiled for educational purposes | Orris Medical Summary — 6-page visual reference',
note_s
))
doc.build(story, onFirstPage=add_bg, onLaterPages=add_bg)
print(f'✅ PDF created: {OUTPUT}')
cd /home/daytona/workspace/pregnancy-physiology && python make_pdf_v2.py 2>&1
~/pregnancy-physiology/make_pdf_v2.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/pregnancy-physiology/make_pdf_v2.py'
cd /home/daytona/workspace/pregnancy-physiology && python make_pdf_v2.py 2>&1
~/pregnancy-physiology/make_pdf_v2.py
Error: String not found in file: ' for i, (ico, title, body, clr) in enumerate(pearls[i+j:i+j+1]):
cell_content = Paragraph(
f'<b><font color="#{clr.hexval()[1:]}">{ico} {title}</font></b><br/>'
f'<font color="#B0BEC5">{body}</font>',
S('pc', fontName='Helvetica', fontSize=8, textColor=LIGHT_TEXT,
leading=12, alignment=TA_LEFT)
)
row.append(cell_content)'"hexval" in ~/pregnancy-physiology
python3 -c "from reportlab.lib.colors import HexColor; c=HexColor('#EF5350'); print(repr(c.hexval())); print('%02X%02X%02X' % (int(c.red*255), int(c.green*255), int(c.blue*255)))"
~/pregnancy-physiology/make_pdf_v2.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/pregnancy-physiology/make_pdf_v2.py'
~/pregnancy-physiology/make_pdf_v2.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/pregnancy-physiology/make_pdf_v2.py'
~/pregnancy-physiology/make_pdf_v2.py
Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/pregnancy-physiology/make_pdf_v2.py'
cd /home/daytona/workspace/pregnancy-physiology && python make_pdf_v2.py 2>&1
ls -lh /home/daytona/workspace/pregnancy-physiology/Physiological_Changes_Pregnancy_v2.pdf
Physiological Changes Pregnancy v2
PDF Document · PDF