Hyy i am a physiotherapist
I need to know the when we should perfom flexion regime erxercise and when to perfom extension regime exercise
flexion regime vs extension regime physiotherapy indications Williams McKenzie exercises
| Condition | Rationale |
|---|---|
| Lumbar Spinal Stenosis | Flexion opens the spinal canal, reducing compression on neural elements. Confirmed: Miller's Review of Orthopaedics recommends Williams flexion exercises as first-line non-operative management |
| Facet Joint Arthropathy / Osteoarthritis | Flexion offloads the posterior facets; extension compresses them and worsens pain |
| Degenerative Spondylolisthesis | Flexion reduces forward slip tendency and opens the narrowed canal |
| Isthmic Spondylolisthesis (Grade I-II) | Flexion stabilizes and reduces shear; extension worsens the slip |
| Hyperlordosis / Increased lumbar lordosis | Exercises aim to flatten lordosis and reduce posterior element stress |
| Pregnancy-related back pain | Extension is contraindicated; gentle flexion is safer |
| Elderly patients with degenerative changes | Extension often painful due to arthritic facets |
| Condition | Rationale |
|---|---|
| Lumbar Disc Herniation / Prolapse | Extension pushes the nucleus anteriorly, away from the posterior annulus and nerve root |
| Disc Bulge with nerve root irritation | Same mechanism - centralization phenomenon |
| Acute low back pain with peripheralization | McKenzie assessment: if extension centralizes symptoms, use extension regime |
| Flattened lumbar lordosis | Restores normal lumbar curve |
| Postural low back pain (flexion-based postures) | Corrects prolonged flexion postures (desk workers, students) |
| Segmental instability with flexion pain | Extension stabilizes the segment |
Patient has lumbar pain
|
├── Pain worse on FLEXION (sitting, bending)?
│ Pain better on EXTENSION (standing)?
│ Younger patient? Disc pathology suspected?
│ → USE EXTENSION REGIME (McKenzie)
│
└── Pain worse on EXTENSION (walking, standing)?
Pain better on FLEXION (sitting, cycling)?
Older patient? Stenosis/facet changes?
→ USE FLEXION REGIME (Williams)
| Regime | Contraindicated in |
|---|---|
| Extension regime | Spinal stenosis, spondylolisthesis (active), osteoporosis with compression fracture, facet arthritis |
| Flexion regime | Acute disc herniation with sciatica, cauda equina syndrome, osteoporosis (loaded flexion), post-spinal fusion (relative) |
Create a quick reference PDF with exercise progressions for flexion vs extension regimes
mkdir -p /tmp/workspace/physio-pdf && ls /tmp/workspace/
~/physio-pdf/generate_pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether
)
from reportlab.graphics.shapes import Drawing, Rect, String
from reportlab.graphics import renderPDF
OUTPUT = "/tmp/workspace/physio-pdf/Flexion_vs_Extension_Regime_Quick_Reference.pdf"
# ── Colour palette ──────────────────────────────────────────────────────────
BLUE_DARK = colors.HexColor("#1A3A5C") # header / title
BLUE_MID = colors.HexColor("#2563A8") # flexion accent
GREEN_MID = colors.HexColor("#1A7A4A") # extension accent
BLUE_LIGHT = colors.HexColor("#D6E8FA") # flexion row bg
GREEN_LIGHT = colors.HexColor("#C8EDD8") # extension row bg
AMBER = colors.HexColor("#F59E0B") # warning / caution
RED_LIGHT = colors.HexColor("#FEE2E2") # contraindication bg
GREY_LIGHT = colors.HexColor("#F1F5F9") # alt row
WHITE = colors.white
BLACK = colors.HexColor("#1E1E1E")
# ── Document ────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT,
pagesize=A4,
leftMargin=15*mm, rightMargin=15*mm,
topMargin=18*mm, bottomMargin=15*mm,
title="Flexion vs Extension Exercise Regime – Quick Reference",
author="Orris Physiotherapy Assistant",
)
W = A4[0] - 30*mm # usable width
styles = getSampleStyleSheet()
# ── Custom paragraph styles ─────────────────────────────────────────────────
def ps(name, parent="Normal", **kw):
return ParagraphStyle(name, parent=styles[parent], **kw)
title_style = ps("Title2", fontSize=20, textColor=WHITE, leading=26, alignment=TA_CENTER, fontName="Helvetica-Bold")
sub_style = ps("Sub", fontSize=10, textColor=BLUE_LIGHT, leading=14, alignment=TA_CENTER, fontName="Helvetica")
h1_style = ps("H1", fontSize=13, textColor=WHITE, leading=17, fontName="Helvetica-Bold", alignment=TA_CENTER)
h2_style = ps("H2", fontSize=11, textColor=WHITE, leading=15, fontName="Helvetica-Bold", alignment=TA_CENTER)
body_style = ps("Body2", fontSize=8.5, textColor=BLACK, leading=12, fontName="Helvetica")
bold_style = ps("Bold2", fontSize=8.5, textColor=BLACK, leading=12, fontName="Helvetica-Bold")
small_style = ps("Small", fontSize=7.5, textColor=BLACK, leading=11, fontName="Helvetica")
small_bold = ps("SmBold", fontSize=7.5, textColor=BLACK, leading=11, fontName="Helvetica-Bold")
note_style = ps("Note", fontSize=7.5, textColor=colors.HexColor("#374151"), leading=11, fontName="Helvetica-Oblique")
red_style = ps("Red", fontSize=8, textColor=colors.HexColor("#991B1B"), leading=12, fontName="Helvetica-Bold")
green_hdr = ps("GrHdr", fontSize=9, textColor=WHITE, leading=13, fontName="Helvetica-Bold", alignment=TA_CENTER)
blue_hdr = ps("BlHdr", fontSize=9, textColor=WHITE, leading=13, fontName="Helvetica-Bold", alignment=TA_CENTER)
pearl_style = ps("Pearl", fontSize=8.5, textColor=BLUE_DARK, leading=12, fontName="Helvetica-Bold")
story = []
# ══════════════════════════════════════════════════════════════════
# HEADER BANNER
# ══════════════════════════════════════════════════════════════════
banner_data = [[
Paragraph("PHYSIOTHERAPY QUICK REFERENCE", title_style),
]]
banner_table = Table(banner_data, colWidths=[W])
banner_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), BLUE_DARK),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING",(0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("ROUNDEDCORNERS", [6]),
]))
story.append(banner_table)
sub_data = [[Paragraph("Flexion Regime (Williams') vs Extension Regime (McKenzie) — Exercise Progressions & Clinical Decision Guide", sub_style)]]
sub_table = Table(sub_data, colWidths=[W])
sub_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), BLUE_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
]))
story.append(sub_table)
story.append(Spacer(1, 6*mm))
# ══════════════════════════════════════════════════════════════════
# SECTION 1 – AT-A-GLANCE DECISION GUIDE
# ══════════════════════════════════════════════════════════════════
dec_header = Table([[Paragraph("CLINICAL DECISION GUIDE — WHICH REGIME TO CHOOSE?", h1_style)]], colWidths=[W])
dec_header.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), BLUE_DARK),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("ROUNDEDCORNERS", [4]),
]))
story.append(dec_header)
story.append(Spacer(1, 2*mm))
half = (W - 4*mm) / 2
# Flexion column content
flex_items = [
("FLEXION REGIME (Williams')", BLUE_MID),
]
flex_body = [
["Pain WORSE with:", "extension, walking, standing, going downhill"],
["Pain BETTER with:", "sitting, leaning forward, cycling, squatting"],
["Patient profile:", "Older adults, degenerative changes, obesity"],
["Directional pref:", "Flexion centralizes / reduces symptoms"],
]
ext_items = [
("EXTENSION REGIME (McKenzie)", GREEN_MID),
]
ext_body = [
["Pain WORSE with:", "prolonged sitting, bending, flexion activities"],
["Pain BETTER with:", "standing, walking, lying prone, extension"],
["Patient profile:", "Younger adults, disc pathology, desk workers"],
["Directional pref:", "Extension centralizes / reduces symptoms"],
]
def side_table(title, color, rows, width):
data = [[Paragraph(title, h2_style)]]
t = Table(data, colWidths=[width])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
body_rows = []
for i,(k,v) in enumerate(rows):
bg = BLUE_LIGHT if color == BLUE_MID else GREEN_LIGHT
body_rows.append([
Paragraph(k, small_bold),
Paragraph(v, small_style),
])
bt = Table(body_rows, colWidths=[width*0.38, width*0.62])
bg_color = BLUE_LIGHT if color == BLUE_MID else GREEN_LIGHT
bt.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), bg_color),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#CBD5E1")),
("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"),
]))
return [t, bt]
flex_parts = side_table("FLEXION REGIME (Williams')", BLUE_MID, flex_body, half)
ext_parts = side_table("EXTENSION REGIME (McKenzie)", GREEN_MID, ext_body, half)
# 2-column layout via a parent table
two_col = Table(
[[flex_parts[0], ext_parts[0]],
[flex_parts[1], ext_parts[1]]],
colWidths=[half, half],
spaceBefore=0, spaceAfter=0,
)
two_col.setStyle(TableStyle([
("LEFTPADDING", (0,0), (-1,-1), 0),
("RIGHTPADDING", (0,0), (-1,-1), 0),
("TOPPADDING", (0,0), (-1,-1), 0),
("BOTTOMPADDING", (0,0), (-1,-1), 0),
("COLUMNCOLUMN", (0,0), (-1,-1), 4*mm),
]))
story.append(two_col)
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════
# SECTION 2 – INDICATIONS
# ══════════════════════════════════════════════════════════════════
ind_header = Table([[Paragraph("INDICATIONS", h1_style)]], colWidths=[W])
ind_header.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), BLUE_DARK),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("ROUNDEDCORNERS", [4]),
]))
story.append(ind_header)
story.append(Spacer(1, 2*mm))
ind_data = [
[Paragraph("FLEXION REGIME", blue_hdr), Paragraph("EXTENSION REGIME", green_hdr)],
[Paragraph("• Lumbar Spinal Stenosis", body_style), Paragraph("• Lumbar Disc Herniation / Prolapse", body_style)],
[Paragraph("• Facet Joint Arthropathy / OA", body_style), Paragraph("• Disc Bulge with nerve root irritation", body_style)],
[Paragraph("• Degenerative Spondylolisthesis", body_style), Paragraph("• Acute LBP with flexion-based posture", body_style)],
[Paragraph("• Isthmic Spondylolisthesis (Gr I–II)", body_style), Paragraph("• Postural LBP (desk workers, students)", body_style)],
[Paragraph("• Hyperlordosis / Posterior element overload", body_style), Paragraph("• Flattened lumbar lordosis", body_style)],
[Paragraph("• Pregnancy-related back pain", body_style), Paragraph("• Segmental instability with flexion pain", body_style)],
[Paragraph("• Elderly with degenerative changes", body_style), Paragraph("• Sciatica centralizing with extension", body_style)],
]
ind_table = Table(ind_data, colWidths=[W/2, W/2])
ind_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (0,0), BLUE_MID),
("BACKGROUND", (1,0), (1,0), GREEN_MID),
("BACKGROUND", (0,1), (0,1), BLUE_LIGHT),
("BACKGROUND", (1,1), (1,1), GREEN_LIGHT),
("BACKGROUND", (0,2), (0,2), WHITE),
("BACKGROUND", (1,2), (1,2), WHITE),
("BACKGROUND", (0,3), (0,3), BLUE_LIGHT),
("BACKGROUND", (1,3), (1,3), GREEN_LIGHT),
("BACKGROUND", (0,4), (0,4), WHITE),
("BACKGROUND", (1,4), (1,4), WHITE),
("BACKGROUND", (0,5), (0,5), BLUE_LIGHT),
("BACKGROUND", (1,5), (1,5), GREEN_LIGHT),
("BACKGROUND", (0,6), (0,6), WHITE),
("BACKGROUND", (1,6), (1,6), WHITE),
("BACKGROUND", (0,7), (0,7), BLUE_LIGHT),
("BACKGROUND", (1,7), (1,7), GREEN_LIGHT),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#CBD5E1")),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]))
story.append(ind_table)
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════
# SECTION 3 – EXERCISE PROGRESSIONS
# ══════════════════════════════════════════════════════════════════
prog_header = Table([[Paragraph("EXERCISE PROGRESSIONS", h1_style)]], colWidths=[W])
prog_header.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), BLUE_DARK),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("ROUNDEDCORNERS", [4]),
]))
story.append(prog_header)
story.append(Spacer(1, 2*mm))
# ── Flexion progression header ───────────────────────────────────
flex_prog_hdr = Table([[Paragraph("FLEXION REGIME — Williams' Exercise Progression", h2_style)]], colWidths=[W])
flex_prog_hdr.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), BLUE_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(flex_prog_hdr)
story.append(Spacer(1, 1.5*mm))
flex_prog = [
["PHASE", "EXERCISE", "SETS × REPS", "TECHNIQUE / NOTES"],
["Phase 1\nAcute\n(Wk 1–2)",
"1. Posterior Pelvic Tilt\n2. Single Knee to Chest\n3. Double Knee to Chest",
"2 × 10\n2 × 10\n2 × 10",
"Supine. Flatten lumbar curve against floor. Hold 5 sec.\nSupine. Pull knee gently to chest. Hold 5–10 sec.\nBoth knees to chest. Gently rock side to side. Hold 5 sec."],
["Phase 2\nSub-acute\n(Wk 3–4)",
"4. Partial Curl-up (Crunch)\n5. Hamstring Stretch (supine)\n6. Hip Flexor Stretch",
"2 × 10\n3 × 30 sec\n3 × 30 sec",
"Supine, knees bent. Lift shoulders only — avoid full sit-up.\nSupine. Straighten knee, dorsiflex ankle. Feel pull in hamstring.\nKneeling lunge. Keep trunk upright. Stretch anterior hip/thigh."],
["Phase 3\nFunctional\n(Wk 5–6)",
"7. Supported Squat\n8. Cat-Camel Stretch\n9. Seated Lumbar Flexion",
"3 × 12\n2 × 10 cycles\n2 × 10",
"Feet shoulder-width. Use wall/chair if needed. Control descent.\nOn all-fours. Arch (camel) then flex (cat) spine rhythmically.\nSeated, slowly lean forward reaching towards feet. Hold 5 sec."],
["Phase 4\nStrength\n(Wk 7+)",
"10. Dead Bug\n11. Bridging (posterior tilt)\n12. Wall Squat with hold",
"3 × 8 each\n3 × 12\n3 × 30 sec",
"Supine. Opposite arm-leg extension. Maintain flat lumbar.\nBridge: posterior pelvic tilt first, then lift. Avoid hyperextension.\nBack against wall, thighs parallel. Posterior tilt maintained."],
]
flex_col_w = [W*0.10, W*0.30, W*0.16, W*0.44]
flex_prog_table = Table(flex_prog, colWidths=flex_col_w)
flex_prog_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), BLUE_MID),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 8),
("BACKGROUND", (0,1), (-1,1), BLUE_LIGHT),
("BACKGROUND", (0,2), (-1,2), WHITE),
("BACKGROUND", (0,3), (-1,3), BLUE_LIGHT),
("BACKGROUND", (0,4), (-1,4), WHITE),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#CBD5E1")),
("FONTNAME", (0,1), (-1,-1), "Helvetica"),
("FONTSIZE", (0,1), (-1,-1), 7.5),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
("FONTNAME", (0,1), (0,-1), "Helvetica-Bold"),
("FONTSIZE", (0,1), (0,-1), 7.5),
("TEXTCOLOR", (0,1), (0,-1), BLUE_MID),
]))
story.append(flex_prog_table)
story.append(Spacer(1, 4*mm))
# ── Extension progression header ────────────────────────────────
ext_prog_hdr = Table([[Paragraph("EXTENSION REGIME — McKenzie Exercise Progression", h2_style)]], colWidths=[W])
ext_prog_hdr.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), GREEN_MID),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(ext_prog_hdr)
story.append(Spacer(1, 1.5*mm))
ext_prog = [
["PHASE", "EXERCISE", "SETS × REPS", "TECHNIQUE / NOTES"],
["Phase 1\nAcute\n(Wk 1–2)",
"1. Prone Lying (Passive Ext.)\n2. Prone on Elbows (Sphinx)\n3. Prone Press-Up — partial",
"Sustained 5–10 min\n3 × 1 min\n3 × 10",
"Lie face-down. Allow gravity to extend spine passively. Relax.\nElbows under shoulders. Gently lift chest. Hold. Monitor symptoms.\nHands under shoulders. Push upper body up — pelvis stays down. Stop at first resistance."],
["Phase 2\nSub-acute\n(Wk 3–4)",
"4. Prone Press-Up — full\n5. Standing Extension\n6. Sustained Prone Extension",
"3 × 10\n3 × 10\n3 × 30 sec",
"Full press-up with arms straight. Hips remain on mat. Exhale on way up.\nStanding, hands on hips. Lean back from waist. Look up. Return slowly.\nMaximum prone extension position. Hold. Breathe normally."],
["Phase 3\nFunctional\n(Wk 5–6)",
"7. Bird-Dog (neutral extension)\n8. Superman / Back Extension\n9. Wall Extension Stretch",
"3 × 10 each\n3 × 12\n3 × 30 sec",
"On all-fours. Extend opposite arm-leg. Maintain neutral to slight extension.\nProne. Lift chest and legs together. Hold 2 sec at top. Control down.\nStanding, hands on wall at hip height. Lean hips forward into extension."],
["Phase 4\nStrength\n(Wk 7+)",
"10. Prone Hip Extension\n11. Romanian Deadlift (light)\n12. McKenzie Repeated Extensions",
"3 × 12 each\n3 × 10\n3 × 15",
"Prone. Lift one straight leg — squeeze glute. Maintain lumbar extension.\nHinge at hips with neutral-to-extended lumbar. Load only when pain-free.\nStanding or prone. Repeated end-range extensions. Monitor centralization."],
]
ext_col_w = [W*0.10, W*0.30, W*0.16, W*0.44]
ext_prog_table = Table(ext_prog, colWidths=ext_col_w)
ext_prog_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), GREEN_MID),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,0), 8),
("BACKGROUND", (0,1), (-1,1), GREEN_LIGHT),
("BACKGROUND", (0,2), (-1,2), WHITE),
("BACKGROUND", (0,3), (-1,3), GREEN_LIGHT),
("BACKGROUND", (0,4), (-1,4), WHITE),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#CBD5E1")),
("FONTNAME", (0,1), (-1,-1), "Helvetica"),
("FONTSIZE", (0,1), (-1,-1), 7.5),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 5),
("RIGHTPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
("FONTNAME", (0,1), (0,-1), "Helvetica-Bold"),
("FONTSIZE", (0,1), (0,-1), 7.5),
("TEXTCOLOR", (0,1), (0,-1), GREEN_MID),
]))
story.append(ext_prog_table)
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════
# SECTION 4 – CONTRAINDICATIONS
# ══════════════════════════════════════════════════════════════════
con_header = Table([[Paragraph("CONTRAINDICATIONS", h1_style)]], colWidths=[W])
con_header.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#991B1B")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("ROUNDEDCORNERS", [4]),
]))
story.append(con_header)
story.append(Spacer(1, 2*mm))
contra_data = [
[Paragraph("FLEXION REGIME — Do NOT use when:", red_style),
Paragraph("EXTENSION REGIME — Do NOT use when:", red_style)],
[Paragraph("• Acute disc herniation with active sciatica\n• Cauda equina syndrome (ABSOLUTE)\n• Post-spinal fusion (relative)\n• Loaded flexion in osteoporosis\n• Acute fracture / instability", body_style),
Paragraph("• Lumbar spinal stenosis\n• Active spondylolisthesis (moderate–severe)\n• Osteoporotic compression fracture\n• Facet arthritis (extension painful)\n• Inflammatory arthritis in active flare", body_style)],
]
contra_table = Table(contra_data, colWidths=[W/2, W/2])
contra_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,0), RED_LIGHT),
("BACKGROUND", (0,1), (-1,-1), colors.HexColor("#FFF5F5")),
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#FCA5A5")),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 7),
("RIGHTPADDING", (0,0), (-1,-1), 7),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(contra_table)
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════
# SECTION 5 – GENERAL DOSAGE GUIDELINES
# ══════════════════════════════════════════════════════════════════
dosage_header = Table([[Paragraph("GENERAL DOSAGE & PROGRESSION GUIDELINES", h1_style)]], colWidths=[W])
dosage_header.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), BLUE_DARK),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("ROUNDEDCORNERS", [4]),
]))
story.append(dosage_header)
story.append(Spacer(1, 2*mm))
dosage_items = [
["Frequency", "Daily exercise — 1–2 sessions/day (acute), 1 session/day (maintenance)"],
["Pain Rule", "Exercise should NOT increase peripheral symptoms. Central/local discomfort (3–4/10) is acceptable."],
["Progression", "Advance to next phase only when current phase is pain-free or symptoms are centralizing."],
["Red Flags", "Stop immediately: new bowel/bladder dysfunction, bilateral neurological symptoms, saddle anaesthesia."],
["Centralization", "Always monitor for McKenzie centralization. If symptoms peripheralize, reduce load or reassess direction."],
["Adjuncts", "Combine with manual therapy, heat/ice, posture education, and ergonomic advice as appropriate."],
["Review", "Reassess every 4–6 sessions. If no improvement in 4 weeks, reconsider diagnosis or regime choice."],
]
dosage_table_data = []
for i, (k, v) in enumerate(dosage_items):
dosage_table_data.append([Paragraph(k, bold_style), Paragraph(v, body_style)])
dosage_table = Table(dosage_table_data, colWidths=[W*0.18, W*0.82])
dosage_style_list = [
("GRID", (0,0), (-1,-1), 0.4, colors.HexColor("#CBD5E1")),
("TOPPADDING", (0,0), (-1,-1), 4),
("BOTTOMPADDING", (0,0), (-1,-1), 4),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("VALIGN", (0,0), (-1,-1), "TOP"),
]
for i in range(len(dosage_items)):
bg = GREY_LIGHT if i % 2 == 0 else WHITE
dosage_style_list.append(("BACKGROUND", (0,i), (-1,i), bg))
dosage_table.setStyle(TableStyle(dosage_style_list))
story.append(dosage_table)
story.append(Spacer(1, 5*mm))
# ══════════════════════════════════════════════════════════════════
# SECTION 6 – CLINICAL PEARL BOX
# ══════════════════════════════════════════════════════════════════
pearl_data = [[
Paragraph(
"CLINICAL PEARL: Always use the McKenzie Directional Preference Assessment before applying any regime. "
"If repeated movements in one direction consistently centralize symptoms — that is your treatment direction, "
"regardless of diagnosis label. Some patients have mixed or non-directional patterns requiring stabilization "
"rather than either regime. Cauda Equina Syndrome is a surgical emergency — refer IMMEDIATELY.",
pearl_style
)
]]
pearl_table = Table(pearl_data, colWidths=[W])
pearl_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colors.HexColor("#FFF8DC")),
("BORDER", (0,0), (-1,-1), 1.5, AMBER),
("BOX", (0,0), (-1,-1), 2, AMBER),
("TOPPADDING", (0,0), (-1,-1), 8),
("BOTTOMPADDING",(0,0), (-1,-1), 8),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("ROUNDEDCORNERS", [6]),
]))
story.append(pearl_table)
story.append(Spacer(1, 4*mm))
# ══════════════════════════════════════════════════════════════════
# FOOTER
# ══════════════════════════════════════════════════════════════════
footer_data = [[
Paragraph(
"Prepared by Orris Physiotherapy Assistant | For clinical reference use | "
"Always defer to individual patient assessment and current clinical guidelines | July 2026",
note_style
)
]]
footer_table = Table(footer_data, colWidths=[W])
footer_table.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), GREY_LIGHT),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING",(0,0), (-1,-1), 5),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("LINEABOVE", (0,0), (-1,0), 0.5, colors.HexColor("#94A3B8")),
]))
story.append(footer_table)
# ── Build ────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF created: {OUTPUT}")
cd /tmp/workspace/physio-pdf && python generate_pdf.py 2>&1
ls -lh /tmp/workspace/physio-pdf/
Flexion vs Extension Regime Quick Reference
PDF Document · PDF