Migraine is now understood as a neurovascular disorder involving abnormal brain excitability, activation of pain pathways, and inflammation around cranial blood vessels. Older theories focused mainly on vasodilation, but standard texts like Harrison's Principles of Internal Medicine explain that migraine is primarily a disorder of the nervous system with secondary vascular changes. Simplified Pathophysiology of Migraine 1. Triggering Phase — “Hyperexcitable Brain” People with migraine have a genetically sensitive or hyperexcitable brain. Common triggers: Stress Lack of sleep Hormonal changes Certain foods Bright light Fasting These triggers activate specific brain regions, especially: Hypothalamus Brainstem nuclei Cortex This explains early symptoms before headache: Yawning Food cravings Mood change Fatigue 2. Cortical Spreading Depression (Especially in Migraine With Aura) A wave of neuronal depolarization spreads slowly across the cerebral cortex. This is called: Cortical Spreading Depression (CSD) Sequence: Sudden neuronal activation Followed by suppression of brain activity Changes in blood flow occur secondarily Rate: About 2–6 mm/min across cortex Effects: Causes aura symptoms: Flashing lights Zig-zag lines Numbness Speech difficulty CSD also activates trigeminal pain pathways. 3. Activation of the Trigeminovascular System (Most Important Step) This is central to migraine pain. The trigeminal nerve supplies: Meninges Large cerebral vessels When activated, trigeminal nerve endings release neuropeptides: CGRP (Calcitonin Gene-Related Peptide) Substance P Neurokinin A These cause: Vasodilation Plasma protein leakage Sterile neurogenic inflammation Result: Throbbing headache pain CGRP is especially important because: Its level rises during attacks CGRP blockers improve migraine 4. Central Sensitization Repeated pain signaling sensitizes neurons in: Trigeminal nucleus caudalis Thalamus This makes the brain overly responsive. Consequences: Normal touch becomes painful Scalp tenderness Pain worsens with movement Sensitivity to light and sound This explains: Allodynia Photophobia Phonophobia 5. Brainstem Dysfunction Brainstem nuclei involved: Dorsal raphe nucleus (serotonin) Locus coeruleus (noradrenaline) Altered neurotransmitters: Serotonin ↓ during attack Dopamine involvement This contributes to: Nausea Vomiting Autonomic symptoms Important Neurotransmitters in Migraine Neurotransmitter Role CGRP Major mediator of pain and vasodilation Serotonin (5-HT) Modulates trigeminal pathways Dopamine Nausea, yawning, prodrome Glutamate Cortical excitability and CSD Why Triptans Work Triptans are: 5-HT1B/1D agonists They: Inhibit CGRP release Reduce trigeminal transmission Cause cranial vasoconstriction Thus they abort migraine attacks. One-Line Flowchart Trigger ↓ Brain hyperexcitability ↓ Cortical spreading depression (± aura) ↓ Trigeminovascular activation ↓ CGRP release + neurogenic inflammation ↓ Pain transmission to brainstem/thalamus ↓ Migraine headache + sensory symptoms High-Yield Harrison-Based Concepts Migraine is primarily a neuronal disorder, not just vascular. Trigeminovascular activation is the key mechanism of pain. CGRP is central in modern migraine theory. Cortical spreading depression explains aura. Central sensitization causes photophobia and allodynia. Standard References Harrison's Principles of Internal Medicine — Chapter on Headache and Migraine Adams and Victor's Principles of Neurology Robbins and Cotran Pathologic Basis of Disease Davidson's Principles and Practice of
migraine pathophysiology cortical spreading depression trigeminovascular
migraine CGRP pathophysiology trigeminovascular
"It seems probable that there is not a single 'migraine generator' shared by all people with migraine and that there is not a single 'migraine generator' within individuals from attack to attack."
"It is debated whether or not CSD can lead to activation of the trigeminocervical system and thus trigger the headache phase." - Bradley & Daroff
"Activation of these fibers releases substance P, CGRP, and other peptides into the vessel wall, which serves to sensitize the trigeminal system to the pulsatility of cranial vessels, and to increase their permeability, thereby promoting an inflammatory response."
"Recurrent and/or prolonged activation of the trigeminocervical system can lead to peripheral and central sensitization. Sensitized neurons have lower thresholds for activation, increased spontaneous activity, and receptive field expansion. Approximately three-fourths of people with migraine have evidence of cutaneous allodynia during a migraine attack."
"Plasma and platelet concentrations of 5HT vary with different phases of the migraine attack. Urinary concentrations of 5HT and its metabolites are elevated during most migraine attacks. Migraine may be precipitated by agents (e.g., reserpine and fenfluramine) that release 5HT from intracellular storage sites."
| Neurotransmitter/Mediator | Role | Notes |
|---|---|---|
| CGRP | Primary mediator of pain and vasodilation | Target of gepants and anti-CGRP antibodies |
| Serotonin (5-HT) | Modulates trigeminal pathways; fluctuates across attack phases | Levels vary by phase, not simply "decrease" |
| Dopamine | Nausea, yawning, prodrome | Dopamine receptor sensitivity may be increased in migraineurs |
| Glutamate | Cortical excitability and CSD propagation | NMDA receptor involvement in central sensitization |
| Substance P | Released with CGRP; neurogenic inflammation | Less targeted therapeutically than CGRP |
| Nitric oxide (NO) | Vasodilation; triggers migraine; released from trigeminovascular neurons | Missing from your summary - nitroglycerin (a NO donor) is used to experimentally trigger migraine attacks |
| Neurokinin A | Released with CGRP/SP from trigeminal endings | Your summary mentions it - correct |
"One hypothesis... activation of 5HT1B/1D receptors may cause constriction of intracranial blood vessels... An alternative hypothesis proposes that both 5HT1B and 5HT1D receptors serve as presynaptic autoreceptors that block the release of neurotransmitter or proinflammatory neuropeptides at nerve terminals in the perivascular space."
| Missing Concept | Source | Importance |
|---|---|---|
| Periaqueductal gray (PAG) activation | Bradley & Daroff, Adams & Victor | Key imaging finding in premonitory phase; descending pain modulation |
| Nitric oxide (NO) as a mediator | Guyton & Hall | Nitroglycerin triggers migraine experimentally |
| CSD's causal link to headache is debated | Bradley & Daroff | Intellectual honesty; CSD clearly explains aura, pain link less certain |
| 75% of migraineurs develop cutaneous allodynia | Bradley & Daroff | High-yield stat |
| Early triptan use required before central sensitization | Textbook principle | Major clinical implication of the sensitization model |
| Triptan vasoconstriction mechanism (5-HT1B) | Goodman & Gilman | Explains contraindication in CVD |
| PACAP as emerging mediator/target | Recent reviews | Beyond CGRP; emerging therapeutics |
| Oligemia does not respect arterial territories | Adams & Victor, Bradley & Daroff | Directly refutes the vasospasm theory |
| Osmophobia - highly specific for migraine | Bradley & Daroff | Diagnostic specificity |
| Anti-CGRP monoclonal antibodies (gepants, erenumab, etc.) | Current knowledge | Major therapeutic advancement |
| Postdrome phase | Bradley & Daroff | Migraine has 4 phases: prodrome, aura, headache, postdrome |
| Your Claim | Verdict | Notes |
|---|---|---|
| Migraine is neuronal, not purely vascular | ✅ Confirmed | Well-supported across all texts |
| CSD rate "2-6 mm/min" | ⚠️ Slightly high | Textbooks say 2-4 mm/min |
| Hypothalamus + brainstem as trigger regions | ✅ Confirmed | PAG should be added |
| Trigeminal nerve releases CGRP, SP, Neurokinin A | ✅ Confirmed | Add NO |
| Sterile neurogenic inflammation | ✅ Confirmed | Classic Moskowitz model |
| Central sensitization - trigeminal nucleus caudalis + thalamus | ✅ Confirmed | Add: 75% allodynia rate |
| Serotonin ↓ during attack | ⚠️ Oversimplified | 5-HT levels vary by phase |
| Triptans = 5-HT1B/1D agonists | ✅ Confirmed | Add vasoconstriction mechanism and CVD contraindication |
| CSD activates trigeminal pain pathways | ⚠️ Debated | Textbooks explicitly call this debated |
Generate a PDF quick-reference card on migraine pathophysiology
ls -la /home/daytona/workspace/ 2>/dev/null && mkdir -p /home/daytona/workspace/migraine-ref-card
~/migraine-ref-card/generate_card.py
#!/usr/bin/env python3
"""
Migraine Pathophysiology Quick-Reference Card
Single A4 landscape page, generated with ReportLab
"""
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.platypus import (
SimpleDocTemplate, Table, TableStyle, Paragraph,
Spacer, HRFlowable, KeepInFrame
)
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus.flowables import BalancedColumns
from reportlab.lib.colors import HexColor, white, black
import os
OUT = "/home/daytona/workspace/migraine-ref-card/Migraine_Pathophysiology_QuickRef.pdf"
# ── Palette ──────────────────────────────────────────────────────────────────
C_HEADER = HexColor("#1A3A5C") # deep navy
C_ACCENT1 = HexColor("#C0392B") # arterial red (pain / CGRP)
C_ACCENT2 = HexColor("#2980B9") # blue (neural)
C_ACCENT3 = HexColor("#27AE60") # green (triptans / tx)
C_ACCENT4 = HexColor("#8E44AD") # purple (sensitization)
C_ACCENT5 = HexColor("#E67E22") # orange (CSD)
C_LIGHT = HexColor("#EBF5FB") # very light blue tint
C_LIGHT2 = HexColor("#FDFEFE")
C_LTGRAY = HexColor("#F2F3F4")
C_ROWALT = HexColor("#EAF4FB")
PAGE = landscape(A4)
W, H = PAGE # 297 x 210 mm
doc = SimpleDocTemplate(
OUT,
pagesize=PAGE,
leftMargin=10*mm,
rightMargin=10*mm,
topMargin=8*mm,
bottomMargin=6*mm,
title="Migraine Pathophysiology Quick-Reference Card",
author="Orris Medical",
)
# ── Styles ───────────────────────────────────────────────────────────────────
def S(name, **kw):
return ParagraphStyle(name, **kw)
BASE = S("base", fontName="Helvetica", fontSize=7, leading=9.5, textColor=black)
BOLD = S("bold", fontName="Helvetica-Bold", fontSize=7, leading=9.5, textColor=black)
TITLE = S("title", fontName="Helvetica-Bold", fontSize=15, leading=18, textColor=white, alignment=TA_CENTER)
SUBTITLE = S("subtitle", fontName="Helvetica", fontSize=8, leading=10, textColor=HexColor("#AED6F1"), alignment=TA_CENTER)
SHEAD = S("shead", fontName="Helvetica-Bold", fontSize=8, leading=10, textColor=white, alignment=TA_CENTER)
SHEAD_L = S("shead_l", fontName="Helvetica-Bold", fontSize=8, leading=10, textColor=white, alignment=TA_LEFT)
ITEM = S("item", fontName="Helvetica", fontSize=6.8, leading=9, textColor=HexColor("#1B2631"))
ITEM_BOLD = S("itembold", fontName="Helvetica-Bold", fontSize=6.8, leading=9, textColor=HexColor("#1B2631"))
LABEL = S("label", fontName="Helvetica-Bold", fontSize=6.5, leading=8.5, textColor=C_HEADER)
ARROW = S("arrow", fontName="Helvetica-Bold", fontSize=9, leading=11, textColor=C_ACCENT1, alignment=TA_CENTER)
NOTE = S("note", fontName="Helvetica-Oblique",fontSize=6.2,leading=8, textColor=HexColor("#555555"))
WARN = S("warn", fontName="Helvetica-Bold", fontSize=6.5, leading=8.5, textColor=HexColor("#922B21"))
def p(text, style=BASE): return Paragraph(text, style)
def pb(text): return Paragraph(text, BOLD)
def pi(text): return Paragraph(text, ITEM)
def bullet(text, color="#1A3A5C"):
return Paragraph(f'<font color="{color}">►</font> {text}', ITEM)
def dot(text, color="#1A3A5C"):
return Paragraph(f'<font color="{color}">•</font> {text}', ITEM)
# ── Helper: section header cell ──────────────────────────────────────────────
def hdr(text, bg=C_HEADER):
return Table(
[[p(text, SHEAD)]],
colWidths=["100%"],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
("ROUNDEDCORNERS", [3,3,3,3]),
])
)
# ══════════════════════════════════════════════════════════════════════════════
# SECTION BUILDERS
# ══════════════════════════════════════════════════════════════════════════════
def make_title_banner():
"""Full-width title bar"""
data = [[
p("MIGRAINE PATHOPHYSIOLOGY", TITLE),
p("Quick-Reference Card | Neurovascular Disorder of Brain Excitability | Sources: Bradley & Daroff • Adams & Victor • Goodman & Gilman • Guyton & Hall", SUBTITLE),
]]
t = Table(data, colWidths=[110*mm, 170*mm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), C_HEADER),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
]))
return t
def make_flowchart():
"""Vertical cascade flowchart in one narrow column"""
steps = [
(C_HEADER, "TRIGGER", "Stress • Sleep deprivation\nHormonal change • Bright light\nCertain foods • Fasting"),
(C_ACCENT2, "BRAIN HYPEREXCITABILITY","Hypothalamus activation\nBrainstem nuclei (PAG, dorsal pons)\nProdrome: yawning, cravings, fatigue"),
(C_ACCENT5, "CORTICAL SPREADING\nDEPRESSION (CSD)", "Wave of depolarization 2–4 mm/min\nOccipital → anterior cortex\nAura: zig-zag lines, numbness, speech Δ"),
(C_ACCENT1, "TRIGEMINOVASCULAR\nACTIVATION", "Trigeminal endings on meninges/vessels\nRelease: CGRP • SP • NKA • NO\nNeurogenic sterile inflammation"),
(C_ACCENT4, "CENTRAL\nSENSITIZATION", "Trigeminal nucleus caudalis → Thalamus\nAllodynia (≈75% of attacks)\nPhotophobia • Phonophobia"),
(C_ACCENT3, "MIGRAINE HEADACHE", "Throbbing unilateral pain 4–72 h\nNausea / vomiting • Osmophobia\n± Postdrome fatigue/cognitive fog"),
]
rows = []
for bg, title, detail in steps:
title_cell = p(f'<b><font color="white">{title}</font></b>', S("fc_t", fontName="Helvetica-Bold", fontSize=6.8, leading=8.5, alignment=TA_CENTER))
detail_cell = p(detail, S("fc_d", fontName="Helvetica", fontSize=6.2, leading=8, alignment=TA_CENTER, textColor=HexColor("#1B2631")))
block = Table([
[title_cell],
[detail_cell],
], colWidths=[52*mm],
style=TableStyle([
("BACKGROUND", (0,0), (0,0), bg),
("BACKGROUND", (0,1), (0,1), C_LTGRAY),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 3),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("BOX", (0,0), (-1,-1), 0.5, bg),
]))
rows.append([block])
if (bg, title, detail) != steps[-1]:
rows.append([p("▼", S("arr", fontName="Helvetica-Bold", fontSize=10, leading=11, textColor=C_ACCENT1, alignment=TA_CENTER))])
return Table(rows, colWidths=[54*mm],
style=TableStyle([
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
]))
def make_phases():
"""4 phases of migraine attack"""
phases = [
(C_ACCENT2, "1. PRODROME\n(hours-days before)", "Hypothalamic activation\nYawning, food cravings\nMood & sleep changes\nPolyuria, osmophobia"),
(C_ACCENT5, "2. AURA\n(20–60 min; ~30% of pts)", "CSD in occipital cortex\nScintillating scotoma\nZig-zag (fortification) lines\nNumbness, dysphasia"),
(C_ACCENT1, "3. HEADACHE\n(4–72 hours)", "Trigeminovascular activation\nThrobbing unilateral pain\nN/V, photo/phono/osmophobia\nAllodynia (≈75%)"),
(C_ACCENT4, "4. POSTDROME\n(hours-days after)", "Fatigue, cognitive fog\n'Migraine hangover'\nNeck stiffness\nPoor concentration"),
]
rows = [[
Table([[p(f'<b><font color="white">{t}</font></b>', S("ph_t", fontName="Helvetica-Bold", fontSize=6.5, leading=8, alignment=TA_CENTER)),
p(d, S("ph_d", fontName="Helvetica", fontSize=6, leading=7.8, alignment=TA_LEFT, textColor=HexColor("#1B2631")))
]], colWidths=[60*mm],
style=TableStyle([
("BACKGROUND", (0,0), (0,0), bg),
("BACKGROUND", (0,1), (0,1), C_LTGRAY),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 3),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("BOX", (0,0), (-1,-1), 0.5, bg),
]))
for bg, t, d in phases
]]
return Table(rows, colWidths=[62*mm]*4,
style=TableStyle([
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 1),
("RIGHTPADDING", (0,0), (-1,-1), 1),
]))
def make_neurotransmitters():
"""Neurotransmitter table"""
header = [
p("<b>Mediator</b>", SHEAD_L),
p("<b>Role in Migraine</b>", SHEAD_L),
p("<b>Clinical Note</b>", SHEAD_L),
]
rows = [
["CGRP", "Primary vasodilator & pain mediator; released from trigeminal endings", "Target: gepants (rimegepant, ubrogepant)\nAnti-CGRP mAbs: erenumab, fremanezumab"],
["Serotonin\n(5-HT)", "Modulates trigeminal pain pathways;\nfluctuates across attack phases", "5-HT1B/1D = triptan target\nReserpine (releases 5-HT) triggers migraine"],
["CGRP", "", ""], # placeholder – replaced below
["Substance P", "Co-released with CGRP; plasma protein leakage\n& neurogenic inflammation", "Contributes to vessel wall sensitization"],
["Nitric Oxide\n(NO)", "Vasodilator from trigeminovascular neurons;\nnitroglycerin → experimental migraine", "NO synthase inhibitors under investigation"],
["Dopamine", "Prodrome: yawning, nausea\nHypersensitivity of D2 receptors", "Prochlorperazine (D2 blocker) used acutely"],
["Glutamate", "Drives cortical hyperexcitability\nand CSD propagation", "NMDA receptor involvement in\ncentral sensitization"],
["Neurokinin A\n(NKA)", "Co-released with CGRP/SP from trigeminal\nC-fibers", "Less studied therapeutically than CGRP"],
["PACAP", "Emerging mediator; triggers migraine;\nneurovascular modulator", "Anti-PACAP mAbs in clinical trials (2024–)"],
]
# Remove placeholder row
rows = [r for r in rows if r[0] != "CGRP" or r[1] != ""]
table_data = [header]
for i, r in enumerate(rows):
table_data.append([
p(f"<b>{r[0]}</b>", ITEM_BOLD),
p(r[1], ITEM),
p(r[2], ITEM),
])
col_w = [28*mm, 60*mm, 55*mm]
ts = TableStyle([
("BACKGROUND", (0,0), (-1,0), C_ACCENT2),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C_LIGHT2, C_ROWALT]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#BDC3C7")),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 3),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("VALIGN", (0,0), (-1,-1), "TOP"),
])
return Table(table_data, colWidths=col_w, style=ts)
def make_triptans():
"""Triptan mechanism box"""
data = [
[p("<b>TRIPTANS — 5-HT<sub rise=2>1B/1D</sub> Agonists</b>", S("th", fontName="Helvetica-Bold", fontSize=7.2, leading=9, textColor=white))],
[p("Mechanism 1 (5-HT<sub rise=2>1B</sub>): Cranial vasoconstriction\n"
"Mechanism 2 (5-HT<sub rise=2>1D</sub>): Presynaptic inhibition of CGRP/SP release\n"
"→ Both reduce neurogenic inflammation & pain transmission\n\n"
"<b>Key point:</b> Most effective when taken <b>early</b> — before central\n"
"sensitization establishes (i.e., before allodynia begins)\n\n"
"<b>Contraindicated:</b> Ischaemic heart disease, uncontrolled HTN,\n"
"prior stroke (vasoconstriction risk)",
ITEM)],
]
return Table(data, colWidths=[56*mm],
style=TableStyle([
("BACKGROUND", (0,0), (0,0), C_ACCENT3),
("BACKGROUND", (0,1), (0,1), HexColor("#EAFAF1")),
("BOX", (0,0), (-1,-1), 0.8, C_ACCENT3),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
]))
def make_csd_box():
"""CSD detail box"""
data = [
[p("<b>CORTICAL SPREADING DEPRESSION</b>", S("ch", fontName="Helvetica-Bold", fontSize=7.2, leading=9, textColor=white))],
[p("• Wave of neuronal depolarization then suppression\n"
"• Origin: usually occipital lobe → spreads anteriorly\n"
"• Rate: 2–4 mm/min (does NOT respect vascular territories)\n"
"• Preceded by brief focal hyperaemia, followed by oligaemia\n"
"• Electrophysiological basis of aura (confirmed)\n"
"• Whether CSD directly triggers headache is <b>debated</b>\n"
"• CSD may activate the trigeminocervical complex via\n"
" inflammatory mediator diffusion to the meninges",
ITEM)],
]
return Table(data, colWidths=[56*mm],
style=TableStyle([
("BACKGROUND", (0,0), (0,0), C_ACCENT5),
("BACKGROUND", (0,1), (0,1), HexColor("#FEF9E7")),
("BOX", (0,0), (-1,-1), 0.8, C_ACCENT5),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
]))
def make_sensitization_box():
"""Central sensitization box"""
data = [
[p("<b>CENTRAL SENSITIZATION</b>", S("sh", fontName="Helvetica-Bold", fontSize=7.2, leading=9, textColor=white))],
[p("Peripheral sensitization: trigeminal C-fibers → respond to\n"
"normally innocuous stimuli (vessel pulsation → throbbing pain)\n\n"
"1st-order neuron sensitization → activates\n"
"2nd-order neurons in trigeminal nucleus caudalis\n"
"→ Cutaneous allodynia (scalp tenderness, ≈75% of attacks)\n\n"
"3rd-order neuron sensitization → thalamus/cortex\n"
"→ Whole-body allodynia, photophobia, phonophobia\n\n"
"<b>Clinical implication:</b> Once allodynia is present,\n"
"triptans are less effective — treat EARLY",
ITEM)],
]
return Table(data, colWidths=[56*mm],
style=TableStyle([
("BACKGROUND", (0,0), (0,0), C_ACCENT4),
("BACKGROUND", (0,1), (0,1), HexColor("#F5EEF8")),
("BOX", (0,0), (-1,-1), 0.8, C_ACCENT4),
("TOPPADDING", (0,0), (-1,-1), 3),
("BOTTOMPADDING", (0,0), (-1,-1), 3),
("LEFTPADDING", (0,0), (-1,-1), 4),
("RIGHTPADDING", (0,0), (-1,-1), 4),
]))
def make_key_facts():
"""High-yield facts"""
facts = [
("<b>Vascular vs Neural:</b>", "Migraine is primarily a <b>neuronal</b> disorder; vascular changes are secondary. MRA studies show no change in cerebral artery diameter during attacks."),
("<b>Genetics:</b>", "1st-degree relatives: ×2 risk (MO without aura); ×4 risk (MA with aura). FHM linked to CACNA1A, ATP1A2, SCN1A mutations."),
("<b>Prevalence:</b>", "12% general population (18% women, 6% men). Peak 4th decade. WHO: among most disabling conditions worldwide."),
("<b>Chronic migraine:</b>", "≥15 headache days/month including ≥8 migraine days. Affects ~2% of population. Average 22 headache days/month."),
("<b>CGRP therapies:</b>", "Acute: gepants (rimegepant, ubrogepant, atogepant). Preventive: anti-CGRP mAbs (erenumab, fremanezumab, galcanezumab, eptinezumab)."),
("<b>PAG role:</b>", "Periaqueductal gray activated in premonitory phase (PET). Descending pain modulator. Iron deposition in PAG found in chronic migraine."),
("<b>NO triggers:</b>", "Nitroglycerin (NO donor) reliably triggers migraine — used in research models. NO released from trigeminovascular neurons."),
("<b>Osmophobia:</b>", "Smell hypersensitivity; not in formal ICHD criteria but highly specific for migraine diagnosis when present."),
]
rows = [[p(k, ITEM_BOLD), p(v, ITEM)] for k, v in facts]
header = [[p("<b>High-Yield Clinical Facts</b>", SHEAD_L), p("", SHEAD_L)]]
t = Table(header + rows,
colWidths=[32*mm, 111*mm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), C_HEADER),
("ROWBACKGROUNDS",(0,1), (-1,-1), [C_LIGHT2, C_ROWALT]),
("GRID", (0,0), (-1,-1), 0.3, HexColor("#BDC3C7")),
("TOPPADDING", (0,0), (-1,-1), 2),
("BOTTOMPADDING", (0,0), (-1,-1), 2),
("LEFTPADDING", (0,0), (-1,-1), 3),
("RIGHTPADDING", (0,0), (-1,-1), 3),
("VALIGN", (0,0), (-1,-1), "TOP"),
("SPAN", (0,0), (1,0)),
]))
return t
def make_footer():
foot_text = (
"Sources: Bradley & Daroff's Neurology in Clinical Practice • Adams & Victor's Principles of Neurology 12e • "
"Goodman & Gilman's Pharmacological Basis of Therapeutics • Guyton & Hall Medical Physiology • "
"ICHD-3 (IHS) • PubMed PMIDs: 38887982, 41087022 | "
"CSD = Cortical Spreading Depression • CGRP = Calcitonin Gene-Related Peptide • "
"SP = Substance P • NKA = Neurokinin A • NO = Nitric Oxide • "
"PAG = Periaqueductal Gray • mAb = Monoclonal Antibody • FHM = Familial Hemiplegic Migraine"
)
return p(foot_text, S("foot", fontName="Helvetica", fontSize=5.5, leading=7, textColor=HexColor("#7F8C8D"), alignment=TA_CENTER))
# ══════════════════════════════════════════════════════════════════════════════
# LAYOUT ASSEMBLY
# ══════════════════════════════════════════════════════════════════════════════
def build():
story = []
# ── Title banner ──────────────────────────────────────────────────────────
story.append(make_title_banner())
story.append(Spacer(1, 2*mm))
# ── Row 1: Flowchart | Phases | Neurotransmitters ─────────────────────────
# Section headers
fc_hdr = hdr("PATHOPHYSIOLOGY PATHWAY", C_HEADER)
ph_hdr = hdr("4 PHASES OF MIGRAINE ATTACK", C_ACCENT2)
nt_hdr = hdr("KEY MEDIATORS & NEUROTRANSMITTERS", C_ACCENT2)
fc_content = make_flowchart()
ph_content = make_phases()
nt_content = make_neurotransmitters()
# Phase + NT stacked in right portion
right_block = Table([
[ph_hdr],
[ph_content],
[Spacer(1, 1.5*mm)],
[nt_hdr],
[nt_content],
], colWidths=[250*mm],
style=TableStyle([
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
]))
row1 = Table([
[
Table([[fc_hdr],[fc_content]], colWidths=[54*mm],
style=TableStyle([("TOPPADDING",(0,0),(-1,-1),0),("BOTTOMPADDING",(0,0),(-1,-1),0),
("LEFTPADDING",(0,0),(-1,-1),0),("RIGHTPADDING",(0,0),(-1,-1),0)])),
right_block,
]
], colWidths=[55*mm, 251*mm],
style=TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 1.5*mm),
("LINEAFTER", (0,0), (0,-1), 0.5, HexColor("#BDC3C7")),
]))
story.append(row1)
story.append(Spacer(1, 1.5*mm))
# ── Row 2: CSD box | Sensitization box | Triptans box | High-Yield Facts ─
csd_block = Table([
[make_csd_box()],
], colWidths=[57*mm],
style=TableStyle([("TOPPADDING",(0,0),(-1,-1),0),("BOTTOMPADDING",(0,0),(-1,-1),0),
("LEFTPADDING",(0,0),(-1,-1),0),("RIGHTPADDING",(0,0),(-1,-1),0)]))
sens_block = Table([
[make_sensitization_box()],
], colWidths=[57*mm],
style=TableStyle([("TOPPADDING",(0,0),(-1,-1),0),("BOTTOMPADDING",(0,0),(-1,-1),0),
("LEFTPADDING",(0,0),(-1,-1),0),("RIGHTPADDING",(0,0),(-1,-1),0)]))
trip_block = Table([
[make_triptans()],
], colWidths=[57*mm],
style=TableStyle([("TOPPADDING",(0,0),(-1,-1),0),("BOTTOMPADDING",(0,0),(-1,-1),0),
("LEFTPADDING",(0,0),(-1,-1),0),("RIGHTPADDING",(0,0),(-1,-1),0)]))
facts_block = make_key_facts()
row2 = Table([
[csd_block, sens_block, trip_block, facts_block]
], colWidths=[58*mm, 58*mm, 58*mm, 147*mm],
style=TableStyle([
("VALIGN", (0,0), (-1,-1), "TOP"),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 1.5*mm),
]))
story.append(row2)
story.append(Spacer(1, 1.5*mm))
# ── Footer ────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=HexColor("#BDC3C7")))
story.append(Spacer(1, 0.8*mm))
story.append(make_footer())
doc.build(story)
print(f"PDF written: {OUT}")
if __name__ == "__main__":
build()
cd /home/daytona/workspace/migraine-ref-card && python3 generate_card.py 2>&1
ls -lh /home/daytona/workspace/migraine-ref-card/
Migraine Pathophysiology QuickRef
PDF Document · PDF
Make it in simplified way for me to understand as I am UG student
~/migraine-ref-card/generate_simple_card.py
#!/usr/bin/env python3
"""
Migraine Pathophysiology - Simple UG Student Quick-Reference Card
A4 landscape, plain language, big fonts, friendly colors
"""
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.units import mm
from reportlab.lib.colors import HexColor, white, black
from reportlab.platypus import (
SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, HRFlowable
)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
OUT = "/home/daytona/workspace/migraine-ref-card/Migraine_SimpleGuide_UG.pdf"
PAGE = landscape(A4)
W, H = PAGE
# ── Friendly palette ──────────────────────────────────────────────────────────
NAVY = HexColor("#1A3A5C")
RED = HexColor("#E74C3C")
BLUE = HexColor("#2980B9")
GREEN = HexColor("#27AE60")
PURPLE = HexColor("#8E44AD")
ORANGE = HexColor("#E67E22")
TEAL = HexColor("#16A085")
YELLOW = HexColor("#F39C12")
LGRAY = HexColor("#F4F6F7")
ALTROW = HexColor("#EAF4FB")
LGREEN = HexColor("#EAFAF1")
LORANGE = HexColor("#FEF9E7")
LPURPLE = HexColor("#F5EEF8")
LRED = HexColor("#FDEDEC")
LBLUE = HexColor("#EBF5FB")
doc = SimpleDocTemplate(
OUT, pagesize=PAGE,
leftMargin=9*mm, rightMargin=9*mm,
topMargin=7*mm, bottomMargin=6*mm,
title="Migraine Pathophysiology – UG Student Guide",
)
# ── Style helpers ─────────────────────────────────────────────────────────────
def S(name, **kw):
return ParagraphStyle(name, **kw)
def p(text, style):
return Paragraph(text, style)
TITLE = S("T", fontName="Helvetica-Bold", fontSize=16, leading=20, textColor=white, alignment=TA_CENTER)
SUBT = S("ST", fontName="Helvetica", fontSize=8, leading=10, textColor=HexColor("#AED6F1"), alignment=TA_CENTER)
SHEAD = S("SH", fontName="Helvetica-Bold", fontSize=8.5,leading=11, textColor=white, alignment=TA_CENTER)
SHEAD_L = S("SHL",fontName="Helvetica-Bold", fontSize=8.5,leading=11, textColor=white, alignment=TA_LEFT)
BODY = S("B", fontName="Helvetica", fontSize=7.5,leading=10, textColor=HexColor("#1B2631"))
BODYB = S("BB", fontName="Helvetica-Bold", fontSize=7.5,leading=10, textColor=HexColor("#1B2631"))
BIG = S("BG", fontName="Helvetica-Bold", fontSize=9, leading=12, textColor=HexColor("#1B2631"), alignment=TA_CENTER)
NOTE = S("N", fontName="Helvetica-Oblique", fontSize=6.5,leading=8.5,textColor=HexColor("#7F8C8D"))
FOOT = S("F", fontName="Helvetica", fontSize=5.8,leading=7.5,textColor=HexColor("#95A5A6"), alignment=TA_CENTER)
EMOJI_S = S("ES", fontName="Helvetica-Bold", fontSize=8, leading=10, textColor=HexColor("#1B2631"), alignment=TA_CENTER)
def cell_block(title, title_bg, body_text, body_bg, width):
"""A box with a colored header and light body."""
return Table([
[p(title, SHEAD)],
[p(body_text, BODY)],
], colWidths=[width],
style=TableStyle([
("BACKGROUND", (0,0),(0,0), title_bg),
("BACKGROUND", (0,1),(0,1), body_bg),
("BOX", (0,0),(-1,-1), 0.8, title_bg),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LEFTPADDING", (0,0),(-1,-1), 4),
("RIGHTPADDING", (0,0),(-1,-1), 4),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
# ══════════════════════════════════════════════════════════════════════════════
# BUILD
# ══════════════════════════════════════════════════════════════════════════════
story = []
# ── TITLE BAR ─────────────────────────────────────────────────────────────────
title_bar = Table([[
p("MIGRAINE — HOW IT HAPPENS", TITLE),
p("A Plain-Language Guide for Undergraduate Medical Students\n"
"Based on: Bradley & Daroff • Adams & Victor • Goodman & Gilman • Guyton & Hall", SUBT),
]], colWidths=[105*mm, 172*mm],
style=TableStyle([
("BACKGROUND", (0,0),(-1,-1), NAVY),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
]))
story.append(title_bar)
story.append(Spacer(1, 2.5*mm))
# ══════════════════════════════════════════════════════════════════════════════
# ROW 1 — Step-by-step story (left) + 4 Phases (right)
# ══════════════════════════════════════════════════════════════════════════════
# ── STEP-BY-STEP STORY ──────────────────────────────────────────────────────
steps = [
(NAVY, "STEP 1 — WHAT IS MIGRAINE?",
"Migraine is a <b>brain disease</b>, not just a headache.\n"
"The brain is over-sensitive (hyperexcitable).\n"
"Blood vessel changes are <b>secondary</b> — the brain fires first."),
(BLUE, "STEP 2 — THE TRIGGER",
"Stress, bad sleep, skipping meals, bright lights,\n"
"hormonal changes or certain foods → brain gets\n"
"overloaded → attack begins in the <b>hypothalamus\n"
"& brainstem</b> (hours before headache!)"),
(ORANGE, "STEP 3 — AURA (in ~30% of people)",
"A wave of electricity called <b>Cortical Spreading\n"
"Depression (CSD)</b> crawls across the back of the brain\n"
"at ~3 mm/min. This causes:\n"
" ★ Zig-zag flashing lines in vision\n"
" ★ Numbness on one side\n"
" ★ Difficulty speaking"),
(RED, "STEP 4 — PAIN STARTS",
"CSD irritates the <b>trigeminal nerve</b> (the pain nerve\n"
"of the face & head). It releases chemicals:\n"
" → CGRP → Substance P → Nitric Oxide\n"
"These cause: blood vessel swelling + inflammation\n"
"around the brain = <b>THROBBING HEADACHE</b>"),
(PURPLE, "STEP 5 — BRAIN GETS SUPER-SENSITIVE",
"Repeated pain signals make brain cells overly\n"
"reactive (<b>central sensitization</b>).\n"
"Now even a gentle touch hurts (allodynia).\n"
"Bright light, sound, smells all become unbearable.\n"
"This is why you hide in a dark, quiet room."),
(GREEN, "STEP 6 — ATTACK ENDS / RECOVERY",
"Pain fades. But a <b>postdrome (hangover) phase</b>\n"
"follows: fatigue, brain fog, neck stiffness.\n"
"Can last hours to a day.\n"
"The brain slowly resets until the next trigger."),
]
step_rows = []
for i, (bg, title, body) in enumerate(steps):
lbg = [LBLUE, LORANGE, LORANGE, LRED, LPURPLE, LGREEN][i]
block = Table([
[p(title, SHEAD)],
[p(body, BODY)],
], colWidths=[55*mm],
style=TableStyle([
("BACKGROUND", (0,0),(0,0), bg),
("BACKGROUND", (0,1),(0,1), lbg),
("BOX", (0,0),(-1,-1), 0.8, bg),
("TOPPADDING", (0,0),(-1,-1), 2),
("BOTTOMPADDING", (0,0),(-1,-1), 2),
("LEFTPADDING", (0,0),(-1,-1), 4),
("RIGHTPADDING", (0,0),(-1,-1), 4),
]))
step_rows.append([block])
if i < len(steps)-1:
step_rows.append([p(" ▼", S("aw", fontName="Helvetica-Bold", fontSize=10, leading=11, textColor=RED))])
steps_col = Table(step_rows, colWidths=[56*mm],
style=TableStyle([
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING", (0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(-1,-1), 0),
]))
# ── 4 PHASES ─────────────────────────────────────────────────────────────────
phases_hdr = Table([[p("THE 4 PHASES OF A MIGRAINE ATTACK", SHEAD)]],
colWidths=[100*mm],
style=TableStyle([
("BACKGROUND",(0,0),(-1,-1), BLUE),
("TOPPADDING",(0,0),(-1,-1), 3),
("BOTTOMPADDING",(0,0),(-1,-1), 3),
("LEFTPADDING",(0,0),(-1,-1), 4),
]))
phase_data = [
(BLUE, "1️⃣ PRODROME\n(hours–days BEFORE headache)",
"What happens: Hypothalamus wakes up early.\n"
"What you feel: Yawning a lot, craving sweet food,\n"
" mood swings, tiredness, needing to pee more.\n"
"<b>Think of it as:</b> The body's early warning system."),
(ORANGE, "2️⃣ AURA\n(20–60 min BEFORE headache; ~30% of people)",
"What happens: CSD wave crosses the brain.\n"
"What you see/feel: Flashing zig-zag lights,\n"
" blind spot, tingling arm, trouble finding words.\n"
"<b>Think of it as:</b> An electrical storm in the brain."),
(RED, "3️⃣ HEADACHE\n(4–72 hours)",
"What happens: Trigeminal nerve fires; CGRP released.\n"
"What you feel: Throbbing pain (usually one side),\n"
" nausea, vomiting, hate light & sound & smell.\n"
"<b>Think of it as:</b> The alarm bells are ringing loudly."),
(PURPLE, "4️⃣ POSTDROME\n(hours–1 day AFTER headache)",
"What happens: Brain slowly calms down.\n"
"What you feel: Exhausted, foggy, neck stiff,\n"
" hard to concentrate — like a hangover.\n"
"<b>Think of it as:</b> The brain recovering from the storm."),
]
ph_rows = []
for bg, title, body in phase_data:
lbg_map = {BLUE: LBLUE, ORANGE: LORANGE, RED: LRED, PURPLE: LPURPLE}
lbg = lbg_map.get(bg, LGRAY)
block = Table([
[p(title, SHEAD)],
[p(body, BODY)],
], colWidths=[97*mm],
style=TableStyle([
("BACKGROUND",(0,0),(0,0), bg),
("BACKGROUND",(0,1),(0,1), lbg),
("BOX",(0,0),(-1,-1), 0.8, bg),
("TOPPADDING",(0,0),(-1,-1), 2),
("BOTTOMPADDING",(0,0),(-1,-1), 2),
("LEFTPADDING",(0,0),(-1,-1), 4),
("RIGHTPADDING",(0,0),(-1,-1), 4),
]))
ph_rows.append([block])
if (bg, title, body) != phase_data[-1]:
ph_rows.append([Spacer(1, 1*mm)])
phases_col = Table([[phases_hdr]] + ph_rows, colWidths=[100*mm],
style=TableStyle([
("TOPPADDING",(0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
("LEFTPADDING",(0,0),(-1,-1), 0),
("RIGHTPADDING",(0,0),(-1,-1), 0),
]))
# ── CHEMICALS & TRIPTANS side-by-side ────────────────────────────────────────
chem_hdr = Table([[p("KEY CHEMICALS (MEDIATORS)", SHEAD)]],
colWidths=[115*mm],
style=TableStyle([
("BACKGROUND",(0,0),(-1,-1), TEAL),
("TOPPADDING",(0,0),(-1,-1), 3),
("BOTTOMPADDING",(0,0),(-1,-1), 3),
("LEFTPADDING",(0,0),(-1,-1), 4),
]))
chem_rows = [
[p("<b>Chemical</b>", BODYB), p("<b>What it does (simple!)</b>", BODYB), p("<b>Why it matters</b>", BODYB)],
[p("CGRP", BODYB),
p("Makes blood vessels swell + sends pain signals", BODY),
p("MAIN target of new drugs (gepants, erenumab)", BODY)],
[p("Serotonin\n(5-HT)", BODYB),
p("Controls pain pathways — levels change\nduring the attack", BODY),
p("Triptans work ON this chemical", BODY)],
[p("Substance P", BODYB),
p("Causes inflammation & vessel leakage\naround the brain", BODY),
p("Released alongside CGRP", BODY)],
[p("Nitric Oxide\n(NO)", BODYB),
p("Dilates blood vessels;\nnitroglycerin (a NO donor) triggers migraine", BODY),
p("Important trigger in research studies", BODY)],
[p("Dopamine", BODYB),
p("Causes yawning, nausea in the prodrome", BODY),
p("Anti-nausea drugs block dopamine", BODY)],
[p("Glutamate", BODYB),
p("Drives the brain's over-excitement\n& helps CSD spread", BODY),
p("Involved in central sensitization", BODY)],
]
chem_table = Table(chem_rows, colWidths=[22*mm, 53*mm, 38*mm],
style=TableStyle([
("BACKGROUND", (0,0),(-1,0), TEAL),
("TEXTCOLOR", (0,0),(-1,0), white),
("ROWBACKGROUNDS",(0,1),(-1,-1), [HexColor("#EAFAF1"), HexColor("#F0F3F4")]),
("GRID", (0,0),(-1,-1), 0.3, HexColor("#BDC3C7")),
("TOPPADDING", (0,0),(-1,-1), 2),
("BOTTOMPADDING", (0,0),(-1,-1), 2),
("LEFTPADDING", (0,0),(-1,-1), 3),
("RIGHTPADDING", (0,0),(-1,-1), 3),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
chem_col = Table([[chem_hdr],[chem_table]], colWidths=[115*mm],
style=TableStyle([("TOPPADDING",(0,0),(-1,-1),0),("BOTTOMPADDING",(0,0),(-1,-1),0),
("LEFTPADDING",(0,0),(-1,-1),0),("RIGHTPADDING",(0,0),(-1,-1),0)]))
# ── TRIPTANS SIMPLE BOX ───────────────────────────────────────────────────────
trip_col = Table([
[p("💊 WHY DO TRIPTANS WORK?", SHEAD)],
[p(
"Triptans (e.g. sumatriptan) are <b>5-HT1B/1D agonists</b>.\n\n"
"<b>Two ways they stop the attack:</b>\n\n"
"1️⃣ They <b>narrow</b> the swollen blood vessels\n"
" around the brain (5-HT<b>1B</b> receptor)\n\n"
"2️⃣ They <b>stop the trigeminal nerve</b> from\n"
" releasing CGRP and Substance P\n"
" (5-HT<b>1D</b> receptor — presynaptic)\n\n"
"<b>Best taken EARLY</b> — before the brain becomes\n"
"super-sensitive (allodynia sets in)\n\n"
"<b>⚠ NOT safe in:</b> heart disease, stroke history,\n"
"uncontrolled high blood pressure\n"
"(because of the blood vessel narrowing effect)",
BODY
)],
], colWidths=[56*mm],
style=TableStyle([
("BACKGROUND", (0,0),(0,0), GREEN),
("BACKGROUND", (0,1),(0,1), LGREEN),
("BOX", (0,0),(-1,-1), 0.8, GREEN),
("TOPPADDING", (0,0),(-1,-1), 3),
("BOTTOMPADDING", (0,0),(-1,-1), 3),
("LEFTPADDING", (0,0),(-1,-1), 4),
("RIGHTPADDING", (0,0),(-1,-1), 4),
]))
right_col = Table([
[chem_col, Spacer(2*mm,1), trip_col]
], colWidths=[116*mm, 2*mm, 57*mm],
style=TableStyle([
("VALIGN", (0,0),(-1,-1), "TOP"),
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING", (0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(-1,-1), 0),
]))
mid_col = Table([
[phases_col],
[Spacer(1,1.5*mm)],
[right_col],
], colWidths=[177*mm],
style=TableStyle([
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING", (0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(-1,-1), 0),
]))
# ── REMEMBER BOX (right strip) ───────────────────────────────────────────────
remember = [
("🧠", "NEURONAL first,\nvascular second"),
("⚡", "CSD = basis\nof AURA"),
("🔴", "CGRP = main\npain chemical"),
("🤚", "Allodynia = sign\nof sensitization"),
("⏱", "Triptans work\nbest taken EARLY"),
("👩", "3× more common\nin women"),
("🧬", "Genetic — runs\nin families"),
("😫", "WHO: one of most\ndisabling diseases"),
]
rem_rows = []
for icon, text in remember:
rem_rows.append([
Table([[
p(icon, S("ic", fontName="Helvetica", fontSize=14, leading=16, alignment=TA_CENTER)),
p(text, S("rt", fontName="Helvetica-Bold", fontSize=6.5, leading=8.5, textColor=NAVY)),
]], colWidths=[9*mm, 44*mm],
style=TableStyle([
("BACKGROUND",(0,0),(-1,-1), ALTROW),
("BOX",(0,0),(-1,-1), 0.4, HexColor("#AED6F1")),
("VALIGN",(0,0),(-1,-1),"MIDDLE"),
("TOPPADDING",(0,0),(-1,-1), 2),
("BOTTOMPADDING",(0,0),(-1,-1), 2),
("LEFTPADDING",(0,0),(-1,-1), 2),
("RIGHTPADDING",(0,0),(-1,-1), 2),
]))
])
rem_rows.append([Spacer(1, 1*mm)])
rem_hdr = Table([[p("REMEMBER!", SHEAD)]],
colWidths=[55*mm],
style=TableStyle([
("BACKGROUND",(0,0),(-1,-1), RED),
("TOPPADDING",(0,0),(-1,-1), 3),
("BOTTOMPADDING",(0,0),(-1,-1), 3),
("LEFTPADDING",(0,0),(-1,-1), 4),
]))
rem_col = Table([[rem_hdr]] + rem_rows, colWidths=[55*mm],
style=TableStyle([
("TOPPADDING",(0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
("LEFTPADDING",(0,0),(-1,-1), 0),
("RIGHTPADDING",(0,0),(-1,-1), 0),
]))
# ── ASSEMBLE 3-COLUMN MAIN ROW ───────────────────────────────────────────────
main_row = Table([[
steps_col,
mid_col,
rem_col,
]], colWidths=[57*mm, 178*mm, 56*mm],
style=TableStyle([
("VALIGN", (0,0),(-1,-1), "TOP"),
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING", (0,0),(-1,-1), 0),
("LEFTPADDING", (0,0),(-1,-1), 0),
("RIGHTPADDING", (0,0),(-1,-1), 1.5*mm),
("LINEAFTER", (0,0),(0,-1), 0.5, HexColor("#BDC3C7")),
("LINEAFTER", (1,0),(1,-1), 0.5, HexColor("#BDC3C7")),
]))
story.append(main_row)
story.append(Spacer(1, 1.5*mm))
# ── FOOTER ────────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.4, color=HexColor("#BDC3C7")))
story.append(Spacer(1, 0.8*mm))
story.append(p(
"Sources: Bradley & Daroff's Neurology in Clinical Practice • Adams & Victor's Principles of Neurology 12e • "
"Goodman & Gilman's The Pharmacological Basis of Therapeutics • Guyton & Hall Textbook of Medical Physiology • ICHD-3 | "
"CGRP = Calcitonin Gene-Related Peptide • CSD = Cortical Spreading Depression • 5-HT = Serotonin • PAG = Periaqueductal Gray • NO = Nitric Oxide",
FOOT
))
doc.build(story)
print(f"PDF written: {OUT}")
cd /home/daytona/workspace/migraine-ref-card && python3 generate_simple_card.py 2>&1
ls -lh /home/daytona/workspace/migraine-ref-card/
Migraine SimpleGuide UG
PDF Document · PDF
Explain the pathophysiology of migraine in a simplified way with reference of standard textbooks like harrison
"Migraine is primarily a disorder of the nervous system — the brain dysfunction comes first. Blood vessel changes are secondary, not the cause." — Harrison's Principles of Internal Medicine 22e
"Migraineurs are particularly sensitive to environmental and sensory stimuli; migraine-prone patients do not habituate easily to sensory stimuli."
| Trigger | Simple Explanation |
|---|---|
| Stress (or stress let-down) | Brain suddenly relaxes after being tense |
| Sleep changes | Disrupts brainstem control of pain pathways |
| Skipping meals / fasting | Low glucose affects brain metabolism |
| Hormonal changes | Estrogen drop before menstruation is a major trigger |
| Bright lights / loud sounds | Brain is already too sensitive |
| Certain foods / alcohol | Chemical stimulation (especially nitrates in wine) |
| Barometric pressure changes | Sensory overload in a sensitive brain |
"The sensory sensitivity that is characteristic of migraine is probably due to dysfunction of monoaminergic and other sensory control systems located in the brainstem and hypothalamus."
Important: CSD clearly explains the AURA. Whether CSD directly triggers the headache phase is still debated in the literature (Bradley & Daroff, 2023).
"The innervation of the large intracranial vessels and dura mater by the trigeminal nerve is known as the trigeminovascular system."
| Chemical | What it does |
|---|---|
| CGRP (Calcitonin Gene-Related Peptide) | Dilates blood vessels; major pain signaller - the MOST important |
| PACAP (Pituitary Adenylate Cyclase-Activating Polypeptide) | Similar to CGRP; newer target of therapy (Harrison's 22e) |
| Substance P | Causes plasma protein leakage from vessels |
| Nitric Oxide (NO) | Vasodilator; nitroglycerin (a NO donor) reliably triggers migraine |
"CGRP receptor antagonists, gepants, have now been shown to be effective in the acute and preventive treatment of migraine, and four monoclonal antibodies to CGRP, or its receptor, have been shown to be effective in migraine prevention."
"Recurrent and/or prolonged activation of the trigeminocervical system can lead to peripheral and central sensitization. Sensitized neurons have lower thresholds for activation, increased spontaneous activity, and receptive field expansion."
1st order neuron sensitized → trigeminal nerve endings
↓
2nd order neuron sensitized → trigeminal nucleus caudalis
↓ (allodynia begins here - scalp tenderness)
3rd order neuron sensitized → thalamus + cortex
↓ (whole-body allodynia, photophobia, phonophobia)
Clinical importance: Once allodynia (central sensitization) is established, triptans become LESS effective. This is why early treatment is emphasised.
| System | Normal role | In Migraine |
|---|---|---|
| Serotonin (5-HT) via Dorsal Raphe Nucleus | Descending pain suppression | Fluctuates across attack phases; levels drop during acute attack |
| Noradrenaline via Locus Coeruleus | Arousal, attention, pain modulation | Dysregulated; contributes to sensory hypersensitivity |
| Dopamine | Controls prodrome symptoms | D2 receptor hypersensitivity; causes yawning, nausea |
"In the late 1950s, methysergide was suggested to antagonize certain peripheral actions of 5-HT and was introduced as a migraine preventive... The triptans were designed to stimulate selectively subpopulations of 5-HT receptors."
"Triptans arrest nerve signaling in the nociceptive pathways of the trigeminovascular system, at least in the trigeminal nucleus caudalis and trigeminal sensory thalamus, in addition to promoting cranial vasoconstriction."
TRIGGERS (stress, sleep, hormones, food, light)
↓
HYPOTHALAMUS + BRAINSTEM over-activate
(prodrome: yawning, cravings, fatigue — hours before pain)
↓
CORTICAL SPREADING DEPRESSION [in 25-30%]
(wave of depolarization, 2-4 mm/min, occipital → frontal)
↓ → AURA (zig-zag lights, numbness, dysphasia)
TRIGEMINOVASCULAR ACTIVATION
(trigeminal nerve fires → CGRP + Substance P + NO released)
↓
STERILE NEUROGENIC INFLAMMATION
(meningeal vessels dilate, plasma leaks, inflammation forms)
↓
PAIN SIGNALS → trigeminal nucleus caudalis → thalamus → cortex
= THROBBING HEADACHE (usually unilateral, 4-72 hours)
↓
CENTRAL SENSITIZATION
(allodynia, photophobia, phonophobia, osmophobia)
↓
POSTDROME (fatigue, brain fog, neck stiffness — the "hangover")
| Concept | Key Point | Source |
|---|---|---|
| Migraine is neuronal, NOT vascular | Blood vessel changes are secondary | Harrison's 22e |
| Key structure | Trigeminovascular system | Harrison's, Adams & Victor |
| Main pain chemical | CGRP (and PACAP) | Harrison's 22e |
| Basis of aura | Cortical Spreading Depression (CSD) | Bradley & Daroff |
| CSD rate | 2-4 mm/min (occipital to frontal) | Bradley & Daroff |
| CSD and headache link | Still debated | Bradley & Daroff |
| Allodynia prevalence | ~75% during attacks | Bradley & Daroff |
| Triptan mechanism | 5-HT1B (vasoconstriction) + 5-HT1D (blocks CGRP release) | Harrison's 22e, Goodman & Gilman |
| Newer drugs | Gepants (CGRP antagonists), anti-CGRP mAbs, ditans | Harrison's 22e |
| Migraine prevalence | 15% women, 6% men; most disabling neurological condition worldwide | Harrison's 22e |
Make a visual flowchart poster showing the complete migraine pathway
ls /home/daytona/workspace/migraine-ref-card/
~/migraine-ref-card/generate_flowchart_poster.py
#!/usr/bin/env python3
"""
Migraine Complete Pathway — Visual Flowchart Poster
A3 portrait, rich colors, arrows, side branches, icons
"""
from reportlab.lib.pagesizes import A3
from reportlab.lib.units import mm, cm
from reportlab.lib.colors import HexColor, white, black
from reportlab.pdfgen.canvas import Canvas
from reportlab.platypus import Paragraph
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
import math
OUT = "/home/daytona/workspace/migraine-ref-card/Migraine_Flowchart_Poster.pdf"
# A3 portrait
W_MM, H_MM = 297, 420
W = W_MM * mm
H = H_MM * mm
# ── Palette ───────────────────────────────────────────────────────────────────
NAVY = HexColor("#0D2137")
DKBLUE = HexColor("#1A3A5C")
BLUE = HexColor("#2471A3")
LBLUE = HexColor("#D6EAF8")
TEAL = HexColor("#148F77")
LTEAL = HexColor("#D1F2EB")
ORANGE = HexColor("#D35400")
LORANGE = HexColor("#FDEBD0")
RED = HexColor("#C0392B")
LRED = HexColor("#FADBD8")
PURPLE = HexColor("#7D3C98")
LPURPLE = HexColor("#F4ECF7")
GREEN = HexColor("#1E8449")
LGREEN = HexColor("#D5F5E3")
GOLD = HexColor("#B7950B")
LGOLD = HexColor("#FEF9E7")
GRAY = HexColor("#566573")
LGRAY = HexColor("#F2F3F4")
MGRAY = HexColor("#D5D8DC")
c = Canvas(OUT, pagesize=(W, H))
# ── Drawing helpers ───────────────────────────────────────────────────────────
def rgba(color, alpha=1.0):
return color
def draw_rounded_rect(canvas, x, y, w, h, r=4*mm, fill_color=white, stroke_color=DKBLUE, stroke_width=1.2):
canvas.setFillColor(fill_color)
canvas.setStrokeColor(stroke_color)
canvas.setLineWidth(stroke_width)
canvas.roundRect(x, y, w, h, r, stroke=1, fill=1)
def draw_rect(canvas, x, y, w, h, fill_color=white, stroke_color=DKBLUE, stroke_width=1.0):
canvas.setFillColor(fill_color)
canvas.setStrokeColor(stroke_color)
canvas.setLineWidth(stroke_width)
canvas.rect(x, y, w, h, stroke=1, fill=1)
def draw_arrow_down(canvas, x, top_y, bottom_y, color=DKBLUE, width=1.5):
"""Draw a vertical arrow pointing downward."""
canvas.setStrokeColor(color)
canvas.setFillColor(color)
canvas.setLineWidth(width)
# Line
canvas.line(x, top_y, x, bottom_y + 4*mm)
# Arrowhead
aw = 3*mm
canvas.beginPath()
canvas.moveTo(x, bottom_y)
canvas.lineTo(x - aw, bottom_y + 4*mm)
canvas.lineTo(x + aw, bottom_y + 4*mm)
canvas.closePath()
canvas.drawPath(canvas._code and canvas._code or canvas, fill=1) # fallback
def filled_arrow_down(canvas, cx, top_y, bottom_y, color=RED, w=2.5*mm):
"""Solid filled downward arrow."""
canvas.setFillColor(color)
canvas.setStrokeColor(color)
canvas.setLineWidth(1.5)
mid = (top_y + bottom_y) / 2
# Shaft
canvas.rect(cx - w*0.4, mid, w*0.8, top_y - mid, fill=1, stroke=0)
# Head
canvas.beginPath()
canvas.moveTo(cx, bottom_y)
canvas.lineTo(cx - w, mid + 1*mm)
canvas.lineTo(cx + w, mid + 1*mm)
canvas.closePath()
canvas.setFillColor(color)
canvas.drawPath(canvas._code and None or None)
def arrow_down(canvas, cx, y_top, y_bot, color=DKBLUE, lw=2):
"""Simple arrow down."""
hw = 3.5*mm
canvas.setStrokeColor(color)
canvas.setFillColor(color)
canvas.setLineWidth(lw)
canvas.line(cx, y_top, cx, y_bot + hw)
p = canvas.beginPath()
p.moveTo(cx, y_bot)
p.lineTo(cx - hw, y_bot + hw*1.2)
p.lineTo(cx + hw, y_bot + hw*1.2)
p.close()
canvas.drawPath(p, fill=1, stroke=0)
def arrow_right(canvas, x_left, x_right, cy, color=DKBLUE, lw=1.5):
hw = 2.5*mm
canvas.setStrokeColor(color)
canvas.setFillColor(color)
canvas.setLineWidth(lw)
canvas.line(x_left, cy, x_right - hw, cy)
p = canvas.beginPath()
p.moveTo(x_right, cy)
p.lineTo(x_right - hw*1.2, cy + hw)
p.lineTo(x_right - hw*1.2, cy - hw)
p.close()
canvas.drawPath(p, fill=1, stroke=0)
def arrow_left(canvas, x_left, x_right, cy, color=DKBLUE, lw=1.5):
hw = 2.5*mm
canvas.setStrokeColor(color)
canvas.setFillColor(color)
canvas.setLineWidth(lw)
canvas.line(x_right, cy, x_left + hw, cy)
p = canvas.beginPath()
p.moveTo(x_left, cy)
p.lineTo(x_left + hw*1.2, cy + hw)
p.lineTo(x_left + hw*1.2, cy - hw)
p.close()
canvas.drawPath(p, fill=1, stroke=0)
def put_text(canvas, text, x, y, size=9, color=black, bold=False, align='center'):
font = "Helvetica-Bold" if bold else "Helvetica"
canvas.setFont(font, size)
canvas.setFillColor(color)
if align == 'center':
canvas.drawCentredString(x, y, text)
elif align == 'left':
canvas.drawString(x, y, text)
elif align == 'right':
canvas.drawRightString(x, y, text)
def put_para(canvas, text, x, y, width, size=8, color=black, bold=False, leading=None, align=TA_CENTER):
if leading is None:
leading = size * 1.35
font = "Helvetica-Bold" if bold else "Helvetica"
style = ParagraphStyle("tmp", fontName=font, fontSize=size, leading=leading,
textColor=color, alignment=align)
para = Paragraph(text, style)
w, h = para.wrap(width, 999)
para.drawOn(canvas, x - (width/2 if align == TA_CENTER else 0), y - h)
return h
def main_box(canvas, cx, cy, bw, bh, title, subtitle, body_lines,
hdr_color=DKBLUE, body_color=LGRAY, icon="", num=""):
"""Draw a main flowchart node box."""
x = cx - bw/2
y = cy - bh/2
hdr_h = 11*mm
# Shadow
canvas.setFillColor(HexColor("#C0C0C0"))
canvas.setStrokeColor(HexColor("#C0C0C0"))
canvas.roundRect(x+1.5*mm, y-1.5*mm, bw, bh, 4*mm, fill=1, stroke=0)
# Body
draw_rounded_rect(canvas, x, y, bw, bh, r=4*mm,
fill_color=body_color, stroke_color=hdr_color, stroke_width=1.5)
# Header band (top of box)
canvas.saveState()
canvas.clipPath(canvas.beginPath(), fill=0, stroke=0)
p2 = canvas.beginPath()
p2.roundRect(x, y + bh - hdr_h, bw, hdr_h, 4*mm)
canvas.clipPath(p2, fill=0, stroke=0)
canvas.setFillColor(hdr_color)
canvas.rect(x, y + bh - hdr_h, bw, hdr_h, fill=1, stroke=0)
canvas.restoreState()
# Draw header rect (simple approach)
canvas.setFillColor(hdr_color)
canvas.roundRect(x, y + bh - hdr_h, bw, hdr_h, 4*mm, fill=1, stroke=0)
# Cover bottom corners of header
canvas.rect(x, y + bh - hdr_h, bw, hdr_h/2, fill=1, stroke=0)
# Step number circle
if num:
cr = 4.5*mm
canvas.setFillColor(white)
canvas.setStrokeColor(hdr_color)
canvas.setLineWidth(0)
canvas.circle(x + cr + 2*mm, y + bh - hdr_h/2, cr, fill=1, stroke=0)
canvas.setFillColor(hdr_color)
canvas.setFont("Helvetica-Bold", 8)
canvas.drawCentredString(x + cr + 2*mm, y + bh - hdr_h/2 - 2.5, num)
# Icon + Title in header
tx = cx + (4*mm if num else 0)
canvas.setFont("Helvetica-Bold", 9)
canvas.setFillColor(white)
canvas.drawCentredString(tx, y + bh - hdr_h/2 - 3, f"{icon} {title}" if icon else title)
# Subtitle
if subtitle:
canvas.setFont("Helvetica-Oblique", 7)
canvas.setFillColor(HexColor("#555555"))
canvas.drawCentredString(cx, y + bh - hdr_h - 5*mm, subtitle)
# Body lines
line_y = y + bh - hdr_h - (8*mm if subtitle else 5*mm)
for line in body_lines:
if line.startswith("**") and line.endswith("**"):
canvas.setFont("Helvetica-Bold", 7.5)
canvas.setFillColor(hdr_color)
canvas.drawCentredString(cx, line_y, line[2:-2])
elif line.startswith("•"):
canvas.setFont("Helvetica", 7.2)
canvas.setFillColor(HexColor("#1B2631"))
canvas.drawString(x + 5*mm, line_y, line)
elif line == "---":
canvas.setStrokeColor(MGRAY)
canvas.setLineWidth(0.4)
canvas.line(x + 4*mm, line_y + 2*mm, x + bw - 4*mm, line_y + 2*mm)
else:
canvas.setFont("Helvetica", 7.2)
canvas.setFillColor(HexColor("#1B2631"))
canvas.drawCentredString(cx, line_y, line)
line_y -= 4.8*mm
def side_box(canvas, cx, cy, bw, bh, title, lines, color=TEAL, light=LTEAL):
x = cx - bw/2
y = cy - bh/2
draw_rounded_rect(canvas, x, y, bw, bh, r=3*mm,
fill_color=light, stroke_color=color, stroke_width=1.2)
# Header
canvas.setFillColor(color)
canvas.roundRect(x, y + bh - 8*mm, bw, 8*mm, 3*mm, fill=1, stroke=0)
canvas.rect(x, y + bh - 8*mm, bw, 4*mm, fill=1, stroke=0)
canvas.setFont("Helvetica-Bold", 7.5)
canvas.setFillColor(white)
canvas.drawCentredString(cx, y + bh - 5.5, title)
ly = y + bh - 11*mm
for line in lines:
if line.startswith("•"):
canvas.setFont("Helvetica", 6.5)
canvas.setFillColor(HexColor("#1B2631"))
canvas.drawString(x + 3*mm, ly, line)
else:
canvas.setFont("Helvetica-Bold", 6.5)
canvas.setFillColor(color)
canvas.drawCentredString(cx, ly, line)
ly -= 4.2*mm
# ══════════════════════════════════════════════════════════════════════════════
# POSTER LAYOUT
# ══════════════════════════════════════════════════════════════════════════════
# ── TITLE BANNER ──────────────────────────────────────────────────────────────
banner_h = 22*mm
canvas_obj = c
c.setFillColor(NAVY)
c.rect(0, H - banner_h, W, banner_h, fill=1, stroke=0)
# Diagonal accent stripe
c.setFillColor(HexColor("#C0392B"))
p = c.beginPath()
p.moveTo(0, H - banner_h)
p.lineTo(60*mm, H - banner_h)
p.lineTo(45*mm, H)
p.lineTo(0, H)
p.close()
c.drawPath(p, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 22)
c.setFillColor(white)
c.drawCentredString(W/2, H - 13*mm, "MIGRAINE PATHOPHYSIOLOGY")
c.setFont("Helvetica", 9)
c.setFillColor(HexColor("#AED6F1"))
c.drawCentredString(W/2, H - 20*mm,
"Complete Neurovascular Pathway | Harrison's 22e • Bradley & Daroff • Adams & Victor • Guyton & Hall")
# Red stripe bottom of banner
c.setFillColor(RED)
c.rect(0, H - banner_h - 2*mm, W, 2*mm, fill=1, stroke=0)
# ── LAYOUT PARAMETERS ─────────────────────────────────────────────────────────
# Main column centre x
MCX = W / 2
# Box widths
MW = 90*mm # main box width
SW = 62*mm # side box width
MH_TALL = 38*mm
MH_MED = 34*mm
MH_SH = 30*mm
# Starting Y (below banner)
TOP_Y = H - banner_h - 5*mm
# Vertical spacing between box centres
STEP = 45*mm
# ── NODE POSITIONS (centre y) ─────────────────────────────────────────────────
y1 = TOP_Y - 20*mm # Step 1: Trigger
y2 = y1 - STEP # Step 2: Hyperexcitable Brain
y3 = y2 - STEP # Step 3: CSD/Aura
y4 = y3 - STEP # Step 4: Trigeminovascular Activation
y5 = y4 - STEP # Step 5: Neurogenic Inflammation
y6 = y5 - STEP # Step 6: Central Sensitization
y7 = y6 - STEP # Step 7: HEADACHE
y8 = y7 - STEP # Step 8: Postdrome
# ── DRAW CONNECTING ARROWS ────────────────────────────────────────────────────
arrow_color = RED
arrow_top_gap = 5.5*mm
arrow_bot_gap = 5.5*mm
node_ys = [y1, y2, y3, y4, y5, y6, y7, y8]
box_heights = [MH_SH, MH_MED, MH_TALL, MH_TALL, MH_MED, MH_TALL, MH_TALL, MH_MED]
for i in range(len(node_ys)-1):
top_box_bot = node_ys[i] - box_heights[i]/2
bot_box_top = node_ys[i+1] + box_heights[i+1]/2
arrow_down(c, MCX,
top_box_bot - 1*mm,
bot_box_top + 1*mm,
color=RED, lw=2.2)
# ── NODE 1: TRIGGERS ──────────────────────────────────────────────────────────
main_box(c, MCX, y1, MW, MH_SH,
"TRIGGERS", "What sets off the attack?",
["• Stress / stress let-down",
"• Sleep disruption",
"• Skipping meals / fasting",
"• Hormonal changes (oestrogen drop)",
"• Bright lights, loud sounds",
"• Alcohol / nitrates / weather change"],
hdr_color=GRAY, body_color=LGRAY, num="1")
# ── NODE 2: HYPEREXCITABLE BRAIN ─────────────────────────────────────────────
main_box(c, MCX, y2, MW, MH_MED,
"HYPEREXCITABLE BRAIN", "The underlying problem",
["• Brain has LOW threshold for activation",
"• Does NOT habituate to sensory input",
"• Genetic basis: ion channel mutations",
"• Hypothalamus activates 24h BEFORE pain",
"• Brainstem: PAG, dorsal pons, raphe nuclei"],
hdr_color=BLUE, body_color=LBLUE, num="2")
# Side box: Genetics
side_box(c, MCX + MW/2 + 4*mm + SW/2, y2, SW, 28*mm,
"GENETICS",
["• CACNA1A (FHM type 1)",
"• ATP1A2 (FHM type 2)",
"• SCN1A (FHM type 3)",
"• 1st-degree relatives:",
" 2-4x higher risk",
"• More common in women (3:1)"],
color=BLUE, light=LBLUE)
arrow_right(c, MCX + MW/2, MCX + MW/2 + 4*mm, y2, color=BLUE, lw=1.5)
# ── NODE 3: CSD / AURA ───────────────────────────────────────────────────────
main_box(c, MCX, y3, MW, MH_TALL,
"CORTICAL SPREADING DEPRESSION", "Basis of Aura (~25-30% of patients)",
["**Wave of neuronal depolarisation**",
"• Starts in occipital cortex (visual area)",
"• Spreads FORWARD at 2-4 mm/min",
"• Does NOT follow arterial territories",
"---",
"• Flashing zig-zag lights (scintillating scotoma)",
"• Blind spot, numbness, speech difficulty",
"• Symptoms march over ~20-30 min"],
hdr_color=ORANGE, body_color=LORANGE, num="3")
# Side box: Aura symptoms detail
side_box(c, MCX - MW/2 - 4*mm - SW/2, y3, SW, MH_TALL,
"AURA TYPES",
["Visual (most common):",
"• Zig-zag fortification lines",
"• Scintillating scotoma",
"• Blurring / blind spot",
"Sensory:",
"• Numbness / pins & needles",
"Motor (rare - FHM):",
"• Unilateral weakness",
"Speech:",
"• Dysphasia"],
color=ORANGE, light=LORANGE)
arrow_left(c, MCX - MW/2 - 4*mm, MCX - MW/2, y3, color=ORANGE, lw=1.5)
# ── NODE 4: TRIGEMINOVASCULAR ACTIVATION ─────────────────────────────────────
main_box(c, MCX, y4, MW, MH_TALL,
"TRIGEMINOVASCULAR ACTIVATION", "The KEY mechanism of migraine pain",
["• Trigeminal nerve fibres wrap meninges & vessels",
"• CSD or brainstem signals activate trigeminal endings",
"---",
"**Neuropeptides released:**",
"• CGRP — main pain mediator (vasodilation)",
"• Substance P — plasma protein leakage",
"• PACAP — emerging mediator (Harrison's 22e)",
"• Nitric Oxide — vasodilation"],
hdr_color=RED, body_color=LRED, num="4")
# Side box: CGRP importance
side_box(c, MCX + MW/2 + 4*mm + SW/2, y4, SW, MH_TALL,
"WHY CGRP MATTERS",
["• Rises sharply during attack",
"• Dilates meningeal vessels",
"• Sensitises trigeminal fibres",
"• Promotes neurogenic inflam.",
"CGRP-targeted drugs:",
"• Gepants (acute + preventive)",
"• Erenumab (anti-CGRP-R mAb)",
"• Fremanezumab",
"• Galcanezumab",
"• Eptinezumab"],
color=RED, light=LRED)
arrow_right(c, MCX + MW/2, MCX + MW/2 + 4*mm, y4, color=RED, lw=1.5)
# ── NODE 5: NEUROGENIC INFLAMMATION ──────────────────────────────────────────
main_box(c, MCX, y5, MW, MH_MED,
"STERILE NEUROGENIC INFLAMMATION", "Around the meningeal vessels",
["• Meningeal blood vessels DILATE",
"• Plasma proteins leak out of vessel walls",
"• Mast cells degranulate",
"• Inflammatory soup forms around meninges",
"• Meninges swell and become hypersensitive"],
hdr_color=PURPLE, body_color=LPURPLE, num="5")
# ── NODE 6: CENTRAL SENSITIZATION ────────────────────────────────────────────
main_box(c, MCX, y6, MW, MH_TALL,
"CENTRAL SENSITIZATION", "Why everything becomes painful",
["• Repeated pain signals LOWER threshold of neurons",
"• 1st order: trigeminal nerve endings (scalp tender)",
"• 2nd order: trigeminal nucleus caudalis",
"• 3rd order: thalamus + cortex",
"---",
"• Allodynia: gentle touch = pain (~75% of attacks)",
"• Photophobia, Phonophobia, Osmophobia",
"• Triptans LESS effective once allodynia sets in"],
hdr_color=PURPLE, body_color=LPURPLE, num="6")
# Side box: Sensitization cascade
side_box(c, MCX - MW/2 - 4*mm - SW/2, y6, SW, MH_TALL,
"SENSITIZATION CASCADE",
["Peripheral:",
"• C-fibres detect vessel pulsation",
"• = Throbbing quality of pain",
"1st order → 2nd order:",
"• Scalp tenderness",
"• Allodynia begins",
"2nd → 3rd order:",
"• Thalamic neurons fire",
"• Light/sound/smell = pain",
"• Whole-body allodynia"],
color=PURPLE, light=LPURPLE)
arrow_left(c, MCX - MW/2 - 4*mm, MCX - MW/2, y6, color=PURPLE, lw=1.5)
# ── NODE 7: HEADACHE ─────────────────────────────────────────────────────────
# Make this the focal "output" box - larger and bold
HW = 100*mm
HH = 40*mm
hx = MCX - HW/2
hy = y7 - HH/2
# Glow effect
c.setFillColor(HexColor("#FDEDEC"))
c.setStrokeColor(RED)
c.setLineWidth(0)
c.roundRect(hx - 2*mm, hy - 2*mm, HW + 4*mm, HH + 4*mm, 6*mm, fill=1, stroke=0)
main_box(c, MCX, y7, HW, HH,
"MIGRAINE HEADACHE", "The full clinical attack",
["**Throbbing, pulsating, usually unilateral**",
"• Moderate to severe intensity",
"• Lasts 4 to 72 hours untreated",
"• Aggravated by physical activity",
"---",
"• Nausea / Vomiting",
"• Photophobia + Phonophobia + Osmophobia",
"• Cutaneous allodynia"],
hdr_color=RED, body_color=HexColor("#FDFEFE"), num="7")
# Side box: Why TRIPTANS work
side_box(c, MCX + HW/2 + 4*mm + SW/2, y7, SW, HH,
"WHY TRIPTANS WORK",
["Triptans = 5-HT1B/1D agonists",
"",
"5-HT1B (blood vessels):",
"• Cranial vasoconstriction",
"• Reverses meningeal dilation",
"",
"5-HT1D (nerve terminals):",
"• Blocks CGRP + SP release",
"• Stops neurogenic inflam.",
"",
"Take EARLY (before allodynia)",
"Avoid in IHD / stroke / HTN"],
color=GREEN, light=LGREEN)
arrow_right(c, MCX + HW/2, MCX + HW/2 + 4*mm, y7, color=GREEN, lw=1.5)
# Serotonin side box LEFT of headache
side_box(c, MCX - HW/2 - 4*mm - SW/2, y7, SW, HH,
"NEUROTRANSMITTERS",
["CGRP: main pain + vasodilation",
"5-HT: pain modulation",
"(fluctuates across phases)",
"Dopamine: nausea, yawning",
"Glutamate: drives CSD",
"Noradrenaline: sensory hypersens.",
"PACAP: emerging (Harrison's 22e)",
"NO: vasodilator, triggers attack"],
color=TEAL, light=LTEAL)
arrow_left(c, MCX - HW/2 - 4*mm, MCX - HW/2, y7, color=TEAL, lw=1.5)
# ── NODE 8: POSTDROME ────────────────────────────────────────────────────────
main_box(c, MCX, y8, MW, MH_MED,
"POSTDROME", "The 'migraine hangover' (hours to 1 day)",
["• Fatigue and exhaustion",
"• Cognitive fog / poor concentration",
"• Neck stiffness",
"• Mood changes (elation or depression)",
"• Brain slowly resets to baseline"],
hdr_color=GRAY, body_color=LGRAY, num="8")
# ── STEP LABELS ON ARROW SHAFT ───────────────────────────────────────────────
label_data = [
(y1, y2, "Brain enters\nalert state"),
(y2, y3, "Electrical\nwave begins"),
(y3, y4, "Pain nerve\nactivated"),
(y4, y5, "Chemicals\nreleased"),
(y5, y6, "Neurons\noverstimulated"),
(y6, y7, "Pain fully\nperceived"),
(y7, y8, "Attack\nresolves"),
]
for y_top_node, y_bot_node, label in label_data:
top_h = box_heights[node_ys.index(y_top_node)] if y_top_node in node_ys else MH_MED
bot_h = box_heights[node_ys.index(y_bot_node)] if y_bot_node in node_ys else MH_MED
mid_y = (y_top_node - top_h/2 + y_bot_node + bot_h/2) / 2
c.setFillColor(HexColor("#FDFEFE"))
c.setStrokeColor(MGRAY)
c.setLineWidth(0.4)
c.roundRect(MCX + 4*mm, mid_y - 4*mm, 22*mm, 8.5*mm, 2*mm, fill=1, stroke=1)
c.setFont("Helvetica-Oblique", 5.8)
c.setFillColor(GRAY)
lines = label.split("\n")
c.drawString(MCX + 5*mm, mid_y + 1.5*mm, lines[0])
if len(lines) > 1:
c.drawString(MCX + 5*mm, mid_y - 2.5*mm, lines[1])
# ── FOOTER ────────────────────────────────────────────────────────────────────
footer_h = 14*mm
c.setFillColor(NAVY)
c.rect(0, 0, W, footer_h, fill=1, stroke=0)
c.setFillColor(RED)
c.rect(0, footer_h, W, 1.5*mm, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 7)
c.setFillColor(white)
c.drawCentredString(W/2, footer_h - 5*mm,
"Sources: Harrison's Principles of Internal Medicine 22e (2025) • Bradley & Daroff's Neurology in Clinical Practice • Adams & Victor's Principles of Neurology 12e • Guyton & Hall Medical Physiology")
c.setFont("Helvetica", 6.5)
c.setFillColor(HexColor("#AED6F1"))
c.drawCentredString(W/2, footer_h - 10*mm,
"CGRP = Calcitonin Gene-Related Peptide • CSD = Cortical Spreading Depression • PAG = Periaqueductal Gray • FHM = Familial Hemiplegic Migraine • 5-HT = Serotonin • NO = Nitric Oxide • PACAP = Pituitary Adenylate Cyclase-Activating Polypeptide • mAb = Monoclonal Antibody")
# ── LEGEND ────────────────────────────────────────────────────────────────────
leg_x = 8*mm
leg_y = y8 - MH_MED/2 - 8*mm
legend_items = [
(GRAY, "Trigger / Prodrome / Postdrome"),
(BLUE, "Brain / Neural Mechanism"),
(ORANGE, "Cortical Spreading Depression"),
(RED, "Pain Pathway / Headache"),
(PURPLE, "Sensitization"),
(GREEN, "Treatment"),
(TEAL, "Neurotransmitters"),
]
c.setFillColor(LGRAY)
c.setStrokeColor(MGRAY)
c.setLineWidth(0.5)
c.roundRect(leg_x - 2*mm, leg_y - 4*mm, 55*mm, len(legend_items)*6.5*mm + 10*mm, 3*mm, fill=1, stroke=1)
c.setFont("Helvetica-Bold", 7)
c.setFillColor(DKBLUE)
c.drawString(leg_x, leg_y + len(legend_items)*6.5*mm + 3*mm, "COLOUR KEY")
for i, (col, label) in enumerate(legend_items):
ly = leg_y + (len(legend_items) - 1 - i) * 6.5*mm
c.setFillColor(col)
c.roundRect(leg_x, ly, 6*mm, 4.5*mm, 1*mm, fill=1, stroke=0)
c.setFont("Helvetica", 6.5)
c.setFillColor(HexColor("#1B2631"))
c.drawString(leg_x + 8*mm, ly + 0.8*mm, label)
c.save()
print(f"Poster saved: {OUT}")
cd /home/daytona/workspace/migraine-ref-card && python3 generate_flowchart_poster.py 2>&1
ls -lh /home/daytona/workspace/migraine-ref-card/Migraine_Flowchart_Poster.pdf
Migraine Flowchart Poster
PDF Document · PDF
| Step | Node | Colour |
|---|---|---|
| 1 | Triggers | Grey |
| 2 | Hyperexcitable Brain | Blue |
| 3 | Cortical Spreading Depression | Orange |
| 4 | Trigeminovascular Activation | Red |
| 5 | Sterile Neurogenic Inflammation | Purple |
| 6 | Central Sensitization | Purple |
| 7 | MIGRAINE HEADACHE (highlighted focal box) | Red |
| 8 | Postdrome | Grey |