History pattern for Orthopedic cases
| Symptom | Notes |
|---|---|
| Pain | Most common presenting complaint |
| Swelling | Acute vs. chronic; joint vs. periarticular |
| Stiffness | Morning stiffness suggests inflammation |
| Limitation of motion | Active vs. passive |
| Weakness | Nerve, muscle, or tendon origin |
| Fatigue | Systemic/inflammatory component |
| Loss of function | Impacts on ADL, work, sport |
| Dimension | Key Questions |
|---|---|
| Site | Exact location; localized vs. diffuse; joint vs. periarticular |
| Onset | Sudden (trauma, fracture, ACL tear) vs. insidious (OA, overuse) |
| Character | Sharp/stabbing, dull/aching, burning (neuropathic), throbbing |
| Radiation | Referred pain from spine, hip → knee, shoulder → neck |
| Associations | Swelling, locking, clicking, giving way, instability, redness, warmth |
| Time course | Constant vs. intermittent; progressive vs. episodic |
| Exacerbating/Relieving | Rest vs. activity; weight-bearing; specific positions |
| Severity | Pain score (0-10 VAS); functional limitation |
| Historical Finding | Likely Diagnosis |
|---|---|
| Pain after sitting or stair climbing | Patellofemoral syndrome |
| Locking or pain with squatting | Meniscal tear |
| Non-contact injury with pop | ACL tear, patellar dislocation |
| Contact injury with pop | Collateral ligament, meniscal tear, fracture |
| Acute swelling (hemarthrosis) | ACL tear, osteochondral fracture |
| Knee "gives way" | Ligamentous laxity, patellar instability |
| Chronic pain with overhead activity + night pain | Rotator cuff tear |
| "Dead arm" sensation | Axillary nerve stretch, shoulder instability |
| Shoulder dislocates during sleep | Significant glenoid/humeral bone defect |
| Shoulder reduced by patient | Subluxation or generalized ligamentous laxity |
| Back pain + fever + neurological deficit | Spinal epidural abscess (urgent) |
| Flag | Concern |
|---|---|
| Night pain not relieved by rest | Malignancy, infection |
| Fever, rigors, weight loss | Infection, malignancy |
| Bilateral/symmetrical joint involvement | Inflammatory arthritis |
| Morning stiffness >1 hour | Inflammatory arthritis (RA, AS) |
| Neurological symptoms (numbness, weakness, bladder/bowel) | Spinal cord/nerve compression - urgent |
| Redness, warmth, single hot joint | Septic arthritis (emergency) |
| History of cancer | Bone metastasis |
| Prolonged steroid use | Avascular necrosis, osteoporosis |
Create a practical checklist or history-taking form for specific ortho regions (knee, shoulder, spine, hip)
mkdir -p /tmp/workspace/ortho-history && ls /tmp/workspace/ortho-history/
~/ortho-history/build_form.py
from docx import Document
from docx.shared import Pt, Inches, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import copy
doc = Document()
# ── Page margins ──────────────────────────────────────────────────────────────
for section in doc.sections:
section.top_margin = Cm(1.8)
section.bottom_margin = Cm(1.8)
section.left_margin = Cm(2.0)
section.right_margin = Cm(2.0)
# ── Base styles ───────────────────────────────────────────────────────────────
normal = doc.styles["Normal"]
normal.font.name = "Arial"
normal.font.size = Pt(9)
# ── Color palette ─────────────────────────────────────────────────────────────
DARK_BLUE = RGBColor(0x1A, 0x3A, 0x5C) # header fill
MID_BLUE = RGBColor(0x2E, 0x6D, 0xA4) # section headings
LIGHT_BLUE = RGBColor(0xD6, 0xE8, 0xF7) # table header row fill
REGION_COLORS = {
"KNEE": RGBColor(0x1F, 0x5C, 0x99),
"SHOULDER": RGBColor(0x1A, 0x7A, 0x4A),
"SPINE": RGBColor(0x7A, 0x3A, 0x00),
"HIP": RGBColor(0x6A, 0x1A, 0x6A),
}
REGION_FILLS = {
"KNEE": "D0E4F5",
"SHOULDER": "D0F0DC",
"SPINE": "F5E6CC",
"HIP": "F0D0F0",
}
# ── Helpers ───────────────────────────────────────────────────────────────────
def set_cell_bg(cell, hex_color):
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
shd = OxmlElement("w:shd")
shd.set(qn("w:val"), "clear")
shd.set(qn("w:color"), "auto")
shd.set(qn("w:fill"), hex_color)
tcPr.append(shd)
def set_cell_borders(cell, top="single", bottom="single", left="single", right="single", color="AAAAAA", sz="4"):
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
tcBorders = OxmlElement("w:tcBorders")
for side, val in [("top",top),("bottom",bottom),("left",left),("right",right)]:
b = OxmlElement(f"w:{side}")
b.set(qn("w:val"), val)
b.set(qn("w:sz"), sz)
b.set(qn("w:space"), "0")
b.set(qn("w:color"), color)
tcBorders.append(b)
tcPr.append(tcBorders)
def no_space_before(para):
pPr = para._p.get_or_add_pPr()
spacing = OxmlElement("w:spacing")
spacing.set(qn("w:before"), "0")
spacing.set(qn("w:after"), "40")
pPr.append(spacing)
def add_cell_text(cell, text, bold=False, size=9, color=None, italic=False):
para = cell.paragraphs[0]
para.clear()
run = para.add_run(text)
run.font.name = "Arial"
run.font.size = Pt(size)
run.font.bold = bold
run.font.italic = italic
if color:
run.font.color.rgb = color
no_space_before(para)
return para
def add_checkbox_row(table, label, hint="", shade=False):
row = table.add_row()
cells = row.cells
# checkbox cell
add_cell_text(cells[0], "☐", bold=False, size=10)
cells[0].width = Inches(0.25)
# label cell
p = add_cell_text(cells[1], label, bold=False, size=9)
# hint cell
add_cell_text(cells[2], hint, bold=False, size=8, italic=True, color=RGBColor(0x55,0x55,0x55))
if shade:
for c in cells:
set_cell_bg(c, "F7F7F7")
for c in cells:
set_cell_borders(c)
return row
def add_section_header(table, title, fill_hex="2E6DA4"):
row = table.add_row()
cells = row.cells
# merge all 3 columns
cells[0].merge(cells[2])
p = add_cell_text(cells[0], title, bold=True, size=9, color=RGBColor(0xFF,0xFF,0xFF))
set_cell_bg(cells[0], fill_hex)
set_cell_borders(cells[0], color="FFFFFF", sz="6")
return row
def add_free_text_row(table, label, lines=1):
row = table.add_row()
cells = row.cells
cells[0].merge(cells[2])
underline = "___________________________________________" * 2
run_text = f"{label}: {underline[:50]}"
add_cell_text(cells[0], run_text, bold=False, size=9)
set_cell_borders(cells[0])
return row
# ══════════════════════════════════════════════════════════════════════════════
# COVER / TITLE BLOCK
# ══════════════════════════════════════════════════════════════════════════════
# Title table (full-width dark blue header)
title_tbl = doc.add_table(rows=1, cols=1)
title_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER
title_tbl.style = "Table Grid"
tc = title_tbl.rows[0].cells[0]
set_cell_bg(tc, "1A3A5C")
p = tc.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run("ORTHOPAEDIC HISTORY-TAKING FORM")
run.font.name = "Arial"
run.font.size = Pt(16)
run.font.bold = True
run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
pPr = p._p.get_or_add_pPr()
sp = OxmlElement("w:spacing"); sp.set(qn("w:before"),"120"); sp.set(qn("w:after"),"120"); pPr.append(sp)
sub_tbl = doc.add_table(rows=1, cols=1)
sub_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER
sub_tbl.style = "Table Grid"
tc2 = sub_tbl.rows[0].cells[0]
set_cell_bg(tc2, "D6E8F7")
p2 = tc2.paragraphs[0]
p2.alignment = WD_ALIGN_PARAGRAPH.CENTER
run2 = p2.add_run("Regions Covered: Knee | Shoulder | Spine | Hip Ref: Miller's Review of Orthopaedics 9th Ed | Campbell's Operative Orthopaedics 15th Ed 2026")
run2.font.name = "Arial"
run2.font.size = Pt(8)
run2.font.italic = True
run2.font.color.rgb = DARK_BLUE
doc.add_paragraph() # spacer
# ── Patient demographic strip ──────────────────────────────────────────────
demo_tbl = doc.add_table(rows=2, cols=6)
demo_tbl.style = "Table Grid"
demo_headers = ["Patient Name", "Age", "Sex", "Date", "Clinician", "MRN"]
for i, h in enumerate(demo_headers):
set_cell_bg(demo_tbl.rows[0].cells[i], "2E6DA4")
add_cell_text(demo_tbl.rows[0].cells[i], h, bold=True, size=8, color=RGBColor(0xFF,0xFF,0xFF))
add_cell_text(demo_tbl.rows[1].cells[i], "", bold=False, size=9)
set_cell_borders(demo_tbl.rows[1].cells[i])
doc.add_paragraph()
# ══════════════════════════════════════════════════════════════════════════════
# GENERIC SECTION (applies to all regions)
# ══════════════════════════════════════════════════════════════════════════════
def make_generic_section(doc):
h = doc.add_heading("A. COMMON HISTORY (Complete for ALL regions)", level=2)
h.runs[0].font.color.rgb = DARK_BLUE
h.runs[0].font.size = Pt(11)
tbl = doc.add_table(rows=1, cols=3)
tbl.style = "Table Grid"
tbl.columns[0].width = Inches(0.28)
tbl.columns[1].width = Inches(2.6)
tbl.columns[2].width = Inches(3.4)
# column headers
hrow = tbl.rows[0]
for c, txt in zip(hrow.cells, ["", "Item", "Clinical Significance / Hint"]):
set_cell_bg(c, "1A3A5C")
add_cell_text(c, txt, bold=True, size=8, color=RGBColor(0xFF,0xFF,0xFF))
# Chief Complaint
add_section_header(tbl, "1. CHIEF COMPLAINT", "2E6DA4")
items = [
("Pain", "Most common; characterise with SOCRATES"),
("Swelling / effusion", "Acute (<6 h = hemarthrosis) vs. chronic"),
("Stiffness", "Morning stiffness >1 h → inflammatory"),
("Limitation of motion", "Active vs. passive restriction"),
("Weakness", "Nerve / tendon / muscle origin"),
("Instability / giving way", "Ligamentous, patellar, neurological"),
("Deformity", "Progressive vs. fixed"),
("Functional loss", "ADL, work, sport"),
]
for i,(item,hint) in enumerate(items):
add_checkbox_row(tbl, item, hint, shade=(i%2==0))
# SOCRATES Pain
add_section_header(tbl, "2. PAIN CHARACTERISATION (SOCRATES)", "2E6DA4")
pain_items = [
("Site", "Exact location; point with one finger"),
("Onset", "Sudden (trauma) vs. insidious (OA, overuse)"),
("Character", "Sharp / dull / burning (neuropathic) / throbbing"),
("Radiation", "Down leg (radiculopathy) / up arm / referred"),
("Associations", "Locking, clicking, neurological symptoms"),
("Time course", "Constant vs. intermittent; progressive vs. episodic"),
("Exacerbating", "Weight-bearing, activity, position, Valsalva"),
("Relieving", "Rest, NSAIDs, ice, elevation, specific posture"),
("Severity (VAS)", "0 = none → 10 = worst imaginable"),
]
for i,(item,hint) in enumerate(pain_items):
add_checkbox_row(tbl, item, hint, shade=(i%2==0))
# Mechanism
add_section_header(tbl, "3. MECHANISM OF INJURY", "2E6DA4")
mech = [
("Traumatic onset", "Date, time, exact activity"),
("Contact vs. non-contact", "Non-contact + pop → ACL / patellar disloc."),
("Direction of force", "Valgus / varus / hyperextension / rotation"),
("Energy level", "High (MVA, collision) vs. low (ground fall)"),
("Audible / palpable 'pop'", "ACL tear, tendon rupture"),
("Position of limb at injury", "Foot position, arm position"),
("Acute swelling < 2 h", "Haemarthrosis (ACL, fracture, meniscus)"),
("Delayed swelling > 24 h", "Minor meniscal / synovial injury"),
("Insidious / overuse onset", "Repetitive load, gradual progression"),
]
for i,(item,hint) in enumerate(mech):
add_checkbox_row(tbl, item, hint, shade=(i%2==0))
# PMH
add_section_header(tbl, "4. PAST MEDICAL & SURGICAL HISTORY", "2E6DA4")
pmh = [
("Previous injury to same region", "Recurrence, pre-existing instability"),
("Previous surgeries", "Hardware, soft-tissue repairs"),
("Osteoporosis / metabolic bone", "Fragility fractures, bisphosphonate use"),
("Inflammatory arthritis (RA / AS)", "Systemic disease, DMARDs"),
("Diabetes mellitus", "Infection risk, neuropathy, wound healing"),
("Cardiovascular / respiratory", "Surgical / anaesthetic risk"),
("Malignancy (current / past)", "Bone metastasis screen"),
("Coagulation disorder", "Haemarthrosis, anticoagulation"),
]
for i,(item,hint) in enumerate(pmh):
add_checkbox_row(tbl, item, hint, shade=(i%2==0))
# Medications
add_section_header(tbl, "5. MEDICATIONS & ALLERGIES", "2E6DA4")
meds = [
("NSAIDs / analgesics", "Current pain management"),
("Steroids (systemic / local)", "Bone quality, AVN risk, wound healing"),
("Anticoagulants", "Warfarin / DOAC — surgical timing"),
("DMARDs / biologics", "Peri-op management, infection risk"),
("Bisphosphonates", "Atypical femoral fracture risk"),
("Drug allergies", "Latex, antibiotics, metal implants"),
]
for i,(item,hint) in enumerate(meds):
add_checkbox_row(tbl, item, hint, shade=(i%2==0))
# Social
add_section_header(tbl, "6. SOCIAL HISTORY", "2E6DA4")
social = [
("Occupation", "Manual vs. sedentary; return-to-work goals"),
("Sporting activity", "Level, specific sport, competitive vs. recreational"),
("Smoking", "Impairs bone healing, fusion rates"),
("Alcohol use", "AVN risk (hip); fall risk"),
("IV drug use", "Osteomyelitis, septic arthritis risk"),
("Living situation", "Stairs, independence — post-op planning"),
("Dominant side", "Upper limb especially"),
("Walking aids used", "Cane, crutch, frame"),
]
for i,(item,hint) in enumerate(social):
add_checkbox_row(tbl, item, hint, shade=(i%2==0))
# Family
add_section_header(tbl, "7. FAMILY HISTORY", "2E6DA4")
fam = [
("Inflammatory arthritis", "RA, AS, psoriatic"),
("Malignancy", "Bone tumour, metastatic disease"),
("Connective tissue disorders", "Marfan, Ehlers-Danlos → instability"),
]
for i,(item,hint) in enumerate(fam):
add_checkbox_row(tbl, item, hint, shade=(i%2==0))
doc.add_paragraph()
make_generic_section(doc)
# ══════════════════════════════════════════════════════════════════════════════
# REGIONAL SECTIONS
# ══════════════════════════════════════════════════════════════════════════════
def make_region_table(doc, region_title, fill_hex, items_dict):
"""
items_dict: ordered list of (section_title, [(item, hint), ...])
"""
h = doc.add_heading(f"B{list(REGION_FILLS.keys()).index(region_title.split()[0].upper())+1}. {region_title}", level=2)
h.runs[0].font.color.rgb = RGBColor(int(fill_hex[0:2],16), int(fill_hex[2:4],16), int(fill_hex[4:6],16))
h.runs[0].font.size = Pt(11)
tbl = doc.add_table(rows=1, cols=3)
tbl.style = "Table Grid"
tbl.columns[0].width = Inches(0.28)
tbl.columns[1].width = Inches(2.6)
tbl.columns[2].width = Inches(3.4)
hrow = tbl.rows[0]
for c, txt in zip(hrow.cells, ["", "Region-specific Item", "Clinical Significance / Hint"]):
set_cell_bg(c, fill_hex)
add_cell_text(c, txt, bold=True, size=8, color=RGBColor(0xFF,0xFF,0xFF))
for section_title, rows in items_dict:
add_section_header(tbl, section_title, fill_hex)
for i,(item,hint) in enumerate(rows):
add_checkbox_row(tbl, item, hint, shade=(i%2==0))
doc.add_paragraph()
# ─── KNEE ──────────────────────────────────────────────────────────────────
knee_data = [
("KNEE — SYMPTOM CHARACTERISATION", [
("Pain location", "Anterior=patellofem; medial=MCL/medial menisc; lateral=LCL/IT band; posterior=PCL/Baker's cyst"),
("Pain after sitting / stair climbing", "Patellofemoral syndrome (theatre sign)"),
("Locking (true) — cannot extend", "Meniscal bucket-handle tear; loose body"),
("Pseudo-locking — pain-limited", "Hamstring spasm, patellofemoral"),
("Pain with squatting / deep flexion", "Meniscal tear, patellofemoral"),
("Giving way", "ACL insufficiency, patellar instability"),
("Acute swelling < 2 h (haemarthrosis)", "ACL tear, osteochondral fracture, periph. meniscal tear"),
("Effusion > 24 h (synovial fluid)", "Meniscal, synovitis, reactive"),
("Clicking / snapping", "Discoid meniscus, IT band syndrome, loose body"),
("Night pain at rest", "Infection, tumour — red flag"),
]),
("KNEE — MECHANISM OF INJURY (from Miller's Review of Orthopaedics)", [
("Non-contact + pop + acute swelling", "ACL tear until proven otherwise"),
("Non-contact + pop (no major swelling)", "Patellar dislocation"),
("Contact + pop — valgus force", "MCL ± ACL ± medial meniscus (O'Donoghue triad)"),
("Contact + pop — varus force", "LCL / posterolateral corner (PLC)"),
("Hyperextension + varus + tibial ext. rot", "PLC injury"),
("Anterior tibial force, foot plantar-flexed","PCL injury"),
("Dashboard / direct posterior tibia blow", "PCL or patellar injury"),
("Anterior force, foot dorsiflexed", "Patellar injury"),
("Twisting on planted foot", "Meniscal tear ± ACL"),
]),
("KNEE — FUNCTIONAL & ACTIVITY ASSESSMENT", [
("Walking distance before pain", "OA / claudication screen"),
("Stairs (ascending vs. descending worse)", "Descending worse → patellofemoral"),
("Sport / activity level (Tegner score)", "Document pre-injury vs. current"),
("Use of brace or walking aid", "Degree of functional compromise"),
("Previous episodes of same injury", "Chronic instability, re-tear"),
]),
("KNEE — RED FLAGS", [
("Fever + hot swollen joint", "EMERGENCY: septic arthritis"),
("Night pain not relieved by rest", "Malignancy / infection"),
("History of malignancy", "Bone metastasis / primary bone tumour"),
("Rapid unexplained swelling", "Haemarthrosis, fracture"),
("Neurovascular symptoms (numbness/cold)", "Popliteal artery / nerve injury — urgent"),
]),
]
make_region_table(doc, "KNEE – Specific History Checklist", REGION_FILLS["KNEE"].replace("0x",""), knee_data)
# ─── SHOULDER ──────────────────────────────────────────────────────────────
shoulder_data = [
("SHOULDER — SYMPTOM CHARACTERISATION", [
("Age group", "Young: instability, AC injury; Older: rotator cuff, arthritis, fracture"),
("Pain location", "Anterior=instability; lateral deltoid=impingement/RC; posterior=posterior instability"),
("Pain with overhead activity", "Rotator cuff impingement / tear"),
("Night pain (lying on affected side)", "Strongly suggests rotator cuff tear"),
("Feeling of shoulder 'popping out'", "Glenohumeral instability / dislocation"),
("'Dead arm' sensation", "Axillary nerve stretch, instability"),
("Reduced range of motion — global", "Frozen shoulder (adhesive capsulitis)"),
("Weakness with elevation", "Rotator cuff tear, deltoid nerve injury"),
("Clicking / clunking", "Labral tear, AC joint, biceps tendon"),
("AC joint tenderness / step deformity", "AC joint injury"),
]),
("SHOULDER — MECHANISM (from Campbell's Operative Orthopaedics)", [
("Direct blow to shoulder", "AC joint separation, clavicle fracture"),
("Abducted + externally rotated arm", "Anterior glenohumeral instability / dislocation"),
("Fall on outstretched hand (FOOSH)", "Proximal humeral fracture, clavicle, SLAP"),
("Overhead throwing sports", "SLAP tear, posterior instability, internal impingement"),
("High-energy collision sport", "Bony Bankart / Hill-Sachs lesion (requires surgical treatment)"),
("Dislocation during sleep / overhead", "Significant glenoid/humeral bone defect (Campbell's)"),
("Reduction by patient themselves", "Subluxation or generalised ligamentous laxity"),
]),
("SHOULDER — INSTABILITY SPECIFIC (Campbell's Operative Orthopaedics)", [
("Direction of instability", "Anterior / posterior / multidirectional"),
("Amount of initial trauma", "High energy → bone defect"),
("Ease of reduction", "Self-reducing → consider laxity"),
("Recurrence with minimal trauma / midrange","Associated bony lesion"),
("Neurological symptoms (numbness/tingling)","Axillary nerve, brachial plexus"),
("Voluntary vs. involuntary subluxation", "Voluntary → psychological component; avoid surgery"),
("Position causing instability", "Abduction + ER → anterior; adduction + IR → posterior"),
]),
("SHOULDER — FUNCTIONAL ASSESSMENT", [
("Overhead activity demands", "Work, sport, throwing"),
("Dominant arm involved", "Critical for treatment decisions"),
("Use of sling / splint", "Previous management"),
("Prior physiotherapy / injection", "Response guides management"),
]),
("SHOULDER — RED FLAGS", [
("Fever + warm swollen joint", "Septic arthritis — emergency"),
("Night pain at rest + weight loss", "Malignancy"),
("Numbness / weakness radiating to hand", "Cervical radiculopathy — examine neck too"),
("Chest pain with shoulder pain", "Cardiac referred pain — exclude"),
]),
]
make_region_table(doc, "SHOULDER – Specific History Checklist", REGION_FILLS["SHOULDER"].replace("0x",""), shoulder_data)
# ─── SPINE ──────────────────────────────────────────────────────────────────
spine_data = [
("SPINE — SYMPTOM CHARACTERISATION", [
("Level / region of pain", "Cervical / thoracic / lumbar / sacral"),
("Axial (local) pain only", "Disc, facet joint, muscular — non-radicular"),
("Radiculopathy — dermatomal radiation", "Disc herniation, foraminal stenosis"),
("Claudication (worse walking, better sitting)","Lumbar spinal stenosis"),
("Myelopathy — bilateral weakness / gait", "Cervical / thoracic cord compression — URGENT"),
("Bladder / bowel dysfunction", "Cauda equina syndrome — EMERGENCY"),
("Morning stiffness > 1 hour", "Inflammatory spondyloarthropathy (AS)"),
("Pain aggravated by Valsalva / cough", "Disc herniation / intrathecal pathology"),
("Pain worsening with extension", "Facet joint / spondylolisthesis"),
("Pain worsening with flexion", "Disc-mediated / vertebral fracture"),
]),
("SPINE — MECHANISM / ONSET", [
("Trauma — compression / flexion", "Vertebral fracture — osteoporosis screen"),
("Insidious onset < 40 yrs + sacroiliac", "Ankylosing spondylitis"),
("Post-operative / procedural", "Haematoma, infection, hardware failure"),
("Lifting / twisting injury", "Disc herniation, muscle / ligament strain"),
]),
("SPINE — NEUROLOGICAL SCREEN", [
("Numbness / paraesthesia — distribution", "Map dermatome: L4=medial leg; L5=dorsum foot; S1=lateral foot"),
("Motor weakness — distribution", "L4=ext. hallucis longus; L5=ext. hallucis longus; S1=plantarflexion"),
("Urinary retention or incontinence", "Cauda equina — EMERGENCY"),
("Faecal incontinence / perineal numbness", "Cauda equina — EMERGENCY"),
("Bilateral lower limb symptoms", "Central disc, cord compression"),
("Upper motor neurone signs (spasticity)", "Cervical myelopathy"),
]),
("SPINE — INFECTION / TUMOUR SCREEN (Miller's Review of Orthopaedics)", [
("Fever, rigors, night sweats", "Spinal infection, discitis, epidural abscess"),
("IV drug use / immunocompromised", "Osteomyelitis / epidural abscess risk factors"),
("Diabetes mellitus", "Increased infection risk"),
("History of TB / exposure", "Pott's disease (spinal TB)"),
("History of malignancy", "Vertebral metastasis"),
("Unintentional weight loss", "Malignancy / infection"),
("Elevated ESR / CRP (if known)", "Raised in infection > OA"),
("Back pain + fever + neurological deficit", "Epidural abscess — EMERGENCY surgical evaluation"),
]),
("SPINE — FUNCTIONAL ASSESSMENT", [
("Walking distance / tolerance", "Claudication vs. arterial — cycling test"),
("Posture-dependent relief", "Flexed posture relief → stenosis"),
("Work / ADL limitation", "Chronic disability assessment"),
("Previous physiotherapy / injections", "Response to treatment"),
("Prior spine surgery", "Hardware, adjacent level disease"),
]),
]
make_region_table(doc, "SPINE – Specific History Checklist", REGION_FILLS["SPINE"].replace("0x",""), spine_data)
# ─── HIP ──────────────────────────────────────────────────────────────────
hip_data = [
("HIP — SYMPTOM CHARACTERISATION", [
("Pain location — groin", "Intrinsic hip pathology (OA, AVN, FAI, labral)"),
("Pain location — lateral (trochanteric)", "Greater trochanteric pain syndrome / bursitis"),
("Pain location — buttock", "Referred from spine / SIJ; piriformis"),
("Pain radiating to knee", "Hip pathology can refer to knee — examine both"),
("Limp (antalgic vs. Trendelenburg)", "Antalgic = pain; Trendelenburg = abductor weakness"),
("Stiffness — loss of internal rotation", "Earliest sign of hip OA"),
("Clicking / snapping sensation", "Snapping hip (iliopsoas / IT band / labral)"),
("Pain with prolonged sitting", "Femoroacetabular impingement (FAI)"),
("Groin pain with activity in young adult", "FAI, labral tear, stress fracture"),
("Start-up pain (first steps)", "OA, inflammatory arthritis"),
]),
("HIP — MECHANISM / ONSET", [
("Trauma — fall on greater trochanter", "Femoral neck or intertrochanteric fracture"),
("Minimal trauma + osteoporosis", "Insufficiency / pathological fracture"),
("Insidious — young active person", "FAI, labral tear, stress fracture"),
("Insidious — older adult", "Osteoarthritis, inflammatory arthritis"),
("Sudden pain + inability to weight-bear", "Fracture, acute AVN, acute labral tear"),
]),
("HIP — AVN RISK FACTOR SCREEN", [
("Systemic corticosteroid use", "Most common non-traumatic cause of AVN"),
("Alcohol excess (> 400 mL ethanol/wk)", "Fat emboli theory"),
("Previous hip dislocation / trauma", "Disruption of blood supply"),
("Sickle cell disease", "Sickling in femoral head vasculature"),
("Radiation therapy to hip region", "Radiation-induced vasculopathy"),
("Dysbaric conditions (diving)", "Gas emboli"),
("Systemic lupus erythematosus", "Vasculitis + steroid treatment"),
("Gaucher's disease", "Storage disorder → bone infarct"),
]),
("HIP — FUNCTIONAL ASSESSMENT", [
("Walking distance", "Quantify limitation"),
("Shoe / sock / toenail care", "Loss of internal rotation impairs ADL"),
("Stair climbing", "Grading function"),
("Use of walking aid", "Stick (contralateral hand) = hip problem"),
("Sexual activity impairment", "Important to document; impacts QoL"),
("Prior physiotherapy / injections", "Response to treatment"),
("Expectations for treatment", "Especially pre-operative planning"),
]),
("HIP — RED FLAGS", [
("Acute groin pain + fever + systemically unwell", "Septic arthritis — EMERGENCY: drain same day"),
("Hip pain + history of malignancy", "Metastatic disease / pathological fracture"),
("Sudden inability to weight-bear after trivial fall", "Fracture (osteoporosis, malignancy)"),
("Night pain at rest + weight loss", "Bone tumour / metastasis"),
("Pain in child + fever + limp", "Septic arthritis / Perthes / SUFE"),
]),
]
make_region_table(doc, "HIP – Specific History Checklist", REGION_FILLS["HIP"].replace("0x",""), hip_data)
# ══════════════════════════════════════════════════════════════════════════════
# SUMMARY / CLINICAL IMPRESSION
# ══════════════════════════════════════════════════════════════════════════════
doc.add_heading("C. CLINICAL IMPRESSION & NEXT STEPS", level=2).runs[0].font.color.rgb = DARK_BLUE
summary_tbl = doc.add_table(rows=7, cols=2)
summary_tbl.style = "Table Grid"
labels = [
"Working Diagnosis",
"Differential Diagnoses",
"Red Flags Identified",
"Investigations Requested",
"Referrals",
"Management Plan",
"Follow-up",
]
for i, label in enumerate(labels):
set_cell_bg(summary_tbl.rows[i].cells[0], "2E6DA4")
add_cell_text(summary_tbl.rows[i].cells[0], label, bold=True, size=9, color=RGBColor(0xFF,0xFF,0xFF))
summary_tbl.rows[i].cells[0].width = Inches(1.8)
add_cell_text(summary_tbl.rows[i].cells[1], "", size=9)
set_cell_borders(summary_tbl.rows[i].cells[1])
# Footer
doc.add_paragraph()
footer_p = doc.add_paragraph("Sources: Miller's Review of Orthopaedics 9th Ed | Campbell's Operative Orthopaedics 15th Ed 2026 | Firestein & Kelley's Textbook of Rheumatology | Rockwood & Green's Fractures in Adults 10th Ed 2025")
footer_p.runs[0].font.size = Pt(7)
footer_p.runs[0].font.italic = True
footer_p.runs[0].font.color.rgb = RGBColor(0x88,0x88,0x88)
# ── Save ──────────────────────────────────────────────────────────────────────
out_path = "/tmp/workspace/ortho-history/Orthopaedic_History_Form.docx"
doc.save(out_path)
print(f"Saved: {out_path}")
cd /tmp/workspace/ortho-history && python3 build_form.py 2>&1
python3 -c " from docx import Document doc = Document('/tmp/workspace/ortho-history/Orthopaedic_History_Form.docx') for p in doc.paragraphs[:30]: if p.text.strip(): print(repr(p.text[:80])) print('---TABLES---') print('Total tables:', len(doc.tables)) for i,t in enumerate(doc.tables): print(f'Table {i}: {len(t.rows)} rows x {len(t.columns)} cols') "
Orthopaedic History Form
Word Document · DOCX
| Section | Contents |
|---|---|
| Patient Demographics | Name, Age, Sex, Date, Clinician, MRN |
| A. Common History | Chief complaint (7 cardinal symptoms), SOCRATES pain, Mechanism of injury, PMH/PSH, Medications & allergies, Social history, Family history |
| B1. Knee | Symptom characterisation, Mechanism (Table from Miller's), Functional assessment, Red flags |
| B2. Shoulder | Symptom characterisation, Mechanism (Campbell's), Instability-specific questions, Functional assessment, Red flags |
| B3. Spine | Symptom characterisation, Mechanism, Neurological screen (dermatome map), Infection/tumour screen (Miller's), Functional assessment |
| B4. Hip | Symptom characterisation, Mechanism, Full AVN risk factor screen, Functional assessment, Red flags |
| C. Clinical Impression | Working diagnosis, differentials, red flags noted, investigations, referrals, management plan, follow-up |