Motor Assessment of the Lower Limbs FORMAT

Reading File
The user is asking about "Motor Assessment of the Lower Limbs FORMAT" - this appears to be a clinical examination/assessment topic. Let me search the medical library for relevant content.
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Finding Sources
Reading File
Reading File
Searching Images

lower limb motor examination neurological assessment diagram

Clinical photographs demonstrating the assessment of motor function and lower limb power in a patient one year post-spinal surgery. Image A shows the patient performing a supine bridge exercise, with the hips and torso elevated off the examination table. The posture demonstrates antigravity muscle strength in the hip extensors, supported by bent knees and feet flat on the table. Image B depicts the patient lying supine while performing a straight-leg raise or maintaining leg elevation against gravity, demonstrating power in the hip flexors and knee extensors. The images serve as a clinical follow-up to document neurological recovery after surgical decompression for a spinal epidural mass. The clinical context indicates improvement in hip and ankle motor power (graded 4/5) and knee extension (4/5), sufficient for ambulation with assistive devices despite residual ankle dorsiflexion weakness and spasticity.

Clinical photographs demonstrating the assessment of motor function and lower limb power in a patient one year post-spinal surgery. Image A shows the patient performing a supine bridge exercise, with the hips and torso elevated off the examination table. The posture demonstrates antigravity muscle strength in the hip extensors, supported by bent knees and feet flat on the table. Image B depicts the patient lying supine while performing a straight-leg raise or maintaining leg elevation against gravity, demonstrating power in the hip flexors and knee extensors. The images serve as a clinical follow-up to document neurological recovery after surgical decompression for a spinal epidural mass. The clinical context indicates improvement in hip and ankle motor power (graded 4/5) and knee extension (4/5), sufficient for ambulation with assistive devices despite residual ankle dorsiflexion weakness and spasticity.

A clinical photograph demonstrating a physical examination technique for neurological assessment. The patient is positioned supine on an examination table with the lower extremities extended. An examiner, wearing a white clinical coat, is shown performing percussion using a Queen Square-style reflex hammer. The examiner's right hand holds the long, flexible handle of the hammer, poised to strike the medial epicondyle of the femur. The examiner's left hand is placed on the patient's distal thigh to stabilize the limb and palpate the anatomical landmarks. This procedural image illustrates the elicitation of a deep tendon reflex or periosteal reflex, specifically focusing on the medial aspect of the knee. The visual highlights the correct patient positioning, instrument grip, and anatomical targeting required for a standard neurological motor system evaluation.

A clinical photograph demonstrating a physical examination technique for neurological assessment. The patient is positioned supine on an examination table with the lower extremities extended. An examiner, wearing a white clinical coat, is shown performing percussion using a Queen Square-style reflex hammer. The examiner's right hand holds the long, flexible handle of the hammer, poised to strike the medial epicondyle of the femur. The examiner's left hand is placed on the patient's distal thigh to stabilize the limb and palpate the anatomical landmarks. This procedural image illustrates the elicitation of a deep tendon reflex or periosteal reflex, specifically focusing on the medial aspect of the knee. The visual highlights the correct patient positioning, instrument grip, and anatomical targeting required for a standard neurological motor system evaluation.

Educational multi-panel illustration detailing neurophysiological assessment techniques for transcranial direct current stimulation (tDCS) of the lower limb motor cortex. Panel A: Coronal brain diagram with red shading identifying the targeted primary motor cortex (M1) area for anodal tDCS. Panel B: Anatomical illustration of a human lower limb showing electrode placement for surface electromyography (sEMG) over the tibialis anterior muscle. Panel C: Comparison chart of motor-evoked potential (MEP) traces recorded via transcranial magnetic stimulation (TMS). The traces illustrate a significant increase in peak-to-peak amplitude from 'Pre-tDCS' to 'Post-tDCS,' signifying enhanced corticospinal excitability. Panel D: Sagittal neuroimaging slice (fMRI) displaying a localized blood oxygen level-dependent (BOLD) signal in the paracentral lobule, corresponding to neural activation in the lower limb motor representation. This composite graphic demonstrates how electrophysiological (MEP) and hemodynamic (fMRI) markers are used to quantify brain response to neuromodulation therapy in clinical research and rehabilitation, such as post-stroke recovery.

Educational multi-panel illustration detailing neurophysiological assessment techniques for transcranial direct current stimulation (tDCS) of the lower limb motor cortex. Panel A: Coronal brain diagram with red shading identifying the targeted primary motor cortex (M1) area for anodal tDCS. Panel B: Anatomical illustration of a human lower limb showing electrode placement for surface electromyography (sEMG) over the tibialis anterior muscle. Panel C: Comparison chart of motor-evoked potential (MEP) traces recorded via transcranial magnetic stimulation (TMS). The traces illustrate a significant increase in peak-to-peak amplitude from 'Pre-tDCS' to 'Post-tDCS,' signifying enhanced corticospinal excitability. Panel D: Sagittal neuroimaging slice (fMRI) displaying a localized blood oxygen level-dependent (BOLD) signal in the paracentral lobule, corresponding to neural activation in the lower limb motor representation. This composite graphic demonstrates how electrophysiological (MEP) and hemodynamic (fMRI) markers are used to quantify brain response to neuromodulation therapy in clinical research and rehabilitation, such as post-stroke recovery.

This clinical photograph consists of two panels demonstrating a bedside neurological assessment for limb apraxia, specifically ideomotor apraxia. The images show a clinical interaction between an examiner (left) and a patient (right). In the first panel, the examiner presents a meaningless gesture with the index and middle fingers extended while other fingers are flexed; the patient attempts to mirror this configuration. In the second panel, the examiner presents a different hand pose with the index and pinky fingers extended and middle fingers flexed, forming a 'V' shape, which the patient again attempts to copy. This test evaluates left parietal lobe function, as deficits in copying these meaningless hand gestures can indicate ideomotor limb apraxia. Such findings are clinically relevant in the diagnosis of left parietal stroke or neurodegenerative conditions like Alzheimer's disease, dementia with Lewy bodies, or corticobasal syndrome. The setting is a clinical examination room, focusing on the motor execution and visuospatial imitation capabilities of the patient.

This clinical photograph consists of two panels demonstrating a bedside neurological assessment for limb apraxia, specifically ideomotor apraxia. The images show a clinical interaction between an examiner (left) and a patient (right). In the first panel, the examiner presents a meaningless gesture with the index and middle fingers extended while other fingers are flexed; the patient attempts to mirror this configuration. In the second panel, the examiner presents a different hand pose with the index and pinky fingers extended and middle fingers flexed, forming a 'V' shape, which the patient again attempts to copy. This test evaluates left parietal lobe function, as deficits in copying these meaningless hand gestures can indicate ideomotor limb apraxia. Such findings are clinically relevant in the diagnosis of left parietal stroke or neurodegenerative conditions like Alzheimer's disease, dementia with Lewy bodies, or corticobasal syndrome. The setting is a clinical examination room, focusing on the motor execution and visuospatial imitation capabilities of the patient.

Now I have comprehensive information to compile a detailed, well-structured answer on Motor Assessment of the Lower Limbs FORMAT.

Motor Assessment of the Lower Limbs - FORMAT

The motor assessment of the lower limbs follows a structured sequence: Inspection → Tone → Power → Reflexes → Coordination → Gait. This order ensures nothing is missed and mirrors standard neurological examination formats.

1. INSPECTION (Look Before You Touch)

Inspect the limbs with the patient supine, fully exposed from waist down, in good lighting.
FeatureWhat to Look ForSignificance
Muscle bulkWasting (atrophy), hypertrophyLMN lesion, disuse
FasciculationsFine, spontaneous twitching under skinLMN / anterior horn cell disease
Involuntary movementsTremor at rest, posture, or actionParkinson's (resting), cerebellar (intention)
PostureLimb deformity, foot drop, scissoringUMN, pyramidal lesion
Skin/trophic changesHair loss, skin texture, ulcersChronic denervation or vascular
AsymmetryLimb length, bulk, positionCongenital or acquired lesion
Scars/deformitiesSurgical, traumaticRelevant history
In children, measure limb length - asymmetry suggests congenital spinal cord/brain malformation. Inspect the lumbar spine for scoliosis or hairy patches (spinal dysraphism).
  • Harrison's Principles of Internal Medicine 22E
  • Bradley and Daroff's Neurology in Clinical Practice

2. TONE

Position: Patient supine, relaxed. Distract the patient to minimize voluntary resistance.
Technique for lower limbs:
  • Place both hands behind the patient's knees
  • Rapidly lift the knees off the bed
  • Observe the heel:
    • Normal: Heel drags along the table for a variable distance before lifting
    • Increased tone (spasticity): Heel lifts immediately off the surface
  • Perform passive flexion/extension at hip, knee, and ankle - assess for resistance
Grading Tone:
FindingDescriptionLesion
Hypotonia / FlaccidityReduced/absent resistance, floppy limbLMN, peripheral nerve, cerebellar
SpasticityVelocity-dependent resistance (clasp-knife)UMN / corticospinal tract
RigidityUniform resistance all directions (lead-pipe)Extrapyramidal / basal ganglia
Cogwheel rigidityRatchet-like jerky interruptions on passive movementParkinsonism
Paratonia (Gegenhalten)Fluctuating variable resistanceFrontal lobe pathways
Key test: Rapidly raise the knees - immediate heel lift = increased tone (spasticity).
  • Harrison's Principles of Internal Medicine 22E, p. 3424

3. POWER (Muscle Strength)

Test proximal → distal, comparing sides. Ask the patient to exert maximal effort against your resistance. Isolate individual muscles or groups as much as possible.

MRC Power Grading Scale

GradeDescription
0No movement (complete paralysis)
1Flicker/trace of contraction - no joint movement
2Movement possible with gravity eliminated (horizontal plane)
3Movement against gravity but not against resistance
4-Movement against mild resistance
4Movement against moderate resistance
4+Movement against strong resistance
5Full power (normal)

Lower Limb Muscle Groups to Test

MovementMusclesRoot Level
Hip flexionIliopsoasL1, L2, L3
Hip extensionGluteus maximusL4, L5, S1
Hip abductionGluteus medius/minimusL4, L5, S1
Hip adductionAdductorsL2, L3, L4
Knee extensionQuadriceps femorisL3, L4
Knee flexionHamstringsL5, S1, S2
Ankle dorsiflexionTibialis anteriorL4, L5
Ankle plantarflexionGastrocnemius/SoleusS1, S2
Ankle eversionPeroneus longus/brevisL5, S1
Ankle inversionTibialis posteriorL4, L5
Great toe extensionExtensor hallucis longusL5
Toe flexionFlexor digitorumS1, S2
Bare minimum screening: Check toe extensor strength (EHL) for lower limb weakness.
  • Harrison's Principles of Internal Medicine 22E
Practical tip: In suspected foot drop, determine if weakness of ankle dorsiflexors is due to spasticity (UMN) vs. anterior horn cell disease, peripheral neuropathy, peroneal nerve compression, or an L5 root lesion - each has a different pattern.

4. REFLEXES

Patient relaxed; limb positioned midway between full contraction and extension. Test both sides sequentially. Use the Jendrassik maneuver (hook fingers and pull apart) to reinforce lower limb reflexes if absent.

Deep Tendon Reflexes (Lower Limb)

ReflexRootTechniqueNormal Response
Knee jerk (patellar)L3, L4Patient sitting with one knee crossed over the other, or flexed knee resting on examiner's hand; brisk tap on ligamentum patellaeExtension (jerk) of leg - quadriceps contraction
Ankle jerk (Achilles)S1, S2Foot slightly dorsiflexed (Achilles on stretch); tap on tendo AchillesPlantar flexion of foot

Reflex Grading Scale

GradeDescription
0Absent
1Present but diminished
2Normoactive (normal)
3Increased (brisk)
4Clonus present

Clonus

TypeTechniquePathological Level
Ankle clonus (S1, S2)Knee slightly flexed, support leg; sudden dorsiflexion of foot and maintain pressureRhythmic oscillating plantar/dorsiflexion = pyramidal lesion
Patellar clonus (L2, 3, 4)Knee straight; suddenly push patella downward from upper border and maintainClonic movement of patella = pyramidal lesion
Clonus is pathognomonic of pyramidal system lesions (UMN signs).
  • S Das A Manual on Clinical Surgery 13th Edition

Cutaneous / Superficial Reflexes (Lower Limb)

ReflexRootTechniqueNormalAbnormal
Plantar reflexS1Stroke lateral sole from heel to ball of footToe flexion (downgoing)Babinski sign = great toe extension + fanning (UMN lesion or infant)
CremastericL1, L2Stroke inner upper thighIpsilateral testis elevationAbsent = UMN or L1/L2 lesion
Anal reflexS2, S3, S4Scratch perianal skinAnal sphincter contractionAbsent = sacral lesion
In pyramidal lesions: deep reflexes exaggerated (may elicit clonus); superficial reflexes diminished/absent (abdominal reflexes lost, plantar = Babinski). Note: Reliability of Babinski sign alone is limited; tone + strength + stretch reflexes + coordination are more useful collectively.
  • Harrison's Principles of Internal Medicine 22E, S Das Manual on Clinical Surgery
Lower limb motor assessment - reflex examination

5. COORDINATION (Cerebellar Function)

TestTechniqueAbnormal Finding
Heel-shin testPatient supine; place heel on opposite knee and slide it down shinAtaxia/tremor = cerebellar or proprioceptive deficit
Foot tappingRapid regular tapping of footInability = bradykinesia (Parkinson's)
Foot circle drawingDraw a circle with footDifficulty = dyspraxia (corticobasal)
Romberg's testStand feet together, eyes open then closedPositive (falls with eyes closed) = sensory/proprioceptive ataxia (NOT cerebellar)
A patient with cerebellar vermis lesion may perform the heel-shin test normally when supine but be ataxic when walking - gait examination is essential.
  • Bradley and Daroff's Neurology in Clinical Practice

6. GAIT (Always Examine Last)

Observe the patient walk before examining in bed. Gait integrates motor, cerebellar, sensory, and basal ganglia function.
Gait PatternFeaturesLesion
Hemiplegic (circumduction)Stiff leg swings outwardContralateral UMN (stroke)
Spastic paraparesis (scissor)Legs cross, stiff, toe-draggingBilateral UMN (spinal cord)
High-stepping (steppage)Foot drop, exaggerated hip flexionL4/L5, peroneal nerve, LMN
WaddlingBilateral hip abductor weaknessProximal myopathy, hip dysplasia
Ataxic (broad-based)Irregular, staggering, wide baseCerebellar
ParkinsonianShuffling, small steps, en bloc turns, festinationBasal ganglia
AntalgicShort stance phase on painful limbMusculoskeletal
TrendelenburgContralateral pelvis dropsIpsilateral hip abductor weakness

SUMMARY TABLE: UMN vs. LMN Findings in Lower Limbs

FeatureUMN LesionLMN Lesion
BulkNormal or mild disuse atrophyMarked wasting
FasciculationsAbsentPresent
ToneIncreased (spasticity)Decreased (flaccidity)
PowerReducedReduced
Deep reflexesExaggerated (hyperreflexia)Diminished or absent
Plantar reflexExtensor (Babinski +ve)Flexor (normal) or absent
ClonusMay be presentAbsent
Superficial reflexesAbsent (abdominal, cremasteric)Present or absent

QUICK EXAMINATION FORMAT (Mnemonics)

"I TRAP C-G" for lower limb motor:
  • I - Inspection (bulk, fasciculations, posture)
  • T - Tone (passive movements, heel-drop test)
  • R - Reflexes (knee jerk L3/4, ankle jerk S1/2, plantar S1)
  • A - Activity/Power (MRC 0-5, proximal to distal)
  • P - Plantar response (Babinski)
  • C - Coordination (heel-shin, foot tap)
  • G - Gait

Sources: Harrison's Principles of Internal Medicine 22E (2025); Bradley and Daroff's Neurology in Clinical Practice; S Das A Manual on Clinical Surgery 13th Edition; Sabiston Textbook of Surgery

i need a well-designed format for above examination that i can use

Asking for Preferences

What format would you like? · How will you use this?

Reading File
Running Command

ls -la /tmp/workspace/ 2>/dev/null || echo "workspace not found"

Running Command

mkdir -p /tmp/workspace/motor-assessment && python3 << 'PYEOF' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm, cm from reportlab.platypus import (SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, HRFlowable, KeepTogether) from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT from reportlab.platypus import Flowable # ── colours ────────────────────────────────────────────────────────────────── DARK_BLUE = colors.HexColor('#1A3A5C') MID_BLUE = colors.HexColor('#2E6DA4') LIGHT_BLUE = colors.HexColor('#D6E8F7') PALE_BLUE = colors.HexColor('#EEF5FB') ACCENT = colors.HexColor('#E8821A') LIGHT_GREY = colors.HexColor('#F2F4F7') MID_GREY = colors.HexColor('#CCCCCC') WHITE = colors.white BLACK = colors.black W, H = A4 # 595 x 842 pt doc = SimpleDocTemplate( '/tmp/workspace/motor-assessment/Lower_Limb_Motor_Assessment.pdf', pagesize=A4, leftMargin=15*mm, rightMargin=15*mm, topMargin=15*mm, bottomMargin=15*mm, ) styles = getSampleStyleSheet() # ── custom paragraph styles ────────────────────────────────────────────────── def S(name, parent='Normal', **kw): return ParagraphStyle(name, parent=styles[parent], **kw) sTitle = S('sTitle', fontSize=18, textColor=WHITE, alignment=TA_CENTER, fontName='Helvetica-Bold', spaceAfter=0) sSub = S('sSub', fontSize=10, textColor=colors.HexColor('#BFD9F2'), alignment=TA_CENTER, fontName='Helvetica', spaceAfter=0) sSecHdr = S('sSecHdr', fontSize=9, textColor=WHITE, fontName='Helvetica-Bold', alignment=TA_LEFT, spaceAfter=0, leading=12) sBody = S('sBody', fontSize=7.5, leading=11, textColor=BLACK) sBold = S('sBold', fontSize=7.5, fontName='Helvetica-Bold', textColor=BLACK, leading=11) sSmall = S('sSmall', fontSize=7, textColor=colors.HexColor('#555555'), leading=10) sLabel = S('sLabel', fontSize=7.5, fontName='Helvetica-Bold', textColor=DARK_BLUE, leading=11) sNote = S('sNote', fontSize=7, fontName='Helvetica-Oblique', textColor=colors.HexColor('#666666'), leading=10) sCenter = S('sCenter', fontSize=7.5, alignment=TA_CENTER, leading=11) sCenterB = S('sCenterB', fontSize=7.5, alignment=TA_CENTER, fontName='Helvetica-Bold', leading=11) # ── helper: section header row ─────────────────────────────────────────────── def sec_hdr(text, col=MID_BLUE, colspan=6): return ([Paragraph(f' {text}', sSecHdr)] + [''] * (colspan - 1), [('BACKGROUND', (0,0), (-1,-1), col), ('SPAN', (0,0), (-1,-1)), ('TOPPADDING', (0,0), (-1,-1), 4), ('BOTTOMPADDING', (0,0), (-1,-1), 4), ('LEFTPADDING', (0,0), (-1,-1), 6)]) def line_row(label, cols=5, width=None): """A label + underline fill row.""" return [Paragraph(label, sLabel)] + [Paragraph('', sBody)] * cols def check_row(label, options, note=''): """Label + checkbox options.""" boxes = ' '.join(f'☐ {o}' for o in options) if note: boxes += f' <font size="6.5" color="#666666"><i>{note}</i></font>' return [Paragraph(label, sLabel), Paragraph(boxes, sBody)] def uline(width=35*mm): return '_' * 30 # ───────────────────────────────────────────────────────────────────────────── # PAGE ELEMENTS # ───────────────────────────────────────────────────────────────────────────── story = [] usable_w = W - 30*mm # 165 mm # ════════════════════════════════════════════════════════════════════════════ # HEADER BANNER # ════════════════════════════════════════════════════════════════════════════ banner_data = [ [Paragraph('MOTOR ASSESSMENT OF THE LOWER LIMBS', sTitle)], [Paragraph('Neurological Examination Proforma | Patient Record', sSub)], ] banner = Table(banner_data, colWidths=[usable_w]) banner.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), DARK_BLUE), ('TOPPADDING', (0,0), (-1,-1), 10), ('BOTTOMPADDING', (0,0), (-1,-1), 8), ('LEFTPADDING', (0,0), (-1,-1), 8), ('RIGHTPADDING', (0,0), (-1,-1), 8), ('BOX', (0,0), (-1,-1), 1.5, MID_BLUE), ])) story.append(banner) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # PATIENT DETAILS # ════════════════════════════════════════════════════════════════════════════ c1, c2, c3, c4 = 28*mm, 37*mm, 28*mm, 37*mm # label | value | label | value detail_rows = [ [Paragraph('Patient Name:', sLabel), Paragraph('___________________________', sBody), Paragraph('Date:', sLabel), Paragraph('__________________', sBody)], [Paragraph('Age / Sex:', sLabel), Paragraph('___________ / ☐ M ☐ F ☐ Other', sBody), Paragraph('Examiner:', sLabel), Paragraph('__________________', sBody)], [Paragraph('Hospital No.:', sLabel), Paragraph('___________________________', sBody), Paragraph('Ward / OPD:', sLabel), Paragraph('__________________', sBody)], [Paragraph('Diagnosis / Presenting Complaint:', sLabel), Paragraph('_________________________________', sBody), Paragraph('Dominant Side:', sLabel), Paragraph('☐ Right ☐ Left', sBody)], ] detail_tbl = Table(detail_rows, colWidths=[c1,c2,c3,c4]) detail_tbl.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), PALE_BLUE), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('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), 'MIDDLE'), ])) story.append(detail_tbl) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 1. INSPECTION # ════════════════════════════════════════════════════════════════════════════ insp_hdr_row, insp_hdr_style = sec_hdr('1. INSPECTION (Patient supine, fully exposed)', colspan=4) insp_hdr_row2 = ['Feature', 'Right', 'Left', 'Notes / Description'] insp_rows = [ insp_hdr_row, [Paragraph(t, sCenterB) for t in insp_hdr_row2], ['Muscle Bulk', '☐ Normal ☐ Wasted ☐ Hypertrophy', '☐ Normal ☐ Wasted ☐ Hypertrophy', ''], ['Fasciculations', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Involuntary Movements', '☐ None ☐ Tremor ☐ Myoclonus ☐ Other', '☐ None ☐ Tremor ☐ Myoclonus ☐ Other', ''], ['Limb Posture', '☐ Normal ☐ Foot drop ☐ Scissoring ☐ Other', '☐ Normal ☐ Foot drop ☐ Scissoring ☐ Other', ''], ['Trophic / Skin Changes', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Scars / Deformity', '☐ None ☐ Present (describe →)', '☐ None ☐ Present (describe →)', ''], ['Limb Length Asymmetry', 'R: _______ cm', 'L: _______ cm', '☐ Symmetric ☐ Asymmetric'], ] ia, ib, ic, id_ = 42*mm, 42*mm, 42*mm, 39*mm insp_tbl = Table( [[Paragraph(str(r[0]), sLabel) if isinstance(r[0], str) else r[0], Paragraph(str(r[1]), sBody) if isinstance(r[1], str) else r[1], Paragraph(str(r[2]), sBody) if isinstance(r[2], str) else r[2], Paragraph(str(r[3]), sBody) if isinstance(r[3], str) else r[3]] if r != insp_hdr_row else r for r in insp_rows], colWidths=[ia, ib, ic, id_] ) insp_tbl.setStyle(TableStyle([ # header band *[(s[0], s[1], s[2], s[3]) for s in insp_hdr_style], ('SPAN', (0,0), (3,0)), # col header ('BACKGROUND', (0,1), (3,1), LIGHT_BLUE), ('FONTNAME', (0,1), (3,1), 'Helvetica-Bold'), ('FONTSIZE', (0,1), (3,1), 7.5), ('ALIGN', (0,1), (3,1), 'CENTER'), # body ('ROWBACKGROUNDS', (0,2), (-1,-1), [WHITE, LIGHT_GREY]), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('RIGHTPADDING', (0,0), (-1,-1), 3), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTNAME', (0,2), (0,-1), 'Helvetica-Bold'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(insp_tbl) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 2. TONE # ════════════════════════════════════════════════════════════════════════════ tone_hdr_row, tone_hdr_style = sec_hdr('2. TONE (Passive movement, heel-drop test)', colspan=4) tone_rows_data = [ ['Finding', 'Right', 'Left', 'Grade / Comment'], ['Tone Character', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', ''], ['Cogwheel Rigidity', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Heel-Drop Test\n(rapid knee lift)', '☐ Normal heel drag\n☐ Immediate heel lift', '☐ Normal heel drag\n☐ Immediate heel lift', ''], ['Hip', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Knee', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Ankle', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ] tone_tbl = Table( [tone_hdr_row] + [[Paragraph(str(cell).replace('\n', '<br/>'), sCenterB if i == 0 else sBody) if r_i == 0 else Paragraph(str(cell).replace('\n', '<br/>'), sLabel if c_i == 0 else sBody) for c_i, cell in enumerate(row)] for r_i, row in enumerate(tone_rows_data)], colWidths=[ia, ib, ic, id_] ) tone_tbl.setStyle(TableStyle([ *[(s[0], s[1], s[2], s[3]) for s in tone_hdr_style], ('SPAN', (0,0), (3,0)), ('BACKGROUND', (0,1), (3,1), LIGHT_BLUE), ('FONTNAME', (0,1), (3,1), 'Helvetica-Bold'), ('FONTSIZE', (0,1), (3,1), 7.5), ('ALIGN', (0,1), (3,1), 'CENTER'), ('ROWBACKGROUNDS', (0,2), (-1,-1), [WHITE, LIGHT_GREY]), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('RIGHTPADDING', (0,0), (-1,-1), 3), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTNAME', (0,2), (0,-1), 'Helvetica-Bold'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(tone_tbl) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 3. POWER (MRC grading) # ════════════════════════════════════════════════════════════════════════════ pow_hdr_row, pow_hdr_style = sec_hdr('3. POWER (MRC Grade 0–5)', colspan=5) # MRC legend strip mrc_legend = Table([[ Paragraph('<b>MRC Scale:</b> 0 = No movement 1 = Flicker 2 = Gravity eliminated ' '3 = Against gravity 4 = Against resistance 5 = Full power', sSmall) ]], colWidths=[usable_w]) mrc_legend.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#FFF4E5')), ('BOX', (0,0), (-1,-1), 0.5, ACCENT), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 6), ])) power_movements = [ ('Hip Flexion (Iliopsoas)', 'L1–L3'), ('Hip Extension (Glut. Max.)', 'L4–S1'), ('Hip Abduction (Glut. Med.)', 'L4–S1'), ('Hip Adduction', 'L2–L4'), ('Knee Extension (Quadriceps)', 'L3–L4'), ('Knee Flexion (Hamstrings)', 'L5–S2'), ('Ankle Dorsiflexion (Tib. Ant.)','L4–L5'), ('Ankle Plantarflexion', 'S1–S2'), ('Ankle Eversion (Peronei)', 'L5–S1'), ('Ankle Inversion (Tib. Post.)', 'L4–L5'), ('Great Toe Extension (EHL)', 'L5'), ('Toe Flexion', 'S1–S2'), ] def mrc_cells(label, root): def grade_box(side): return Paragraph(f' 0 / 1 / 2 / 3 / 4⁻ / 4 / 4⁺ / 5\nCircle: ____', sSmall) return [Paragraph(label, sLabel), Paragraph(root, sCenter), Paragraph('0 1 2 3 4⁻ 4 4⁺ 5\n(circle)', sSmall), Paragraph('0 1 2 3 4⁻ 4 4⁺ 5\n(circle)', sSmall), Paragraph('', sBody)] pa, pb, pc, pd, pe = 52*mm, 15*mm, 34*mm, 34*mm, 30*mm pow_body = [pow_hdr_row, [Paragraph(t, sCenterB) for t in ['Movement', 'Root', 'Right', 'Left', 'Notes']]] for mv, rt in power_movements: pow_body.append(mrc_cells(mv, rt)) pow_tbl = Table(pow_body, colWidths=[pa, pb, pc, pd, pe]) pow_tbl.setStyle(TableStyle([ *[(s[0], s[1], s[2], s[3]) for s in pow_hdr_style], ('SPAN', (0,0), (4,0)), ('BACKGROUND', (0,1), (4,1), LIGHT_BLUE), ('FONTNAME', (0,1), (4,1), 'Helvetica-Bold'), ('FONTSIZE', (0,1), (4,1), 7.5), ('ALIGN', (0,1), (4,1), 'CENTER'), ('ROWBACKGROUNDS', (0,2), (-1,-1), [WHITE, LIGHT_GREY]), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('RIGHTPADDING', (0,0), (-1,-1), 3), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(KeepTogether([mrc_legend, Spacer(1,2*mm), pow_tbl])) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 4. REFLEXES # ════════════════════════════════════════════════════════════════════════════ ref_hdr_row, ref_hdr_style = sec_hdr('4. REFLEXES', colspan=5) # Reflex grade legend ref_legend = Table([[ Paragraph('<b>Reflex Grade:</b> 0 = Absent 1 = Diminished ' '2 = Normal 3 = Brisk / Increased 4 = Clonus', sSmall) ]], colWidths=[usable_w]) ref_legend.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#EEF8EE')), ('BOX', (0,0), (-1,-1), 0.5, colors.HexColor('#4CAF50')), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 6), ])) deep_reflexes = [ ('Knee Jerk (Patellar)', 'L3–L4'), ('Ankle Jerk (Achilles)', 'S1–S2'), ] superficial_reflexes = [ ('Plantar Reflex', 'S1', '☐ Flexor (Normal) ☐ Extensor (Babinski +ve) ☐ No response', '☐ Flexor (Normal) ☐ Extensor (Babinski +ve) ☐ No response'), ('Cremasteric', 'L1–L2', '☐ Present ☐ Absent', '☐ Present ☐ Absent'), ('Abdominal', 'T7–T12','☐ Present ☐ Absent', '☐ Present ☐ Absent'), ] clonus_tests = [ ('Ankle Clonus', 'S1–S2'), ('Patellar Clonus','L2–L4'), ] ra, rb, rc, rd, re = 50*mm, 14*mm, 35*mm, 35*mm, 31*mm ref_rows = [ref_hdr_row, [Paragraph(t, sCenterB) for t in ['Reflex', 'Root', 'Right', 'Left', 'Notes']]] # sub-section: Deep Tendon ref_rows.append([Paragraph(' DEEP TENDON REFLEXES', sSecHdr)] + [''] * 4) for name, root in deep_reflexes: ref_rows.append([Paragraph(name, sLabel), Paragraph(root, sCenter), Paragraph('0 1 2 3 4\n(circle)', sSmall), Paragraph('0 1 2 3 4\n(circle)', sSmall), Paragraph('', sBody)]) # sub-section: Clonus ref_rows.append([Paragraph(' CLONUS', sSecHdr)] + [''] * 4) for name, root in clonus_tests: ref_rows.append([Paragraph(name, sLabel), Paragraph(root, sCenter), Paragraph('☐ Absent ☐ Present\nBeats: ___', sSmall), Paragraph('☐ Absent ☐ Present\nBeats: ___', sSmall), Paragraph('', sBody)]) # sub-section: Superficial ref_rows.append([Paragraph(' SUPERFICIAL / CUTANEOUS REFLEXES', sSecHdr)] + [''] * 4) for name, root, right_opts, left_opts in superficial_reflexes: ref_rows.append([Paragraph(name, sLabel), Paragraph(root, sCenter), Paragraph(right_opts, sSmall), Paragraph(left_opts, sSmall), Paragraph('', sBody)]) ref_tbl = Table(ref_rows, colWidths=[ra, rb, rc, rd, re]) sub_hdr_indices = [2+len(deep_reflexes), 2+len(deep_reflexes)+1+len(clonus_tests)] # clonus, superficial all_sub_indices = [2, sub_hdr_indices[0], sub_hdr_indices[1]] ref_style_cmds = [ *[(s[0], s[1], s[2], s[3]) for s in ref_hdr_style], ('SPAN', (0,0), (4,0)), ('BACKGROUND', (0,1), (4,1), LIGHT_BLUE), ('FONTNAME', (0,1), (4,1), 'Helvetica-Bold'), ('FONTSIZE', (0,1), (4,1), 7.5), ('ALIGN', (0,1), (4,1), 'CENTER'), ('ROWBACKGROUNDS', (0,2), (-1,-1), [WHITE, LIGHT_GREY]), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('RIGHTPADDING', (0,0), (-1,-1), 3), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ] for idx in all_sub_indices: ref_style_cmds += [ ('BACKGROUND', (0, idx), (-1, idx), colors.HexColor('#3A7EBF')), ('SPAN', (0, idx), (-1, idx)), ('TOPPADDING', (0, idx), (-1, idx), 3), ('BOTTOMPADDING', (0, idx), (-1, idx), 3), ] ref_tbl.setStyle(TableStyle(ref_style_cmds)) story.append(KeepTogether([ref_legend, Spacer(1,2*mm), ref_tbl])) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 5. COORDINATION # ════════════════════════════════════════════════════════════════════════════ coord_hdr_row, coord_hdr_style = sec_hdr('5. COORDINATION (Cerebellar Function)', colspan=4) coord_tests = [ ('Heel-Shin Test', '☐ Normal ☐ Ataxia ☐ Tremor/Dysmetria', '☐ Normal ☐ Ataxia ☐ Tremor/Dysmetria', ''), ('Foot Tapping\n(rapid regular)', '☐ Normal ☐ Bradykinesia ☐ Dysdiadochokinesia', '☐ Normal ☐ Bradykinesia ☐ Dysdiadochokinesia', ''), ('Foot Circle\n(draw circle in air)', '☐ Normal ☐ Dyspraxia ☐ Abnormal', '☐ Normal ☐ Dyspraxia ☐ Abnormal', ''), ('Romberg\'s Test\n(eyes open → closed)', 'Eyes open: ☐ Stable ☐ Unsteady', 'Eyes closed: ☐ Stable ☐ Falls\n→ ☐ Positive ☐ Negative', ''), ] coa, cob, coc, cod = 38*mm, 45*mm, 45*mm, 37*mm coord_rows = [coord_hdr_row, [Paragraph(t, sCenterB) for t in ['Test', 'Right', 'Left', 'Notes']]] for t, r, l, n in coord_tests: coord_rows.append([Paragraph(t.replace('\n','<br/>'), sLabel), Paragraph(r, sBody), Paragraph(l, sBody), Paragraph(n, sBody)]) coord_tbl = Table(coord_rows, colWidths=[coa, cob, coc, cod]) coord_tbl.setStyle(TableStyle([ *[(s[0], s[1], s[2], s[3]) for s in coord_hdr_style], ('SPAN', (0,0), (3,0)), ('BACKGROUND', (0,1), (3,1), LIGHT_BLUE), ('FONTNAME', (0,1), (3,1), 'Helvetica-Bold'), ('FONTSIZE', (0,1), (3,1), 7.5), ('ALIGN', (0,1), (3,1), 'CENTER'), ('ROWBACKGROUNDS', (0,2), (-1,-1), [WHITE, LIGHT_GREY]), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('RIGHTPADDING', (0,0), (-1,-1), 3), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(coord_tbl) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 6. GAIT # ════════════════════════════════════════════════════════════════════════════ gait_hdr_row, gait_hdr_style = sec_hdr('6. GAIT (Examine last)', colspan=3) gait_patterns = [ 'Hemiplegic (circumduction)', 'Spastic paraparesis (scissor)', 'Steppage (foot drop)', 'Waddling (proximal weakness)', 'Ataxic (broad-based)', 'Parkinsonian (shuffling/festination)', 'Antalgic (pain-avoidance)', 'Trendelenburg (hip abductor weakness)', 'Functional / Non-organic', 'Normal', ] gait_check_pairs = [] for i in range(0, len(gait_patterns), 2): left = f'☐ {gait_patterns[i]}' right = f'☐ {gait_patterns[i+1]}' if i+1 < len(gait_patterns) else '' gait_check_pairs.append([left, right]) ga, gb, gc = 25*mm, 70*mm, 70*mm gait_rows = [gait_hdr_row, [Paragraph(t, sCenterB) for t in ['', 'Gait Pattern (tick all that apply)', '']]] for l, r in gait_check_pairs: gait_rows.append(['', Paragraph(l, sBody), Paragraph(r, sBody)]) gait_rows.append(['', Paragraph('Additional Observations:', sLabel), Paragraph('______________________________________', sBody)]) gait_rows.append(['', Paragraph('Assistive Device:', sLabel), Paragraph('☐ None ☐ Walking stick ☐ Frame ☐ Crutches ☐ Wheelchair', sBody)]) gait_tbl = Table(gait_rows, colWidths=[ga, gb, gc]) gait_tbl.setStyle(TableStyle([ *[(s[0], s[1], s[2], s[3]) for s in gait_hdr_style], ('SPAN', (0,0), (2,0)), ('BACKGROUND', (0,1), (2,1), LIGHT_BLUE), ('FONTNAME', (0,1), (2,1), 'Helvetica-Bold'), ('FONTSIZE', (0,1), (2,1), 7.5), ('SPAN', (0,1), (0,1)), ('ALIGN', (0,1), (2,1), 'CENTER'), ('ROWBACKGROUNDS', (0,2), (-1,-1), [WHITE, LIGHT_GREY]), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('RIGHTPADDING', (0,0), (-1,-1), 3), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(gait_tbl) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 7. UMN vs LMN SUMMARY BOX # ════════════════════════════════════════════════════════════════════════════ sum_hdr_row, sum_hdr_style = sec_hdr('7. UMN vs LMN PATTERN SUMMARY', colspan=4) sum_rows = [ sum_hdr_row, [Paragraph(t, sCenterB) for t in ['Feature', 'UMN (Central)', 'LMN (Peripheral)', 'Patient Findings']], ['Muscle Bulk', 'Normal / mild disuse atrophy', 'Marked wasting', ''], ['Fasciculations', 'Absent', 'Present', ''], ['Tone', 'Increased (spasticity)', 'Decreased (flaccidity)', ''], ['Power', 'Reduced (pyramidal pattern)', 'Reduced (root/nerve pattern)', ''], ['Deep Reflexes', 'Hyperreflexia', 'Diminished / Absent', ''], ['Plantar Reflex', 'Extensor (Babinski +ve)', 'Flexor (normal) / Absent', ''], ['Clonus', 'May be present', 'Absent', ''], ['Superficial Reflexes', 'Absent', 'Present / Absent', ''], [Paragraph('Clinical Pattern:', sLabel), Paragraph('☐ UMN ☐ LMN ☐ Mixed ☐ Cerebellar ☐ Extrapyramidal ☐ Normal', sBody), '', ''], ] sa, sb, sc, sd = 38*mm, 44*mm, 44*mm, 39*mm sum_tbl = Table( [row if row == sum_hdr_row else [Paragraph(str(c), sCenterB if r_i==1 else (sLabel if c_i==0 else sBody)) for c_i, c in enumerate(row)] for r_i, row in enumerate(sum_rows)], colWidths=[sa, sb, sc, sd] ) sum_tbl.setStyle(TableStyle([ *[(s[0], s[1], s[2], s[3]) for s in sum_hdr_style], ('SPAN', (0,0), (3,0)), ('SPAN', (1, len(sum_rows)-1), (3, len(sum_rows)-1)), ('BACKGROUND', (0,1), (3,1), LIGHT_BLUE), ('FONTNAME', (0,1), (3,1), 'Helvetica-Bold'), ('FONTSIZE', (0,1), (3,1), 7.5), ('ALIGN', (0,1), (3,1), 'CENTER'), ('ROWBACKGROUNDS', (0,2), (-1,-2), [WHITE, LIGHT_GREY]), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('RIGHTPADDING', (0,0), (-1,-1), 3), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ('BACKGROUND', (0, len(sum_rows)-1), (-1, len(sum_rows)-1), colors.HexColor('#FFF4E5')), ])) story.append(sum_tbl) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 8. CLINICAL NOTES + SIGNATURE # ════════════════════════════════════════════════════════════════════════════ notes_hdr_row, notes_hdr_style = sec_hdr('8. CLINICAL SUMMARY & NOTES', colspan=1) notes_data = [ notes_hdr_row, [Paragraph('Overall Impression / Provisional Diagnosis:', sLabel)], [Paragraph('_' * 120, sBody)], [Paragraph('_' * 120, sBody)], [Paragraph('_' * 120, sBody)], [Paragraph('Further Investigations Requested:', sLabel)], [Paragraph('_' * 120, sBody)], [Paragraph('_' * 120, sBody)], ] notes_tbl = Table(notes_data, colWidths=[usable_w]) notes_tbl.setStyle(TableStyle([ *[(s[0], s[1], s[2], s[3]) for s in notes_hdr_style], ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 6), ('ROWBACKGROUNDS', (0,1), (-1,-1), [PALE_BLUE, WHITE]), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(notes_tbl) story.append(Spacer(1, 5*mm)) # Signature row sig_data = [[ Paragraph('Examiner Signature: ___________________________', sBody), Paragraph('Designation: _______________________', sBody), Paragraph('Date: _______________', sBody), ]] sig_tbl = Table(sig_data, colWidths=[65*mm, 60*mm, 40*mm]) sig_tbl.setStyle(TableStyle([ ('TOPPADDING', (0,0), (-1,-1), 4), ('LEFTPADDING', (0,0), (-1,-1), 4), ('FONTSIZE', (0,0), (-1,-1), 7.5), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ])) story.append(sig_tbl) # ════════════════════════════════════════════════════════════════════════════ # FOOTER # ════════════════════════════════════════════════════════════════════════════ story.append(Spacer(1, 3*mm)) footer_tbl = Table([[ Paragraph('Sequence: Inspection → Tone → Power → Reflexes → Coordination → Gait', sNote), Paragraph('Based on Harrison\'s 22E | Bradley & Daroff\'s Neurology | Das Manual of Clinical Surgery', ParagraphStyle('fn2', parent=sNote, alignment=TA_RIGHT)) ]], colWidths=[90*mm, 75*mm]) footer_tbl.setStyle(TableStyle([ ('TOPPADDING', (0,0), (-1,-1), 2), ('BOTTOMPADDING', (0,0), (-1,-1), 2), ('LINEABOVE', (0,0), (-1,0), 0.5, MID_GREY), ])) story.append(footer_tbl) # ── BUILD ──────────────────────────────────────────────────────────────────── doc.build(story) print("PDF created successfully.") PYEOF

The issue is with the sec_hdr function returning a tuple being unpacked in a list context. Let me fix this properly.
Running Command

python3 << 'PYEOF' from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.units import mm from reportlab.platypus import (SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, KeepTogether) from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT # ── colours ────────────────────────────────────────────────────────────────── DARK_BLUE = colors.HexColor('#1A3A5C') MID_BLUE = colors.HexColor('#2E6DA4') LIGHT_BLUE = colors.HexColor('#D6E8F7') PALE_BLUE = colors.HexColor('#EEF5FB') ACCENT = colors.HexColor('#E8821A') LIGHT_GREY = colors.HexColor('#F2F4F7') MID_GREY = colors.HexColor('#CCCCCC') WHITE = colors.white W, H = A4 USABLE_W = W - 30*mm doc = SimpleDocTemplate( '/tmp/workspace/motor-assessment/Lower_Limb_Motor_Assessment.pdf', pagesize=A4, leftMargin=15*mm, rightMargin=15*mm, topMargin=15*mm, bottomMargin=15*mm, ) styles = getSampleStyleSheet() def S(name, parent='Normal', **kw): return ParagraphStyle(name, parent=styles[parent], **kw) sTitle = S('sTitle', fontSize=18, textColor=colors.white, alignment=TA_CENTER, fontName='Helvetica-Bold') sSub = S('sSub', fontSize=10, textColor=colors.HexColor('#BFD9F2'), alignment=TA_CENTER, fontName='Helvetica') sSecHdr = S('sSecHdr', fontSize=9, textColor=colors.white, fontName='Helvetica-Bold', alignment=TA_LEFT, leading=12) sSubHdr = S('sSubHdr', fontSize=8, textColor=colors.white, fontName='Helvetica-Bold', alignment=TA_LEFT, leading=11) sBody = S('sBody', fontSize=7.5, leading=11, textColor=colors.black) sLabel = S('sLabel', fontSize=7.5, fontName='Helvetica-Bold', textColor=DARK_BLUE, leading=11) sSmall = S('sSmall', fontSize=7, textColor=colors.HexColor('#444444'), leading=10) sCenter = S('sCenter', fontSize=7.5, alignment=TA_CENTER, leading=11) sCenterB= S('sCenterB',fontSize=7.5, alignment=TA_CENTER, fontName='Helvetica-Bold', leading=11) sNote = S('sNote', fontSize=7, fontName='Helvetica-Oblique', textColor=colors.HexColor('#666666'), leading=10) def make_section(title, col_widths, col_headers, data_rows, hdr_color=MID_BLUE, sub_hdr_rows=None): """ Builds a section table. title: string for the coloured header band col_widths: list of widths col_headers: list of header strings data_rows: list of lists of (string or Paragraph) sub_hdr_rows: dict {row_index: color} for sub-header rows (0-based within data_rows) """ ncols = len(col_widths) # --- build raw rows --- all_rows = [] style_cmds = [] # Row 0: section header band header_cells = [Paragraph(f' {title}', sSecHdr)] + [''] * (ncols - 1) all_rows.append(header_cells) style_cmds += [ ('BACKGROUND', (0,0), (-1,0), hdr_color), ('SPAN', (0,0), (-1,0)), ('TOPPADDING', (0,0), (-1,0), 5), ('BOTTOMPADDING', (0,0), (-1,0), 5), ('LEFTPADDING', (0,0), (-1,0), 6), ] # Row 1: column headers ch_cells = [Paragraph(h, sCenterB) for h in col_headers] all_rows.append(ch_cells) style_cmds += [ ('BACKGROUND', (0,1), (-1,1), LIGHT_BLUE), ('ALIGN', (0,1), (-1,1), 'CENTER'), ('FONTNAME', (0,1), (-1,1), 'Helvetica-Bold'), ('FONTSIZE', (0,1), (-1,1), 7.5), ] # Data rows start at index 2 sub_hdr_rows = sub_hdr_rows or {} for i, row in enumerate(data_rows): tbl_row_idx = i + 2 if i in sub_hdr_rows: # sub-header row sh_cells = [Paragraph(f' {row[0]}', sSubHdr)] + [''] * (ncols - 1) all_rows.append(sh_cells) sh_col = sub_hdr_rows[i] style_cmds += [ ('BACKGROUND', (0, tbl_row_idx), (-1, tbl_row_idx), sh_col), ('SPAN', (0, tbl_row_idx), (-1, tbl_row_idx)), ('TOPPADDING', (0, tbl_row_idx), (-1, tbl_row_idx), 3), ('BOTTOMPADDING', (0, tbl_row_idx), (-1, tbl_row_idx), 3), ] else: formatted = [] for j, cell in enumerate(row): if isinstance(cell, Paragraph): formatted.append(cell) elif j == 0: formatted.append(Paragraph(str(cell), sLabel)) else: formatted.append(Paragraph(str(cell).replace('\n','<br/>'), sBody)) all_rows.append(formatted) # Global style style_cmds += [ ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,2), (-1,-1), 3), ('BOTTOMPADDING', (0,2), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('RIGHTPADDING',(0,0), (-1,-1), 3), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTSIZE', (0,2), (-1,-1), 7.5), ('ROWBACKGROUNDS', (0,2), (-1,-1), [WHITE, LIGHT_GREY]), ('FONTNAME', (0,2), (0,-1), 'Helvetica-Bold'), ] tbl = Table(all_rows, colWidths=col_widths) tbl.setStyle(TableStyle(style_cmds)) return tbl story = [] # ════════════════════════════════════════════════════════════════════════════ # BANNER # ════════════════════════════════════════════════════════════════════════════ banner = Table([ [Paragraph('MOTOR ASSESSMENT OF THE LOWER LIMBS', sTitle)], [Paragraph('Neurological Examination Proforma · Patient Record / Documentation', sSub)], ], colWidths=[USABLE_W]) banner.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), DARK_BLUE), ('TOPPADDING', (0,0), (-1,-1), 10), ('BOTTOMPADDING', (0,0), (-1,-1), 8), ('LEFTPADDING', (0,0), (-1,-1), 8), ('BOX', (0,0), (-1,-1), 1.5, MID_BLUE), ])) story.append(banner) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # PATIENT DETAILS # ════════════════════════════════════════════════════════════════════════════ det = Table([ [Paragraph('Patient Name:', sLabel), Paragraph('___________________________', sBody), Paragraph('Date:', sLabel), Paragraph('__________________', sBody)], [Paragraph('Age / Sex:', sLabel), Paragraph('_________ / ☐ M ☐ F ☐ Other', sBody), Paragraph('Examiner:', sLabel), Paragraph('__________________', sBody)], [Paragraph('Hospital No.:', sLabel), Paragraph('___________________________', sBody), Paragraph('Ward / OPD:', sLabel), Paragraph('__________________', sBody)], [Paragraph('Diagnosis / Complaint:', sLabel), Paragraph('_________________________________', sBody), Paragraph('Dominant Side:', sLabel), Paragraph('☐ Right ☐ Left', sBody)], ], colWidths=[30*mm, 38*mm, 28*mm, 69*mm]) det.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), PALE_BLUE), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 5), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(det) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 1. INSPECTION # ════════════════════════════════════════════════════════════════════════════ insp = make_section( '1. INSPECTION (Patient supine, fully exposed, good lighting)', col_widths=[38*mm, 43*mm, 43*mm, 41*mm], col_headers=['Feature', 'Right', 'Left', 'Notes'], data_rows=[ ['Muscle Bulk', '☐ Normal ☐ Wasted ☐ Hypertrophy', '☐ Normal ☐ Wasted ☐ Hypertrophy', ''], ['Fasciculations', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Involuntary Movements', '☐ None ☐ Tremor ☐ Myoclonus ☐ Other', '☐ None ☐ Tremor ☐ Myoclonus ☐ Other', ''], ['Limb Posture', '☐ Normal ☐ Foot drop ☐ Scissoring ☐ Other', '☐ Normal ☐ Foot drop ☐ Scissoring ☐ Other', ''], ['Trophic / Skin Changes', '☐ Absent ☐ Present (describe)', '☐ Absent ☐ Present (describe)', ''], ['Scars / Deformity', '☐ None ☐ Present', '☐ None ☐ Present', ''], ['Limb Length', 'R: _______ cm', 'L: _______ cm', '☐ Symmetric ☐ Asymmetric'], ] ) story.append(insp) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 2. TONE # ════════════════════════════════════════════════════════════════════════════ tone = make_section( '2. TONE (Passive movement; heel-drop test)', col_widths=[38*mm, 43*mm, 43*mm, 41*mm], col_headers=['Finding', 'Right', 'Left', 'Comment'], data_rows=[ ['Tone Character', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', ''], ['Cogwheel Rigidity', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Heel-Drop Test\n(rapid knee lift)', '☐ Normal heel drag\n☐ Immediate heel lift', '☐ Normal heel drag\n☐ Immediate heel lift', ''], ['Hip', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Knee', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Ankle','☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ] ) story.append(tone) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 3. POWER # ════════════════════════════════════════════════════════════════════════════ mrc_legend = Table([[Paragraph( '<b>MRC Scale:</b> 0 = No movement 1 = Flicker 2 = Gravity eliminated ' '3 = Against gravity 4⁻ = Mild resistance 4 = Moderate resistance ' '4⁺ = Strong resistance 5 = Full power', sSmall) ]], colWidths=[USABLE_W]) mrc_legend.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#FFF4E5')), ('BOX', (0,0), (-1,-1), 0.5, ACCENT), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 6), ])) power_data = [ ['Hip Flexion (Iliopsoas)', 'L1–L3', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Hip Extension (Glut. Max.)', 'L4–S1', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Hip Abduction (Glut. Med.)', 'L4–S1', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Hip Adduction', 'L2–L4', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Knee Extension (Quadriceps)', 'L3–L4', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Knee Flexion (Hamstrings)', 'L5–S2', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Ankle Dorsiflexion (Tib. Ant.)','L4–L5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Ankle Plantarflexion', 'S1–S2', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Ankle Eversion (Peronei)', 'L5–S1', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Ankle Inversion (Tib. Post.)', 'L4–L5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Great Toe Extension (EHL)', 'L5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Toe Flexion', 'S1–S2', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ] pow_tbl = make_section( '3. POWER (MRC Grade 0–5; circle the grade)', col_widths=[48*mm, 14*mm, 37*mm, 37*mm, 29*mm], col_headers=['Movement', 'Root', 'Right (circle)', 'Left (circle)', 'Notes'], data_rows=power_data ) story.append(KeepTogether([mrc_legend, Spacer(1,2*mm), pow_tbl])) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 4. REFLEXES # ════════════════════════════════════════════════════════════════════════════ ref_legend = Table([[Paragraph( '<b>Reflex Grade:</b> 0 = Absent 1 = Diminished 2 = Normal ' '3 = Brisk / Increased 4 = Clonus', sSmall) ]], colWidths=[USABLE_W]) ref_legend.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#EEF8EE')), ('BOX', (0,0), (-1,-1), 0.5, colors.HexColor('#4CAF50')), ('TOPPADDING', (0,0), (-1,-1), 3), ('BOTTOMPADDING', (0,0), (-1,-1), 3), ('LEFTPADDING', (0,0), (-1,-1), 6), ])) # sub-header rows: index within data_rows reflex_data = [ ['DEEP TENDON REFLEXES'], # sub-hdr index 0 ['Knee Jerk (Patellar)', 'L3–L4', 'Grade: 0 1 2 3 4', 'Grade: 0 1 2 3 4', ''], ['Ankle Jerk (Achilles)', 'S1–S2', 'Grade: 0 1 2 3 4', 'Grade: 0 1 2 3 4', ''], ['CLONUS'], # sub-hdr index 3 ['Ankle Clonus', 'S1–S2', '☐ Absent ☐ Present Beats:___', '☐ Absent ☐ Present Beats:___', ''], ['Patellar Clonus', 'L2–L4', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['SUPERFICIAL / CUTANEOUS REFLEXES'], # sub-hdr index 6 ['Plantar Reflex', 'S1', '☐ Flexor (normal)\n☐ Extensor (Babinski +ve)\n☐ No response', '☐ Flexor (normal)\n☐ Extensor (Babinski +ve)\n☐ No response', ''], ['Cremasteric', 'L1–L2', '☐ Present ☐ Absent', '☐ Present ☐ Absent', ''], ['Abdominal', 'T7–T12','☐ Present ☐ Absent', '☐ Present ☐ Absent', ''], ['Anal Reflex', 'S2–S4', '☐ Present ☐ Absent', '☐ Present ☐ Absent', ''], ] sub_hdr_map = {0: colors.HexColor('#3A7EBF'), 3: colors.HexColor('#3A7EBF'), 6: colors.HexColor('#3A7EBF')} ref_tbl = make_section( '4. REFLEXES', col_widths=[44*mm, 14*mm, 38*mm, 38*mm, 31*mm], col_headers=['Reflex', 'Root', 'Right', 'Left', 'Notes'], data_rows=reflex_data, sub_hdr_rows=sub_hdr_map ) story.append(KeepTogether([ref_legend, Spacer(1,2*mm), ref_tbl])) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 5. COORDINATION # ════════════════════════════════════════════════════════════════════════════ coord_data = [ ['Heel-Shin Test', '☐ Normal ☐ Ataxia ☐ Dysmetria', '☐ Normal ☐ Ataxia ☐ Dysmetria', ''], ['Foot Tapping (rapid)', '☐ Normal ☐ Bradykinesia\n☐ Dysdiadochokinesia', '☐ Normal ☐ Bradykinesia\n☐ Dysdiadochokinesia', ''], ['Foot Circle Drawing', '☐ Normal ☐ Dyspraxia ☐ Abnormal', '☐ Normal ☐ Dyspraxia ☐ Abnormal', ''], ['Romberg\'s Test', 'Eyes open: ☐ Stable ☐ Unsteady', 'Eyes closed: ☐ Stable ☐ Falls → ☐ +ve ☐ −ve', ''], ] coord_tbl = make_section( '5. COORDINATION (Cerebellar / Proprioceptive Function)', col_widths=[38*mm, 46*mm, 50*mm, 31*mm], col_headers=['Test', 'Right', 'Left / Bilateral', 'Notes'], data_rows=coord_data ) story.append(coord_tbl) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 6. GAIT # ════════════════════════════════════════════════════════════════════════════ gait_data = [ ['TICK ALL THAT APPLY'], # sub-hdr ['☐ Normal', 'Pattern', ''], ['☐ Hemiplegic (circumduction)', '☐ Spastic paraparesis (scissor)', ''], ['☐ Steppage / Foot drop', '☐ Waddling (proximal weakness)', ''], ['☐ Ataxic (broad-based)', '☐ Parkinsonian (shuffling / festination)', ''], ['☐ Antalgic (pain-avoidance)', '☐ Trendelenburg (hip abductor weakness)', ''], ['☐ Functional / Non-organic', '☐ Other: ___________________', ''], ['Assistive Device', '☐ None ☐ Walking stick ☐ Frame ☐ Crutches ☐ Wheelchair', ''], ['Additional Observations', '____________________________________________', ''], ] gait_tbl = make_section( '6. GAIT (Examine last; observe before recumbent exam)', col_widths=[55*mm, 75*mm, 35*mm], col_headers=['', 'Finding', 'Notes'], data_rows=gait_data, sub_hdr_rows={0: colors.HexColor('#3A7EBF')} ) story.append(gait_tbl) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 7. UMN vs LMN SUMMARY # ════════════════════════════════════════════════════════════════════════════ summ_data = [ ['Muscle Bulk', 'Normal / mild disuse atrophy', 'Marked wasting', ''], ['Fasciculations', 'Absent', 'Present', ''], ['Tone', 'Increased (spasticity)', 'Decreased (flaccidity)', ''], ['Power', 'Reduced (pyramidal pattern)', 'Reduced (root/nerve)', ''], ['Deep Reflexes', 'Hyperreflexia', 'Diminished / Absent', ''], ['Plantar Reflex', 'Extensor (Babinski +ve)', 'Flexor / Absent', ''], ['Clonus', 'May be present', 'Absent', ''], ['Superficial Reflex','Absent', 'Present / Absent', ''], ] summ_tbl = make_section( '7. PATTERN SUMMARY (UMN vs LMN)', col_widths=[38*mm, 46*mm, 46*mm, 35*mm], col_headers=['Feature', 'UMN (Central)', 'LMN (Peripheral)', 'Patient Findings'], data_rows=summ_data, hdr_color=colors.HexColor('#2E7D32') ) # Clinical pattern selector (appended below the table as a separate mini-table) pattern_sel = Table([[ Paragraph('<b>Overall Clinical Pattern:</b>', sLabel), Paragraph('☐ Normal ☐ UMN ☐ LMN ☐ Mixed ☐ Cerebellar ☐ Extrapyramidal ☐ Functional', sBody) ]], colWidths=[45*mm, USABLE_W - 45*mm]) pattern_sel.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#FFF4E5')), ('BOX', (0,0), (-1,-1), 0.8, ACCENT), ('TOPPADDING', (0,0), (-1,-1), 4), ('BOTTOMPADDING', (0,0), (-1,-1), 4), ('LEFTPADDING', (0,0), (-1,-1), 6), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(KeepTogether([summ_tbl, Spacer(1,2*mm), pattern_sel])) story.append(Spacer(1, 4*mm)) # ════════════════════════════════════════════════════════════════════════════ # 8. CLINICAL NOTES + SIGNATURE # ════════════════════════════════════════════════════════════════════════════ notes_tbl = Table([ [Paragraph(' 8. CLINICAL SUMMARY & PLAN', sSecHdr)], [Paragraph('Impression / Provisional Diagnosis:', sLabel)], [Paragraph('_' * 110, sBody)], [Paragraph('_' * 110, sBody)], [Paragraph('Further Investigations Requested:', sLabel)], [Paragraph('_' * 110, sBody)], [Paragraph('Management / Referral Plan:', sLabel)], [Paragraph('_' * 110, sBody)], ], colWidths=[USABLE_W]) notes_tbl.setStyle(TableStyle([ ('BACKGROUND', (0,0), (-1,0), MID_BLUE), ('TOPPADDING', (0,0), (-1,0), 5), ('BOTTOMPADDING', (0,0), (-1,0), 5), ('LEFTPADDING', (0,0), (-1,0), 6), ('BOX', (0,0), (-1,-1), 0.8, MID_BLUE), ('INNERGRID', (0,0), (-1,-1), 0.3, MID_GREY), ('ROWBACKGROUNDS', (0,1), (-1,-1), [PALE_BLUE, WHITE]), ('TOPPADDING', (0,1), (-1,-1), 3), ('BOTTOMPADDING', (0,1), (-1,-1), 3), ('LEFTPADDING', (0,1), (-1,-1), 6), ('FONTSIZE', (0,0), (-1,-1), 7.5), ])) story.append(notes_tbl) story.append(Spacer(1, 5*mm)) # Signature sig_tbl = Table([[ Paragraph('Examiner Signature: ___________________________', sBody), Paragraph('Designation: _______________________', sBody), Paragraph('Date: ________________', sBody), ]], colWidths=[65*mm, 60*mm, 40*mm]) sig_tbl.setStyle(TableStyle([ ('TOPPADDING', (0,0), (-1,-1), 4), ('LEFTPADDING', (0,0), (-1,-1), 4), ('FONTSIZE', (0,0), (-1,-1), 7.5), ('VALIGN', (0,0), (-1,-1), 'MIDDLE'), ])) story.append(sig_tbl) story.append(Spacer(1, 3*mm)) # Footer footer = Table([[ Paragraph('Sequence: Inspection → Tone → Power → Reflexes → Coordination → Gait', sNote), Paragraph("Harrison's 22E · Bradley & Daroff's Neurology · Das Manual of Clinical Surgery", ParagraphStyle('ftr', parent=sNote, alignment=TA_RIGHT)) ]], colWidths=[90*mm, 75*mm]) footer.setStyle(TableStyle([ ('LINEABOVE', (0,0), (-1,0), 0.5, MID_GREY), ('TOPPADDING', (0,0), (-1,-1), 2), ('BOTTOMPADDING', (0,0), (-1,-1), 2), ])) story.append(footer) doc.build(story) print("PDF created successfully at /tmp/workspace/motor-assessment/Lower_Limb_Motor_Assessment.pdf") PYEOF

Your PDF proforma is ready:

Lower Limb Motor Assessment

PDF Document · PDF


What's included in the proforma (A4, print-ready):
SectionContents
Patient DetailsName, Age/Sex, Hospital No., Date, Examiner, Ward/OPD, Dominant side
1. InspectionMuscle bulk, fasciculations, involuntary movements, posture, trophic changes, scars, limb length - with Right/Left columns
2. ToneCharacter (normal/hypotonia/spasticity/rigidity/paratonia), cogwheel rigidity, heel-drop test, joint-by-joint (hip/knee/ankle)
3. PowerAll 12 lower limb movements with root levels, MRC 0–5 circle-to-grade format, with a colour-coded MRC legend strip
4. ReflexesDeep tendon (knee/ankle jerk), clonus (ankle/patellar), superficial (plantar/Babinski, cremasteric, abdominal, anal) - with reflex grade legend
5. CoordinationHeel-shin test, foot tapping, foot circle, Romberg's test
6. GaitAll major gait patterns as tickboxes, assistive device, free-text observation
7. UMN vs LMN Pattern SummarySide-by-side comparison table + clinical pattern selector
8. Clinical SummaryImpression, investigations, management/referral, signature block
The design uses a dark blue/blue colour scheme with alternating row backgrounds for easy reading, MRC and reflex grading legends in coloured callout boxes, and a footer with the source references.

Generate a Word (.docx) version for easier digital note-taking and editing

Reading File
Running Command

python3 << 'PYEOF' 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 # ── colour helpers ──────────────────────────────────────────────────────────── def hex_rgb(h): h = h.lstrip('#') return RGBColor(int(h[0:2],16), int(h[2:4],16), int(h[4:6],16)) C_DARK_BLUE = '1A3A5C' C_MID_BLUE = '2E6DA4' C_LIGHT_BLUE = 'D6E8F7' C_PALE_BLUE = 'EEF5FB' C_ACCENT = 'E8821A' C_GREEN = '2E7D32' C_LT_GREEN = 'EEF8EE' C_GREY = 'F2F4F7' C_MID_GREY = 'CCCCCC' C_SUB_BLUE = '3A7EBF' C_ORANGE_LT = 'FFF4E5' C_WHITE = 'FFFFFF' C_BLACK = '000000' 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=None, bottom=None, left=None, right=None, color='CCCCCC', 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)]: if val: el = OxmlElement(f'w:{side}') el.set(qn('w:val'), val) el.set(qn('w:sz'), sz) el.set(qn('w:space'),'0') el.set(qn('w:color'), color) tcBorders.append(el) tcPr.append(tcBorders) def set_table_outer_border(table, color='2E6DA4', sz='8'): for row in table.rows: for i, cell in enumerate(row.cells): is_first_row = (row == table.rows[0]) is_last_row = (row == table.rows[-1]) is_first_col = (i == 0) is_last_col = (i == len(row.cells)-1) tc = cell._tc tcPr = tc.get_or_add_tcPr() tcBorders = OxmlElement('w:tcBorders') for side in ['top','bottom','left','right']: el = OxmlElement(f'w:{side}') if ((side=='top' and is_first_row) or (side=='bottom' and is_last_row) or (side=='left' and is_first_col) or (side=='right' and is_last_col)): el.set(qn('w:val'), 'single') el.set(qn('w:sz'), sz) el.set(qn('w:space'),'0') el.set(qn('w:color'), color) else: el.set(qn('w:val'), 'single') el.set(qn('w:sz'), '2') el.set(qn('w:space'),'0') el.set(qn('w:color'), 'CCCCCC') tcBorders.append(el) tcPr.append(tcBorders) def set_col_widths(table, widths_cm): for i, row in enumerate(table.rows): for j, cell in enumerate(row.cells): tc = cell._tc tcPr = tc.get_or_add_tcPr() tcW = OxmlElement('w:tcW') tcW.set(qn('w:w'), str(int(widths_cm[j] * 567))) # 1 cm ≈ 567 twips tcW.set(qn('w:type'), 'dxa') tcPr.append(tcW) def para_in_cell(cell, text, bold=False, italic=False, size=9, color=C_BLACK, align=WD_ALIGN_PARAGRAPH.LEFT, bg=None, space_before=0, space_after=0): if bg: set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER p = cell.paragraphs[0] if cell.paragraphs else cell.add_paragraph() p.clear() p.alignment = align p.paragraph_format.space_before = Pt(space_before) p.paragraph_format.space_after = Pt(space_after) p.paragraph_format.left_indent = Pt(0) run = p.add_run(text) run.bold = bold run.italic = italic run.font.size = Pt(size) run.font.color.rgb = hex_rgb(color) run.font.name = 'Calibri' return p def add_para_to_cell(cell, text, bold=False, italic=False, size=9, color=C_BLACK, align=WD_ALIGN_PARAGRAPH.LEFT): p = cell.add_paragraph() p.alignment = align p.paragraph_format.space_before = Pt(0) p.paragraph_format.space_after = Pt(0) run = p.add_run(text) run.bold = bold run.italic = italic run.font.size = Pt(size) run.font.color.rgb = hex_rgb(color) run.font.name = 'Calibri' return p # ── Document setup ──────────────────────────────────────────────────────────── doc = Document() # Page margins for section in doc.sections: section.page_width = Cm(21) section.page_height = Cm(29.7) section.left_margin = Cm(1.5) section.right_margin = Cm(1.5) section.top_margin = Cm(1.5) section.bottom_margin = Cm(1.5) # Normal style normal = doc.styles['Normal'] normal.font.name = 'Calibri' normal.font.size = Pt(10) def add_space(doc, pt=4): p = doc.add_paragraph() p.paragraph_format.space_before = Pt(0) p.paragraph_format.space_after = Pt(pt) # ──────────────────────────────────────────────────────────────────────────── # BANNER # ──────────────────────────────────────────────────────────────────────────── banner_tbl = doc.add_table(rows=2, cols=1) banner_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER banner_tbl.style = 'Table Grid' set_cell_bg(banner_tbl.rows[0].cells[0], C_DARK_BLUE) set_cell_bg(banner_tbl.rows[1].cells[0], C_DARK_BLUE) para_in_cell(banner_tbl.rows[0].cells[0], 'MOTOR ASSESSMENT OF THE LOWER LIMBS', bold=True, size=16, color=C_WHITE, align=WD_ALIGN_PARAGRAPH.CENTER, space_before=6, space_after=2) para_in_cell(banner_tbl.rows[1].cells[0], 'Neurological Examination Proforma · Patient Record / Documentation', bold=False, size=10, color='BFD9F2', align=WD_ALIGN_PARAGRAPH.CENTER, space_before=2, space_after=6) set_table_outer_border(banner_tbl, color=C_MID_BLUE, sz='12') add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # PATIENT DETAILS # ──────────────────────────────────────────────────────────────────────────── def add_detail_row(table, row_idx, items): # items: list of (label, value) pairs row = table.rows[row_idx] cells = row.cells ci = 0 for label, value in items: para_in_cell(cells[ci], label, bold=True, size=9, color=C_DARK_BLUE, bg=C_PALE_BLUE) para_in_cell(cells[ci+1], value, bold=False, size=9, bg=C_PALE_BLUE) ci += 2 detail_tbl = doc.add_table(rows=4, cols=4) detail_tbl.style = 'Table Grid' detail_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_col_widths(detail_tbl, [3.5, 5.5, 3.2, 5.8]) add_detail_row(detail_tbl, 0, [('Patient Name:', ''), ('Date:', '')]) add_detail_row(detail_tbl, 1, [('Age / Sex:', ' / ☐ M ☐ F ☐ Other'), ('Examiner:', '')]) add_detail_row(detail_tbl, 2, [('Hospital No.:', ''), ('Ward / OPD:', '')]) add_detail_row(detail_tbl, 3, [('Diagnosis / Complaint:', ''), ('Dominant Side:', '☐ Right ☐ Left')]) set_table_outer_border(detail_tbl) add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # Helper: section header row # ──────────────────────────────────────────────────────────────────────────── def add_section_header(table, title, color=C_MID_BLUE, ncols=None): """Merge first row across all cols and colour it as a section header.""" row = table.rows[0] if ncols is None: ncols = len(row.cells) # merge if ncols > 1: row.cells[0].merge(row.cells[ncols-1]) para_in_cell(row.cells[0], f' {title}', bold=True, size=9.5, color=C_WHITE, bg=color, space_before=4, space_after=4) def add_col_headers(table, row_idx, headers, bg=C_LIGHT_BLUE): row = table.rows[row_idx] for i, h in enumerate(headers): para_in_cell(row.cells[i], h, bold=True, size=8.5, align=WD_ALIGN_PARAGRAPH.CENTER, bg=bg) def style_data_rows(table, start_row=2): for i, row in enumerate(table.rows[start_row:], start=start_row): bg = C_WHITE if i % 2 == 0 else C_GREY for cell in row.cells: if not cell._tc.find(qn('w:shd')): set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER def fill_data_row(table, row_idx, cells_data, label_col=0): """cells_data: list of strings or (text, extra_lines) tuples.""" row = table.rows[row_idx] for j, data in enumerate(cells_data): cell = row.cells[j] if isinstance(data, tuple): text, extras = data[0], data[1:] para_in_cell(cell, text, bold=(j == label_col), size=9, color=C_DARK_BLUE if j==label_col else C_BLACK) for ex in extras: add_para_to_cell(cell, ex, size=8.5) else: para_in_cell(cell, str(data), bold=(j == label_col), size=9, color=C_DARK_BLUE if j==label_col else C_BLACK) # ──────────────────────────────────────────────────────────────────────────── # 1. INSPECTION (1 header + 1 col-hdr + 7 data rows = 9 rows, 4 cols) # ──────────────────────────────────────────────────────────────────────────── insp_tbl = doc.add_table(rows=9, cols=4) insp_tbl.style = 'Table Grid' insp_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_col_widths(insp_tbl, [4.0, 4.5, 4.5, 5.0]) add_section_header(insp_tbl, '1. INSPECTION (Patient supine, fully exposed, good lighting)') add_col_headers(insp_tbl, 1, ['Feature', 'Right', 'Left', 'Notes']) insp_data = [ ['Muscle Bulk', '☐ Normal ☐ Wasted ☐ Hypertrophy', '☐ Normal ☐ Wasted ☐ Hypertrophy', ''], ['Fasciculations', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Involuntary Movements', '☐ None ☐ Tremor ☐ Myoclonus\n☐ Other: ___', '☐ None ☐ Tremor ☐ Myoclonus\n☐ Other: ___', ''], ['Limb Posture', '☐ Normal ☐ Foot drop\n☐ Scissoring ☐ Other', '☐ Normal ☐ Foot drop\n☐ Scissoring ☐ Other', ''], ['Trophic / Skin Changes', '☐ Absent ☐ Present (describe):', '☐ Absent ☐ Present (describe):', ''], ['Scars / Deformity', '☐ None ☐ Present', '☐ None ☐ Present', ''], ['Limb Length', 'R: _______ cm', 'L: _______ cm', '☐ Symmetric ☐ Asymmetric'], ] for i, row_data in enumerate(insp_data): fill_data_row(insp_tbl, i+2, row_data) style_data_rows(insp_tbl, 2) set_table_outer_border(insp_tbl) add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # 2. TONE (1 header + 1 col-hdr + 6 data rows = 8 rows, 4 cols) # ──────────────────────────────────────────────────────────────────────────── tone_tbl = doc.add_table(rows=8, cols=4) tone_tbl.style = 'Table Grid' tone_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_col_widths(tone_tbl, [4.0, 4.5, 4.5, 5.0]) add_section_header(tone_tbl, '2. TONE (Passive movement; heel-drop test)') add_col_headers(tone_tbl, 1, ['Finding', 'Right', 'Left', 'Comment']) tone_data = [ ['Tone Character', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', ''], ['Cogwheel Rigidity', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Heel-Drop Test\n(rapid knee lift)', '☐ Normal heel drag\n☐ Immediate heel lift', '☐ Normal heel drag\n☐ Immediate heel lift', ''], ['Hip', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Knee', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Ankle', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ] for i, row_data in enumerate(tone_data): fill_data_row(tone_tbl, i+2, row_data) style_data_rows(tone_tbl, 2) set_table_outer_border(tone_tbl) add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # MRC LEGEND (coloured callout paragraph) # ──────────────────────────────────────────────────────────────────────────── mrc_leg = doc.add_table(rows=1, cols=1) mrc_leg.style = 'Table Grid' set_cell_bg(mrc_leg.rows[0].cells[0], C_ORANGE_LT) set_table_outer_border(mrc_leg, color=C_ACCENT, sz='4') set_col_widths(mrc_leg, [18.0]) cell = mrc_leg.rows[0].cells[0] p = cell.paragraphs[0] p.paragraph_format.space_before = Pt(3) p.paragraph_format.space_after = Pt(3) r = p.add_run('MRC Scale: ') r.bold = True; r.font.size = Pt(8.5); r.font.name = 'Calibri'; r.font.color.rgb = hex_rgb(C_DARK_BLUE) r2 = p.add_run('0 = No movement 1 = Flicker 2 = Gravity eliminated 3 = Against gravity ' '4⁻ = Mild resistance 4 = Moderate resistance 4⁺ = Strong resistance 5 = Full power') r2.font.size = Pt(8.5); r2.font.name = 'Calibri'; r2.font.color.rgb = hex_rgb('444444') add_space(doc, 2) # ──────────────────────────────────────────────────────────────────────────── # 3. POWER (1 header + 1 col-hdr + 12 data rows = 14 rows, 5 cols) # ──────────────────────────────────────────────────────────────────────────── pow_tbl = doc.add_table(rows=14, cols=5) pow_tbl.style = 'Table Grid' pow_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_col_widths(pow_tbl, [5.2, 1.5, 4.0, 4.0, 3.3]) add_section_header(pow_tbl, '3. POWER (MRC Grade 0–5; circle the grade for each side)') add_col_headers(pow_tbl, 1, ['Movement', 'Root', 'Right (circle)', 'Left (circle)', 'Notes']) power_rows = [ ['Hip Flexion (Iliopsoas)', 'L1–L3', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Hip Extension (Glut. Max.)', 'L4–S1', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Hip Abduction (Glut. Med.)', 'L4–S1', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Hip Adduction', 'L2–L4', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Knee Extension (Quadriceps)', 'L3–L4', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Knee Flexion (Hamstrings)', 'L5–S2', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Ankle Dorsiflexion (Tib. Ant.)','L4–L5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Ankle Plantarflexion', 'S1–S2', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Ankle Eversion (Peronei)', 'L5–S1', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Ankle Inversion (Tib. Post.)', 'L4–L5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Great Toe Extension (EHL)', 'L5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ['Toe Flexion', 'S1–S2', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', '0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5', ''], ] for i, row_data in enumerate(power_rows): fill_data_row(pow_tbl, i+2, row_data) style_data_rows(pow_tbl, 2) set_table_outer_border(pow_tbl) add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # REFLEX LEGEND # ──────────────────────────────────────────────────────────────────────────── ref_leg = doc.add_table(rows=1, cols=1) ref_leg.style = 'Table Grid' set_cell_bg(ref_leg.rows[0].cells[0], C_LT_GREEN) set_table_outer_border(ref_leg, color=C_GREEN, sz='4') set_col_widths(ref_leg, [18.0]) cell = ref_leg.rows[0].cells[0] p = cell.paragraphs[0] p.paragraph_format.space_before = Pt(3); p.paragraph_format.space_after = Pt(3) r = p.add_run('Reflex Grade: ') r.bold = True; r.font.size = Pt(8.5); r.font.name = 'Calibri'; r.font.color.rgb = hex_rgb(C_GREEN) r2 = p.add_run('0 = Absent 1 = Diminished 2 = Normal 3 = Brisk / Increased 4 = Clonus') r2.font.size = Pt(8.5); r2.font.name = 'Calibri'; r2.font.color.rgb = hex_rgb('444444') add_space(doc, 2) # ──────────────────────────────────────────────────────────────────────────── # 4. REFLEXES (header + col-hdr + 3 sub-headers + 8 data = 14 rows, 5 cols) # ──────────────────────────────────────────────────────────────────────────── ref_tbl = doc.add_table(rows=14, cols=5) ref_tbl.style = 'Table Grid' ref_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_col_widths(ref_tbl, [4.6, 1.5, 4.0, 4.0, 3.9]) add_section_header(ref_tbl, '4. REFLEXES') add_col_headers(ref_tbl, 1, ['Reflex', 'Root', 'Right', 'Left', 'Notes']) reflex_rows = [ ('subhdr', 'DEEP TENDON REFLEXES'), ['Knee Jerk (Patellar)', 'L3–L4', 'Grade: 0 1 2 3 4', 'Grade: 0 1 2 3 4', ''], ['Ankle Jerk (Achilles)', 'S1–S2', 'Grade: 0 1 2 3 4', 'Grade: 0 1 2 3 4', ''], ('subhdr', 'CLONUS'), ['Ankle Clonus', 'S1–S2', '☐ Absent ☐ Present\nBeats: ___', '☐ Absent ☐ Present\nBeats: ___', ''], ['Patellar Clonus', 'L2–L4', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ('subhdr', 'SUPERFICIAL / CUTANEOUS REFLEXES'), ['Plantar Reflex', 'S1', '☐ Flexor (normal)\n☐ Extensor (Babinski +ve)\n☐ No response', '☐ Flexor (normal)\n☐ Extensor (Babinski +ve)\n☐ No response', ''], ['Cremasteric', 'L1–L2', '☐ Present ☐ Absent', '☐ Present ☐ Absent', ''], ['Abdominal', 'T7–T12','☐ Present ☐ Absent', '☐ Present ☐ Absent', ''], ['Anal Reflex', 'S2–S4', '☐ Present ☐ Absent', '☐ Present ☐ Absent', ''], ] row_idx = 2 for item in reflex_rows: row = ref_tbl.rows[row_idx] if isinstance(item, tuple) and item[0] == 'subhdr': # sub-header: merge all cols, colour darker blue row.cells[0].merge(row.cells[4]) para_in_cell(row.cells[0], f' {item[1]}', bold=True, size=8.5, color=C_WHITE, bg=C_SUB_BLUE, space_before=3, space_after=3) else: fill_data_row(ref_tbl, row_idx, item) bg = C_WHITE if row_idx % 2 == 0 else C_GREY for j, cell in enumerate(row.cells): existing_shd = cell._tc.find(qn('w:shd')) if existing_shd is None: set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER row_idx += 1 set_table_outer_border(ref_tbl) add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # 5. COORDINATION (header + col-hdr + 4 data rows = 6 rows, 4 cols) # ──────────────────────────────────────────────────────────────────────────── coord_tbl = doc.add_table(rows=6, cols=4) coord_tbl.style = 'Table Grid' coord_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_col_widths(coord_tbl, [4.0, 4.8, 5.2, 4.0]) add_section_header(coord_tbl, '5. COORDINATION (Cerebellar / Proprioceptive Function)') add_col_headers(coord_tbl, 1, ['Test', 'Right', 'Left / Bilateral', 'Notes']) coord_data = [ ['Heel-Shin Test', '☐ Normal ☐ Ataxia ☐ Dysmetria', '☐ Normal ☐ Ataxia ☐ Dysmetria', ''], ['Foot Tapping (rapid)', '☐ Normal ☐ Bradykinesia\n☐ Dysdiadochokinesia', '☐ Normal ☐ Bradykinesia\n☐ Dysdiadochokinesia', ''], ['Foot Circle Drawing', '☐ Normal ☐ Dyspraxia ☐ Abnormal', '☐ Normal ☐ Dyspraxia ☐ Abnormal', ''], ["Romberg's Test", 'Eyes open: ☐ Stable ☐ Unsteady', 'Eyes closed: ☐ Stable ☐ Falls\n→ Result: ☐ +ve ☐ −ve', ''], ] for i, row_data in enumerate(coord_data): fill_data_row(coord_tbl, i+2, row_data) style_data_rows(coord_tbl, 2) set_table_outer_border(coord_tbl) add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # 6. GAIT (header + col-hdr + 1 sub-hdr + 8 data = 11 rows, 3 cols) # ──────────────────────────────────────────────────────────────────────────── gait_tbl = doc.add_table(rows=11, cols=3) gait_tbl.style = 'Table Grid' gait_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_col_widths(gait_tbl, [3.0, 8.0, 7.0]) add_section_header(gait_tbl, '6. GAIT (Examine last; observe before recumbent examination)') add_col_headers(gait_tbl, 1, ['', 'Gait Pattern', 'Finding / Notes']) # sub-header gait_tbl.rows[2].cells[0].merge(gait_tbl.rows[2].cells[2]) para_in_cell(gait_tbl.rows[2].cells[0], ' TICK ALL PATTERNS THAT APPLY', bold=True, size=8.5, color=C_WHITE, bg=C_SUB_BLUE, space_before=3, space_after=3) gait_data = [ ['☐ Normal', '', ''], ['☐ Hemiplegic (circumduction)', '☐ Spastic paraparesis (scissor)', ''], ['☐ Steppage / Foot drop', '☐ Waddling (proximal weakness)', ''], ['☐ Ataxic (broad-based)', '☐ Parkinsonian (shuffling / festination)', ''], ['☐ Antalgic (pain-avoidance)', '☐ Trendelenburg (hip abductor weakness)', ''], ['☐ Functional / Non-organic', '☐ Other: ___________________', ''], ['Assistive Device', '☐ None ☐ Walking stick ☐ Frame ☐ Crutches ☐ Wheelchair', ''], ['Additional Observations', '', ''], ] for i, row_data in enumerate(gait_data): ri = i + 3 row = gait_tbl.rows[ri] fill_data_row(gait_tbl, ri, row_data) bg = C_WHITE if ri % 2 == 0 else C_GREY for cell in row.cells: if cell._tc.find(qn('w:shd')) is None: set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER set_table_outer_border(gait_tbl) add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # 7. UMN vs LMN SUMMARY # ──────────────────────────────────────────────────────────────────────────── summ_tbl = doc.add_table(rows=10, cols=4) summ_tbl.style = 'Table Grid' summ_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_col_widths(summ_tbl, [4.0, 4.8, 4.8, 4.4]) add_section_header(summ_tbl, '7. PATTERN SUMMARY (UMN vs LMN)', color=C_GREEN) add_col_headers(summ_tbl, 1, ['Feature', 'UMN (Central)', 'LMN (Peripheral)', 'Patient Findings']) summ_data = [ ['Muscle Bulk', 'Normal / mild disuse atrophy', 'Marked wasting', ''], ['Fasciculations', 'Absent', 'Present', ''], ['Tone', 'Increased (spasticity)', 'Decreased (flaccidity)', ''], ['Power', 'Reduced (pyramidal pattern)', 'Reduced (root/nerve)', ''], ['Deep Reflexes', 'Hyperreflexia', 'Diminished / Absent', ''], ['Plantar Reflex', 'Extensor (Babinski +ve)', 'Flexor / Absent', ''], ['Clonus', 'May be present', 'Absent', ''], ['Superficial Reflex','Absent', 'Present / Absent', ''], ] for i, row_data in enumerate(summ_data): fill_data_row(summ_tbl, i+2, row_data) style_data_rows(summ_tbl, 2) set_table_outer_border(summ_tbl) add_space(doc, 2) # Pattern selector pat_tbl = doc.add_table(rows=1, cols=2) pat_tbl.style = 'Table Grid' set_col_widths(pat_tbl, [5.0, 13.0]) set_cell_bg(pat_tbl.rows[0].cells[0], C_ORANGE_LT) set_cell_bg(pat_tbl.rows[0].cells[1], C_ORANGE_LT) set_table_outer_border(pat_tbl, color=C_ACCENT, sz='6') para_in_cell(pat_tbl.rows[0].cells[0], 'Overall Clinical Pattern:', bold=True, size=9, color=C_DARK_BLUE) para_in_cell(pat_tbl.rows[0].cells[1], '☐ Normal ☐ UMN ☐ LMN ☐ Mixed ☐ Cerebellar ☐ Extrapyramidal ☐ Functional', size=9) add_space(doc, 4) # ──────────────────────────────────────────────────────────────────────────── # 8. CLINICAL NOTES # ──────────────────────────────────────────────────────────────────────────── notes_tbl = doc.add_table(rows=9, cols=1) notes_tbl.style = 'Table Grid' set_col_widths(notes_tbl, [18.0]) add_section_header(notes_tbl, '8. CLINICAL SUMMARY & PLAN') notes_fields = [ ('Impression / Provisional Diagnosis:', True), ('', False), ('', False), ('Further Investigations Requested:', True), ('', False), ('Management / Referral Plan:', True), ('', False), ('', False), ] for i, (label, is_label) in enumerate(notes_fields): cell = notes_tbl.rows[i+1].cells[0] bg = C_PALE_BLUE if is_label else C_WHITE set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER para_in_cell(cell, label, bold=is_label, size=9, color=C_DARK_BLUE if is_label else C_BLACK) set_table_outer_border(notes_tbl) add_space(doc, 5) # Signature sig_tbl = doc.add_table(rows=1, cols=3) sig_tbl.style = 'Table Grid' set_col_widths(sig_tbl, [6.5, 6.5, 5.0]) para_in_cell(sig_tbl.rows[0].cells[0], 'Examiner Signature: ___________________', size=9) para_in_cell(sig_tbl.rows[0].cells[1], 'Designation: _______________________', size=9) para_in_cell(sig_tbl.rows[0].cells[2], 'Date: ________________', size=9) set_table_outer_border(sig_tbl, color=C_MID_GREY, sz='4') add_space(doc, 3) # Footer paragraph footer_p = doc.add_paragraph() footer_p.paragraph_format.space_before = Pt(2) footer_p.paragraph_format.space_after = Pt(0) pPr = footer_p._p.get_or_add_pPr() pBdr = OxmlElement('w:pBdr') top_el = OxmlElement('w:top') top_el.set(qn('w:val'), 'single'); top_el.set(qn('w:sz'), '4') top_el.set(qn('w:space'), '1'); top_el.set(qn('w:color'), 'CCCCCC') pBdr.append(top_el) pPr.append(pBdr) r_left = footer_p.add_run('Sequence: Inspection → Tone → Power → Reflexes → Coordination → Gait') r_left.italic = True; r_left.font.size = Pt(7.5); r_left.font.name = 'Calibri' r_left.font.color.rgb = hex_rgb('888888') r_tab = footer_p.add_run(' ') r_right = footer_p.add_run("Harrison's 22E · Bradley & Daroff's Neurology · Das Manual of Clinical Surgery") r_right.italic = True; r_right.font.size = Pt(7.5); r_right.font.name = 'Calibri' r_right.font.color.rgb = hex_rgb('AAAAAA') # ── Save ───────────────────────────────────────────────────────────────────── out_path = '/tmp/workspace/motor-assessment/Lower_Limb_Motor_Assessment.docx' doc.save(out_path) print(f"DOCX saved: {out_path}") PYEOF

Your Word document is ready:

Lower Limb Motor Assessment

Word Document · DOCX


What's in the .docx (digital note-taking optimised):
  • Fully editable tables - click any cell and type directly; the checkbox symbols (☐) can be replaced with ☑ when ticked
  • All 8 sections match the PDF exactly: Inspection → Tone → Power → Reflexes → Coordination → Gait → UMN/LMN Summary → Clinical Notes
  • Colour-coded headers - dark blue banner, section headers, green for the UMN/LMN summary, orange for the MRC and reflex grade legend strips
  • Notes columns throughout for free-text clinical findings
  • MRC grade rows show 0 / 1 / 2 / 3 / 4⁻/ 4 / 4⁺/ 5 - just bold or highlight the chosen grade digitally
  • Signature block and footer with exam sequence reminder
Tips for digital use in Word:
  • Use Ctrl+H (Find & Replace) to swap for checked items
  • Add comments (Ctrl+Alt+M) to flag findings for follow-up
  • Use Track Changes for collaborative review

SIMILARLY MAKE A WORD FILE FOR UPPER LIMB EXAMINATION

Finding Sources
Good — I already have all the clinical content from the previous session (Harrison's 22E, Bradley & Daroff's Neurology, Das Manual). I'll build the Word file directly using that knowledge plus the same design template.
Running Command

python3 << 'PYEOF' from docx import Document from docx.shared import Pt, Cm, RGBColor 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 # ── colours ─────────────────────────────────────────────────────────────────── def hex_rgb(h): h = h.lstrip('#') return RGBColor(int(h[0:2],16), int(h[2:4],16), int(h[4:6],16)) C_DARK_BLUE = '1A3A5C' C_MID_BLUE = '2E6DA4' C_LIGHT_BLUE= 'D6E8F7' C_PALE_BLUE = 'EEF5FB' C_ACCENT = 'E8821A' C_GREEN = '2E7D32' C_LT_GREEN = 'EEF8EE' C_GREY = 'F2F4F7' C_MID_GREY = 'CCCCCC' C_SUB_BLUE = '3A7EBF' C_ORANGE_LT = 'FFF4E5' C_WHITE = 'FFFFFF' C_BLACK = '000000' # ── XML 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_table_outer_border(table, color=C_MID_BLUE, sz='8'): for row in table.rows: for i, cell in enumerate(row.cells): is_first_row = (row == table.rows[0]) is_last_row = (row == table.rows[-1]) is_first_col = (i == 0) is_last_col = (i == len(row.cells)-1) tc = cell._tc; tcPr = tc.get_or_add_tcPr() tcBorders = OxmlElement('w:tcBorders') for side in ['top','bottom','left','right']: el = OxmlElement(f'w:{side}') outer = ((side=='top' and is_first_row) or (side=='bottom' and is_last_row) or (side=='left' and is_first_col) or (side=='right' and is_last_col)) el.set(qn('w:val'), 'single') el.set(qn('w:sz'), sz if outer else '2') el.set(qn('w:space'),'0') el.set(qn('w:color'), color if outer else 'CCCCCC') tcBorders.append(el) tcPr.append(tcBorders) def set_col_widths(table, widths_cm): for row in table.rows: for j, cell in enumerate(row.cells): tc = cell._tc; tcPr = tc.get_or_add_tcPr() tcW = OxmlElement('w:tcW') tcW.set(qn('w:w'), str(int(widths_cm[j]*567))) tcW.set(qn('w:type'),'dxa'); tcPr.append(tcW) def para_in_cell(cell, text, bold=False, italic=False, size=9, color=C_BLACK, align=WD_ALIGN_PARAGRAPH.LEFT, bg=None, space_before=0, space_after=0): if bg: set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER p = cell.paragraphs[0] if cell.paragraphs else cell.add_paragraph() p.clear(); p.alignment = align p.paragraph_format.space_before = Pt(space_before) p.paragraph_format.space_after = Pt(space_after) run = p.add_run(text); run.bold=bold; run.italic=italic run.font.size=Pt(size); run.font.color.rgb=hex_rgb(color); run.font.name='Calibri' return p def add_para_to_cell(cell, text, bold=False, size=9, color=C_BLACK): p = cell.add_paragraph(); p.alignment = WD_ALIGN_PARAGRAPH.LEFT p.paragraph_format.space_before=Pt(0); p.paragraph_format.space_after=Pt(0) run = p.add_run(text); run.bold=bold; run.font.size=Pt(size) run.font.color.rgb=hex_rgb(color); run.font.name='Calibri' def add_space(doc, pt=4): p = doc.add_paragraph() p.paragraph_format.space_before=Pt(0); p.paragraph_format.space_after=Pt(pt) def add_section_header(table, title, color=C_MID_BLUE, ncols=None): row = table.rows[0] if ncols is None: ncols = len(row.cells) if ncols > 1: row.cells[0].merge(row.cells[ncols-1]) para_in_cell(row.cells[0], f' {title}', bold=True, size=9.5, color=C_WHITE, bg=color, space_before=4, space_after=4) def add_col_headers(table, row_idx, headers, bg=C_LIGHT_BLUE): row = table.rows[row_idx] for i, h in enumerate(headers): para_in_cell(row.cells[i], h, bold=True, size=8.5, align=WD_ALIGN_PARAGRAPH.CENTER, bg=bg) def style_data_rows(table, start_row=2): for i, row in enumerate(table.rows[start_row:], start=start_row): bg = C_WHITE if i % 2 == 0 else C_GREY for cell in row.cells: if cell._tc.find(qn('w:shd')) is None: set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER def fill_data_row(table, row_idx, cells_data, label_col=0): row = table.rows[row_idx] for j, data in enumerate(cells_data): cell = row.cells[j] if isinstance(data, (list, tuple)): lines = list(data) para_in_cell(cell, lines[0], bold=(j==label_col), size=9, color=C_DARK_BLUE if j==label_col else C_BLACK) for ln in lines[1:]: add_para_to_cell(cell, ln, size=8.5) else: para_in_cell(cell, str(data), bold=(j==label_col), size=9, color=C_DARK_BLUE if j==label_col else C_BLACK) def make_legend(doc, label, text, bg, border_color): tbl = doc.add_table(rows=1, cols=1) tbl.style = 'Table Grid' set_col_widths(tbl, [18.0]) set_cell_bg(tbl.rows[0].cells[0], bg) set_table_outer_border(tbl, color=border_color, sz='4') cell = tbl.rows[0].cells[0] p = cell.paragraphs[0] p.paragraph_format.space_before=Pt(3); p.paragraph_format.space_after=Pt(3) r1 = p.add_run(label+': ') r1.bold=True; r1.font.size=Pt(8.5); r1.font.name='Calibri' r1.font.color.rgb=hex_rgb(C_DARK_BLUE) r2 = p.add_run(text) r2.font.size=Pt(8.5); r2.font.name='Calibri'; r2.font.color.rgb=hex_rgb('444444') return tbl # ═════════════════════════════════════════════════════════════════════════════ # DOCUMENT # ═════════════════════════════════════════════════════════════════════════════ doc = Document() for sec in doc.sections: sec.page_width=Cm(21); sec.page_height=Cm(29.7) sec.left_margin=Cm(1.5); sec.right_margin=Cm(1.5) sec.top_margin=Cm(1.5); sec.bottom_margin=Cm(1.5) doc.styles['Normal'].font.name='Calibri'; doc.styles['Normal'].font.size=Pt(10) # ── BANNER ──────────────────────────────────────────────────────────────────── banner = doc.add_table(rows=2, cols=1) banner.alignment=WD_TABLE_ALIGNMENT.CENTER; banner.style='Table Grid' set_cell_bg(banner.rows[0].cells[0], C_DARK_BLUE) set_cell_bg(banner.rows[1].cells[0], C_DARK_BLUE) para_in_cell(banner.rows[0].cells[0], 'MOTOR ASSESSMENT OF THE UPPER LIMBS', bold=True, size=16, color=C_WHITE, align=WD_ALIGN_PARAGRAPH.CENTER, space_before=6, space_after=2) para_in_cell(banner.rows[1].cells[0], 'Neurological Examination Proforma · Patient Record / Documentation', size=10, color='BFD9F2', align=WD_ALIGN_PARAGRAPH.CENTER, space_before=2, space_after=6) set_table_outer_border(banner, color=C_MID_BLUE, sz='12') add_space(doc, 4) # ── PATIENT DETAILS ─────────────────────────────────────────────────────────── det = doc.add_table(rows=4, cols=4) det.style='Table Grid'; det.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(det, [3.5, 5.5, 3.2, 5.8]) rows_det = [ [('Patient Name:',''), ('Date:','')], [('Age / Sex:',' / ☐ M ☐ F ☐ Other'), ('Examiner:','')], [('Hospital No.:',''), ('Ward / OPD:','')], [('Diagnosis / Complaint:',''), ('Dominant Side:','☐ Right ☐ Left')], ] for ri, pairs in enumerate(rows_det): cells = det.rows[ri].cells ci = 0 for label, val in pairs: para_in_cell(cells[ci], label, bold=True, size=9, color=C_DARK_BLUE, bg=C_PALE_BLUE) para_in_cell(cells[ci+1], val, bold=False, size=9, bg=C_PALE_BLUE) ci += 2 set_table_outer_border(det) add_space(doc, 4) # ═════════════════════════════════════════════════════════════════════════════ # 1. INSPECTION # ═════════════════════════════════════════════════════════════════════════════ insp = doc.add_table(rows=11, cols=4) insp.style='Table Grid'; insp.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(insp, [4.0, 4.5, 4.5, 5.0]) add_section_header(insp, '1. INSPECTION (Patient seated, arms fully exposed, good lighting)') add_col_headers(insp, 1, ['Feature', 'Right', 'Left', 'Notes']) insp_data = [ ['Muscle Bulk', '☐ Normal ☐ Wasted ☐ Hypertrophy', '☐ Normal ☐ Wasted ☐ Hypertrophy', ''], ['Fasciculations', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Involuntary Movements', '☐ None ☐ Tremor (rest)\n☐ Tremor (postural/action) ☐ Other', '☐ None ☐ Tremor (rest)\n☐ Tremor (postural/action) ☐ Other', ''], ['Pronator Drift\n(arms extended, eyes closed)', '☐ Absent ☐ Present\n☐ Pronation ☐ Elbow flexion', '☐ Absent ☐ Present\n☐ Pronation ☐ Elbow flexion', ''], ['Posture / Deformity', '☐ Normal ☐ Wrist drop\n☐ Claw hand ☐ Other', '☐ Normal ☐ Wrist drop\n☐ Claw hand ☐ Other', ''], ['Trophic / Skin Changes', '☐ Absent ☐ Present (describe):', '☐ Absent ☐ Present (describe):', ''], ['Scars / Deformity', '☐ None ☐ Present', '☐ None ☐ Present', ''], ['Limb Circumference\n(mid-upper arm)', 'R: _______ cm', 'L: _______ cm', '☐ Symmetric ☐ Asymmetric'], ['Thenar / Hypothenar\nWasting', '☐ Normal ☐ Wasted', '☐ Normal ☐ Wasted', ''], ] for i, r in enumerate(insp_data): fill_data_row(insp, i+2, r) style_data_rows(insp, 2) set_table_outer_border(insp) add_space(doc, 4) # ═════════════════════════════════════════════════════════════════════════════ # 2. TONE # ═════════════════════════════════════════════════════════════════════════════ tone = doc.add_table(rows=9, cols=4) tone.style='Table Grid'; tone.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(tone, [4.0, 4.5, 4.5, 5.0]) add_section_header(tone, '2. TONE (Passive movement; patient relaxed and distracted)') add_col_headers(tone, 1, ['Finding', 'Right', 'Left', 'Comment']) tone_data = [ ['Tone Character', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', ''], ['Cogwheel Rigidity\n(at wrist)', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Lead-Pipe Rigidity', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ['Forearm\n(pronation/supination)', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Elbow\n(flexion/extension)', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Wrist\n(flexion/extension)', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ['Shoulder', '☐ Normal ☐ ↑ ☐ ↓', '☐ Normal ☐ ↑ ☐ ↓', ''], ] for i, r in enumerate(tone_data): fill_data_row(tone, i+2, r) style_data_rows(tone, 2) set_table_outer_border(tone) add_space(doc, 4) # ═════════════════════════════════════════════════════════════════════════════ # MRC Legend + 3. POWER # ═════════════════════════════════════════════════════════════════════════════ mrc_leg = make_legend(doc, 'MRC Scale', '0 = No movement 1 = Flicker 2 = Gravity eliminated 3 = Against gravity ' '4⁻ = Mild resistance 4 = Moderate resistance 4⁺ = Strong resistance 5 = Full power', C_ORANGE_LT, C_ACCENT) add_space(doc, 2) pow_tbl = doc.add_table(rows=18, cols=5) pow_tbl.style='Table Grid'; pow_tbl.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(pow_tbl, [5.2, 1.5, 4.0, 4.0, 3.3]) add_section_header(pow_tbl, '3. POWER (MRC Grade 0–5; circle the grade for each side)') add_col_headers(pow_tbl, 1, ['Movement', 'Root', 'Right (circle)', 'Left (circle)', 'Notes']) power_rows = [ # Shoulder ('subhdr', 'SHOULDER'), ['Shoulder Flexion (Ant. Deltoid)', 'C5–C6', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Shoulder Abduction (Deltoid)', 'C5–C6', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Shoulder Extension (Post. Deltoid)', 'C5–C6', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], # Elbow ('subhdr', 'ELBOW'), ['Elbow Flexion (Biceps)', 'C5–C6', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Elbow Extension (Triceps)', 'C6–C8', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], # Wrist ('subhdr', 'WRIST & FOREARM'), ['Wrist Extension (Ext. Carpi)', 'C6–C7', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Wrist Flexion (Flex. Carpi)', 'C7–C8', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Forearm Supination', 'C5–C6', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Forearm Pronation', 'C6–C7', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], # Hand ('subhdr', 'HAND & FINGERS'), ['Finger Extension (Ext. Digitorum)', 'C7–C8', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Finger Flexion (FDP/FDS)', 'C7–T1', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Finger Abduction (Interossei)', 'T1', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ['Thumb Abduction (Abd. Poll. Br.)', 'C8–T1', '0/1/2/3/4⁻/4/4⁺/5', '0/1/2/3/4⁻/4/4⁺/5', ''], ] ri = 2 for item in power_rows: row = pow_tbl.rows[ri] if isinstance(item, tuple) and item[0]=='subhdr': row.cells[0].merge(row.cells[4]) para_in_cell(row.cells[0], f' {item[1]}', bold=True, size=8.5, color=C_WHITE, bg=C_SUB_BLUE, space_before=3, space_after=3) else: fill_data_row(pow_tbl, ri, item) bg = C_WHITE if ri % 2 == 0 else C_GREY for cell in row.cells: if cell._tc.find(qn('w:shd')) is None: set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER ri += 1 set_table_outer_border(pow_tbl) add_space(doc, 4) # ═════════════════════════════════════════════════════════════════════════════ # Reflex Legend + 4. REFLEXES # ═════════════════════════════════════════════════════════════════════════════ ref_leg = make_legend(doc, 'Reflex Grade', '0 = Absent 1 = Diminished 2 = Normal 3 = Brisk / Increased 4 = Clonus', C_LT_GREEN, C_GREEN) add_space(doc, 2) ref_tbl = doc.add_table(rows=12, cols=5) ref_tbl.style='Table Grid'; ref_tbl.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(ref_tbl, [4.6, 1.5, 4.0, 4.0, 3.9]) add_section_header(ref_tbl, '4. REFLEXES') add_col_headers(ref_tbl, 1, ['Reflex', 'Root', 'Right', 'Left', 'Notes']) reflex_rows = [ ('subhdr', 'DEEP TENDON REFLEXES'), ['Biceps Jerk', 'C5–C6', 'Grade: 0 1 2 3 4', 'Grade: 0 1 2 3 4', ''], ['Brachioradialis', 'C5–C6', 'Grade: 0 1 2 3 4', 'Grade: 0 1 2 3 4', ''], ['Triceps Jerk', 'C6–C7', 'Grade: 0 1 2 3 4', 'Grade: 0 1 2 3 4', ''], ['Finger Flexor Jerk','C8–T1', 'Grade: 0 1 2 3 4', 'Grade: 0 1 2 3 4', ''], ('subhdr', 'PATHOLOGICAL / SPECIAL REFLEXES'), ['Hoffmann Sign\n(digital tap C8–T1)', 'C8–T1', '☐ Absent ☐ Present\n(thumb flexion = UMN)', '☐ Absent ☐ Present', ''], ['Finger Escape\n(Wartenberg sign)', 'C8–T1', '☐ Absent ☐ Present', '☐ Absent ☐ Present', ''], ('subhdr', 'SUPERFICIAL / CUTANEOUS REFLEXES'), ['Abdominal Reflex\n(upper T7–T9)', 'T7–T9', '☐ Present ☐ Absent', '☐ Present ☐ Absent', ''], ['Palmomental Reflex', 'Frontal', '☐ Absent ☐ Present (frontal lobe sign)', '', ''], ] ri = 2 for item in reflex_rows: row = ref_tbl.rows[ri] if isinstance(item, tuple) and item[0]=='subhdr': row.cells[0].merge(row.cells[4]) para_in_cell(row.cells[0], f' {item[1]}', bold=True, size=8.5, color=C_WHITE, bg=C_SUB_BLUE, space_before=3, space_after=3) else: fill_data_row(ref_tbl, ri, item) bg = C_WHITE if ri % 2 == 0 else C_GREY for cell in row.cells: if cell._tc.find(qn('w:shd')) is None: set_cell_bg(cell, bg) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER ri += 1 set_table_outer_border(ref_tbl) add_space(doc, 4) # ═════════════════════════════════════════════════════════════════════════════ # 5. COORDINATION # ═════════════════════════════════════════════════════════════════════════════ coord = doc.add_table(rows=8, cols=4) coord.style='Table Grid'; coord.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(coord, [4.0, 4.8, 5.2, 4.0]) add_section_header(coord, '5. COORDINATION (Cerebellar / Proprioceptive Function)') add_col_headers(coord, 1, ['Test', 'Right', 'Left / Bilateral', 'Notes']) coord_data = [ ['Finger–Nose Test', '☐ Normal ☐ Dysmetria\n☐ Intention tremor', '☐ Normal ☐ Dysmetria\n☐ Intention tremor', ''], ['Rapid Alternating\nMovements (RAM)\n(dysdiadochokinesia)', '☐ Normal ☐ Irregular\n☐ Slow', '☐ Normal ☐ Irregular\n☐ Slow', ''], ['Rebound Test\n(cerebellar check reflex)', '☐ Normal ☐ Rebound +ve', '☐ Normal ☐ Rebound +ve', ''], ['Grip / Pinch Strength\n(subjective)', '☐ Normal ☐ Weak ☐ Very weak', '☐ Normal ☐ Weak ☐ Very weak', ''], ['Fine Motor / Dexterity\n(button/coin test)', '☐ Normal ☐ Impaired', '☐ Normal ☐ Impaired', ''], ['Writing Sample', '☐ Normal ☐ Micrographia\n☐ Tremulous ☐ Dyspraxic', '', ''], ] for i, r in enumerate(coord_data): fill_data_row(coord, i+2, r) style_data_rows(coord, 2) set_table_outer_border(coord) add_space(doc, 4) # ═════════════════════════════════════════════════════════════════════════════ # 6. SPECIAL TESTS (functional / screening) # ═════════════════════════════════════════════════════════════════════════════ spec = doc.add_table(rows=8, cols=4) spec.style='Table Grid'; spec.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(spec, [4.5, 4.3, 4.3, 4.9]) add_section_header(spec, '6. SPECIAL / SCREENING TESTS', color=C_SUB_BLUE) add_col_headers(spec, 1, ['Test', 'Right', 'Left', 'Significance']) spec_data = [ ['Pronator Drift\n(10 s, eyes closed)', '☐ Absent ☐ Present', '☐ Absent ☐ Present', 'UMN weakness screen'], ['Thumb Abduction\n(opponens / thenar)', '☐ Normal ☐ Weak', '☐ Normal ☐ Weak', 'Median nerve (T1); CTS'], ['Finger Abduction\n(index–little)', '☐ Normal ☐ Weak', '☐ Normal ☐ Weak', 'Ulnar nerve (T1)'], ['Wrist/Finger Extension', '☐ Normal ☐ Weak', '☐ Normal ☐ Weak', 'Radial nerve (C7); posterior cord'], ['Power Grip\n(hand-shake grip)', '☐ Normal ☐ Weak', '☐ Normal ☐ Weak', 'Overall hand strength'], ['Bulk: Thenar / Hypothenar', '☐ Normal ☐ Wasted', '☐ Normal ☐ Wasted', 'Median / Ulnar nerve'], ] for i, r in enumerate(spec_data): fill_data_row(spec, i+2, r) style_data_rows(spec, 2) set_table_outer_border(spec) add_space(doc, 4) # ═════════════════════════════════════════════════════════════════════════════ # 7. UMN vs LMN SUMMARY # ═════════════════════════════════════════════════════════════════════════════ summ = doc.add_table(rows=10, cols=4) summ.style='Table Grid'; summ.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(summ, [4.0, 4.8, 4.8, 4.4]) add_section_header(summ, '7. PATTERN SUMMARY (UMN vs LMN)', color=C_GREEN) add_col_headers(summ, 1, ['Feature', 'UMN (Central)', 'LMN (Peripheral)', 'Patient Findings']) summ_data = [ ['Muscle Bulk', 'Normal / mild disuse atrophy', 'Marked wasting', ''], ['Fasciculations', 'Absent', 'Present', ''], ['Tone', 'Increased (spasticity)', 'Decreased (flaccidity)', ''], ['Pronator Drift', 'Present (corticospinal)', 'Absent (unless weak)', ''], ['Deep Reflexes', 'Hyperreflexia', 'Diminished / Absent', ''], ['Hoffmann Sign', 'Positive', 'Negative', ''], ['Power', 'Reduced (pyramidal pattern)', 'Reduced (root/nerve)', ''], ['Coordination', 'May be affected (UMN)', 'Intact (unless sensory)', ''], ] for i, r in enumerate(summ_data): fill_data_row(summ, i+2, r) style_data_rows(summ, 2) set_table_outer_border(summ) add_space(doc, 2) # Pattern selector pat = doc.add_table(rows=1, cols=2) pat.style='Table Grid' set_col_widths(pat, [5.0, 13.0]) set_cell_bg(pat.rows[0].cells[0], C_ORANGE_LT) set_cell_bg(pat.rows[0].cells[1], C_ORANGE_LT) set_table_outer_border(pat, color=C_ACCENT, sz='6') para_in_cell(pat.rows[0].cells[0], 'Overall Clinical Pattern:', bold=True, size=9, color=C_DARK_BLUE) para_in_cell(pat.rows[0].cells[1], '☐ Normal ☐ UMN ☐ LMN ☐ Mixed ☐ Cerebellar ☐ Extrapyramidal ☐ Functional', size=9) add_space(doc, 4) # ═════════════════════════════════════════════════════════════════════════════ # 8. CLINICAL NOTES + SIGNATURE # ═════════════════════════════════════════════════════════════════════════════ notes = doc.add_table(rows=9, cols=1) notes.style='Table Grid' set_col_widths(notes, [18.0]) add_section_header(notes, '8. CLINICAL SUMMARY & PLAN') notes_fields = [ ('Impression / Provisional Diagnosis:', True), ('', False), ('', False), ('Further Investigations Requested:', True), ('', False), ('Management / Referral Plan:', True), ('', False), ('', False), ] for i, (label, is_label) in enumerate(notes_fields): cell = notes.rows[i+1].cells[0] set_cell_bg(cell, C_PALE_BLUE if is_label else C_WHITE) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER para_in_cell(cell, label, bold=is_label, size=9, color=C_DARK_BLUE if is_label else C_BLACK) set_table_outer_border(notes) add_space(doc, 5) # Signature sig = doc.add_table(rows=1, cols=3) sig.style='Table Grid' set_col_widths(sig, [6.5, 6.5, 5.0]) para_in_cell(sig.rows[0].cells[0], 'Examiner Signature: ___________________', size=9) para_in_cell(sig.rows[0].cells[1], 'Designation: _______________________', size=9) para_in_cell(sig.rows[0].cells[2], 'Date: ________________', size=9) set_table_outer_border(sig, color=C_MID_GREY, sz='4') add_space(doc, 3) # Footer footer_p = doc.add_paragraph() footer_p.paragraph_format.space_before=Pt(2); footer_p.paragraph_format.space_after=Pt(0) pPr = footer_p._p.get_or_add_pPr() pBdr = OxmlElement('w:pBdr') top_el = OxmlElement('w:top') top_el.set(qn('w:val'),'single'); top_el.set(qn('w:sz'),'4') top_el.set(qn('w:space'),'1'); top_el.set(qn('w:color'),'CCCCCC') pBdr.append(top_el); pPr.append(pBdr) r1 = footer_p.add_run('Sequence: Inspection → Tone → Power → Reflexes → Coordination → Special Tests') r1.italic=True; r1.font.size=Pt(7.5); r1.font.name='Calibri'; r1.font.color.rgb=hex_rgb('888888') r2 = footer_p.add_run(' ') r3 = footer_p.add_run("Harrison's 22E · Bradley & Daroff's Neurology · Das Manual of Clinical Surgery") r3.italic=True; r3.font.size=Pt(7.5); r3.font.name='Calibri'; r3.font.color.rgb=hex_rgb('AAAAAA') # ── Save ────────────────────────────────────────────────────────────────────── out = '/tmp/workspace/motor-assessment/Upper_Limb_Motor_Assessment.docx' doc.save(out) print(f"Saved: {out}") PYEOF

The power table has more sub-headers than rows allocated. Let me count and fix.
Running Command

python3 -c " power_rows = [ ('subhdr', 'SHOULDER'), ['Shoulder Flexion','C5-C6','','',''], ['Shoulder Abduction','C5-C6','','',''], ['Shoulder Extension','C5-C6','','',''], ('subhdr', 'ELBOW'), ['Elbow Flexion','C5-C6','','',''], ['Elbow Extension','C6-C8','','',''], ('subhdr', 'WRIST & FOREARM'), ['Wrist Extension','C6-C7','','',''], ['Wrist Flexion','C7-C8','','',''], ['Forearm Supination','C5-C6','','',''], ['Forearm Pronation','C6-C7','','',''], ('subhdr', 'HAND & FINGERS'), ['Finger Extension','C7-C8','','',''], ['Finger Flexion','C7-T1','','',''], ['Finger Abduction','T1','','',''], ['Thumb Abduction','C8-T1','','',''], ] print(f'power_rows count: {len(power_rows)}') print(f'rows needed (header+colhdr+data): {2 + len(power_rows)}') "

Power table needs 19 rows (was 18), reflex table similarly. Let me recount all tables and fix:
Running Command

python3 << 'PYEOF' from docx import Document from docx.shared import Pt, Cm, RGBColor 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 def hex_rgb(h): h = h.lstrip('#') return RGBColor(int(h[0:2],16), int(h[2:4],16), int(h[4:6],16)) C_DARK_BLUE='1A3A5C'; C_MID_BLUE='2E6DA4'; C_LIGHT_BLUE='D6E8F7'; C_PALE_BLUE='EEF5FB' C_ACCENT='E8821A'; C_GREEN='2E7D32'; C_LT_GREEN='EEF8EE'; C_GREY='F2F4F7' C_MID_GREY='CCCCCC'; C_SUB_BLUE='3A7EBF'; C_ORANGE_LT='FFF4E5'; C_WHITE='FFFFFF'; C_BLACK='000000' 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_table_outer_border(table, color=C_MID_BLUE, sz='8'): for row in table.rows: for i, cell in enumerate(row.cells): is_first_row=(row==table.rows[0]); is_last_row=(row==table.rows[-1]) is_first_col=(i==0); is_last_col=(i==len(row.cells)-1) tc=cell._tc; tcPr=tc.get_or_add_tcPr() tcBorders=OxmlElement('w:tcBorders') for side in ['top','bottom','left','right']: el=OxmlElement(f'w:{side}') outer=((side=='top' and is_first_row) or (side=='bottom' and is_last_row) or (side=='left' and is_first_col) or (side=='right' and is_last_col)) el.set(qn('w:val'),'single'); el.set(qn('w:sz'),sz if outer else '2') el.set(qn('w:space'),'0'); el.set(qn('w:color'),color if outer else 'CCCCCC') tcBorders.append(el) tcPr.append(tcBorders) def set_col_widths(table, widths_cm): for row in table.rows: for j, cell in enumerate(row.cells): tc=cell._tc; tcPr=tc.get_or_add_tcPr() tcW=OxmlElement('w:tcW') tcW.set(qn('w:w'),str(int(widths_cm[j]*567))); tcW.set(qn('w:type'),'dxa') tcPr.append(tcW) def para_in_cell(cell, text, bold=False, italic=False, size=9, color=C_BLACK, align=WD_ALIGN_PARAGRAPH.LEFT, bg=None, space_before=0, space_after=0): if bg: set_cell_bg(cell, bg) cell.vertical_alignment=WD_ALIGN_VERTICAL.CENTER p=cell.paragraphs[0] if cell.paragraphs else cell.add_paragraph() p.clear(); p.alignment=align p.paragraph_format.space_before=Pt(space_before); p.paragraph_format.space_after=Pt(space_after) run=p.add_run(text); run.bold=bold; run.italic=italic run.font.size=Pt(size); run.font.color.rgb=hex_rgb(color); run.font.name='Calibri' def add_para_to_cell(cell, text, bold=False, size=9, color=C_BLACK): p=cell.add_paragraph(); p.alignment=WD_ALIGN_PARAGRAPH.LEFT p.paragraph_format.space_before=Pt(0); p.paragraph_format.space_after=Pt(0) run=p.add_run(text); run.bold=bold; run.font.size=Pt(size) run.font.color.rgb=hex_rgb(color); run.font.name='Calibri' def add_space(doc, pt=4): p=doc.add_paragraph() p.paragraph_format.space_before=Pt(0); p.paragraph_format.space_after=Pt(pt) def add_section_header(table, title, color=C_MID_BLUE): ncols=len(table.rows[0].cells) if ncols>1: table.rows[0].cells[0].merge(table.rows[0].cells[ncols-1]) para_in_cell(table.rows[0].cells[0], f' {title}', bold=True, size=9.5, color=C_WHITE, bg=color, space_before=4, space_after=4) def add_col_headers(table, row_idx, headers, bg=C_LIGHT_BLUE): row=table.rows[row_idx] for i,h in enumerate(headers): para_in_cell(row.cells[i], h, bold=True, size=8.5, align=WD_ALIGN_PARAGRAPH.CENTER, bg=bg) def fill_row(table, row_idx, cells_data): row=table.rows[row_idx] for j,data in enumerate(cells_data): cell=row.cells[j] if isinstance(data,(list,tuple)): lines=list(data) para_in_cell(cell, lines[0], bold=(j==0), size=9, color=C_DARK_BLUE if j==0 else C_BLACK) for ln in lines[1:]: add_para_to_cell(cell, ln, size=8.5) else: para_in_cell(cell, str(data), bold=(j==0), size=9, color=C_DARK_BLUE if j==0 else C_BLACK) def auto_style(table, start=2): for i,row in enumerate(table.rows[start:], start=start): bg=C_WHITE if i%2==0 else C_GREY for cell in row.cells: if cell._tc.find(qn('w:shd')) is None: set_cell_bg(cell, bg) cell.vertical_alignment=WD_ALIGN_VERTICAL.CENTER def build_mixed_table(doc, section_title, col_widths, col_headers, rows_spec, sec_color=C_MID_BLUE): """rows_spec: list of ('subhdr', text) or list-of-cell-strings.""" nrows = 2 + len(rows_spec) # section-hdr + col-hdr + data ncols = len(col_widths) tbl = doc.add_table(rows=nrows, cols=ncols) tbl.style='Table Grid'; tbl.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(tbl, col_widths) add_section_header(tbl, section_title, color=sec_color) add_col_headers(tbl, 1, col_headers) ri=2 for item in rows_spec: row=tbl.rows[ri] if isinstance(item,tuple) and item[0]=='subhdr': row.cells[0].merge(row.cells[ncols-1]) para_in_cell(row.cells[0], f' {item[1]}', bold=True, size=8.5, color=C_WHITE, bg=C_SUB_BLUE, space_before=3, space_after=3) else: fill_row(tbl, ri, item) bg=C_WHITE if ri%2==0 else C_GREY for cell in row.cells: if cell._tc.find(qn('w:shd')) is None: set_cell_bg(cell, bg) cell.vertical_alignment=WD_ALIGN_VERTICAL.CENTER ri+=1 set_table_outer_border(tbl) return tbl def make_legend(doc, label, text, bg, border_color): tbl=doc.add_table(rows=1,cols=1); tbl.style='Table Grid' set_col_widths(tbl,[18.0]); set_cell_bg(tbl.rows[0].cells[0], bg) set_table_outer_border(tbl, color=border_color, sz='4') cell=tbl.rows[0].cells[0]; p=cell.paragraphs[0] p.paragraph_format.space_before=Pt(3); p.paragraph_format.space_after=Pt(3) r1=p.add_run(label+': '); r1.bold=True; r1.font.size=Pt(8.5); r1.font.name='Calibri' r1.font.color.rgb=hex_rgb(C_DARK_BLUE) r2=p.add_run(text); r2.font.size=Pt(8.5); r2.font.name='Calibri'; r2.font.color.rgb=hex_rgb('444444') return tbl # ════ DOCUMENT ════════════════════════════════════════════════════════════════ doc=Document() for sec in doc.sections: sec.page_width=Cm(21); sec.page_height=Cm(29.7) sec.left_margin=Cm(1.5); sec.right_margin=Cm(1.5) sec.top_margin=Cm(1.5); sec.bottom_margin=Cm(1.5) doc.styles['Normal'].font.name='Calibri'; doc.styles['Normal'].font.size=Pt(10) # ── BANNER ──────────────────────────────────────────────────────────────────── banner=doc.add_table(rows=2,cols=1) banner.alignment=WD_TABLE_ALIGNMENT.CENTER; banner.style='Table Grid' set_cell_bg(banner.rows[0].cells[0], C_DARK_BLUE) set_cell_bg(banner.rows[1].cells[0], C_DARK_BLUE) para_in_cell(banner.rows[0].cells[0],'MOTOR ASSESSMENT OF THE UPPER LIMBS', bold=True,size=16,color=C_WHITE,align=WD_ALIGN_PARAGRAPH.CENTER,space_before=6,space_after=2) para_in_cell(banner.rows[1].cells[0], 'Neurological Examination Proforma · Patient Record / Documentation', size=10,color='BFD9F2',align=WD_ALIGN_PARAGRAPH.CENTER,space_before=2,space_after=6) set_table_outer_border(banner,color=C_MID_BLUE,sz='12') add_space(doc,4) # ── PATIENT DETAILS ─────────────────────────────────────────────────────────── det=doc.add_table(rows=4,cols=4) det.style='Table Grid'; det.alignment=WD_TABLE_ALIGNMENT.CENTER set_col_widths(det,[3.5,5.5,3.2,5.8]) rows_det=[ [('Patient Name:',''),('Date:','')], [('Age / Sex:',' / ☐ M ☐ F ☐ Other'),('Examiner:','')], [('Hospital No.:',''),('Ward / OPD:','')], [('Diagnosis / Complaint:',''),('Dominant Side:','☐ Right ☐ Left')], ] for ri,pairs in enumerate(rows_det): cells=det.rows[ri].cells; ci=0 for label,val in pairs: para_in_cell(cells[ci],label,bold=True,size=9,color=C_DARK_BLUE,bg=C_PALE_BLUE) para_in_cell(cells[ci+1],val,bold=False,size=9,bg=C_PALE_BLUE); ci+=2 set_table_outer_border(det); add_space(doc,4) # ── 1. INSPECTION ───────────────────────────────────────────────────────────── insp_rows=[ ['Muscle Bulk','☐ Normal ☐ Wasted ☐ Hypertrophy','☐ Normal ☐ Wasted ☐ Hypertrophy',''], ['Fasciculations','☐ Absent ☐ Present','☐ Absent ☐ Present',''], ['Involuntary Movements', '☐ None ☐ Tremor (rest)\n☐ Tremor (postural) ☐ Other', '☐ None ☐ Tremor (rest)\n☐ Tremor (postural) ☐ Other',''], ['Pronator Drift\n(arms extended,\neyes closed 10 s)', '☐ Absent\n☐ Present: pronation\n☐ Present: elbow flexion', '☐ Absent\n☐ Present: pronation\n☐ Present: elbow flexion',''], ['Posture / Deformity', '☐ Normal ☐ Wrist drop\n☐ Claw hand ☐ Other', '☐ Normal ☐ Wrist drop\n☐ Claw hand ☐ Other',''], ['Thenar / Hypothenar\nWasting', '☐ Normal ☐ Thenar wasted\n☐ Hypothenar wasted', '☐ Normal ☐ Thenar wasted\n☐ Hypothenar wasted',''], ['Trophic / Skin Changes','☐ Absent ☐ Present','☐ Absent ☐ Present',''], ['Scars / Deformity','☐ None ☐ Present','☐ None ☐ Present',''], ['Mid-upper Arm\nCircumference','R: _______ cm','L: _______ cm','☐ Symmetric ☐ Asymmetric'], ] tbl=build_mixed_table(doc,'1. INSPECTION (Patient seated, arms fully exposed, good lighting)', [4.0,4.5,4.5,5.0],['Feature','Right','Left','Notes'],insp_rows) add_space(doc,4) # ── 2. TONE ─────────────────────────────────────────────────────────────────── tone_rows=[ ['Tone Character', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia', '☐ Normal ☐ Hypotonia\n☐ Spasticity ☐ Rigidity ☐ Paratonia',''], ['Cogwheel Rigidity\n(wrist)', '☐ Absent ☐ Present','☐ Absent ☐ Present',''], ['Lead-Pipe Rigidity', '☐ Absent ☐ Present','☐ Absent ☐ Present',''], ['Shoulder','☐ Normal ☐ ↑ ☐ ↓','☐ Normal ☐ ↑ ☐ ↓',''], ['Elbow\n(flex/extend)','☐ Normal ☐ ↑ ☐ ↓','☐ Normal ☐ ↑ ☐ ↓',''], ['Forearm\n(pro/supination)','☐ Normal ☐ ↑ ☐ ↓','☐ Normal ☐ ↑ ☐ ↓',''], ['Wrist\n(flex/extend)','☐ Normal ☐ ↑ ☐ ↓','☐ Normal ☐ ↑ ☐ ↓',''], ] tbl=build_mixed_table(doc,'2. TONE (Passive movement; patient relaxed and distracted)', [4.0,4.5,4.5,5.0],['Finding','Right','Left','Comment'],tone_rows) add_space(doc,4) # ── MRC Legend ──────────────────────────────────────────────────────────────── make_legend(doc,'MRC Scale', '0 = No movement 1 = Flicker 2 = Gravity eliminated 3 = Against gravity ' '4⁻ = Mild resistance 4 = Moderate resistance 4⁺ = Strong resistance 5 = Full power', C_ORANGE_LT, C_ACCENT) add_space(doc,2) # ── 3. POWER ────────────────────────────────────────────────────────────────── MRC='0/1/2/3/4⁻/4/4⁺/5' power_rows=[ ('subhdr','SHOULDER'), ['Shoulder Flexion (Ant. Deltoid)','C5–C6',MRC,MRC,''], ['Shoulder Abduction (Deltoid)', 'C5–C6',MRC,MRC,''], ['Shoulder Extension', 'C5–C6',MRC,MRC,''], ('subhdr','ELBOW'), ['Elbow Flexion (Biceps)', 'C5–C6',MRC,MRC,''], ['Elbow Extension (Triceps)', 'C6–C8',MRC,MRC,''], ('subhdr','WRIST & FOREARM'), ['Wrist Extension (Ext. Carpi)', 'C6–C7',MRC,MRC,''], ['Wrist Flexion (Flex. Carpi)', 'C7–C8',MRC,MRC,''], ['Forearm Supination', 'C5–C6',MRC,MRC,''], ['Forearm Pronation', 'C6–C7',MRC,MRC,''], ('subhdr','HAND & FINGERS'), ['Finger Extension (Ext. Dig.)', 'C7–C8',MRC,MRC,''], ['Finger Flexion (FDP / FDS)', 'C7–T1',MRC,MRC,''], ['Finger Abduction (Interossei)', 'T1', MRC,MRC,''], ['Thumb Abduction (Abd. Poll. Br.)','C8–T1',MRC,MRC,''], ] # rows needed = 2 (header+colhdr) + len(power_rows) tbl=build_mixed_table(doc,'3. POWER (MRC Grade 0–5; circle the grade for each side)', [5.2,1.5,3.9,3.9,3.5], ['Movement','Root','Right (circle)','Left (circle)','Notes'], power_rows) add_space(doc,4) # ── Reflex Legend ───────────────────────────────────────────────────────────── make_legend(doc,'Reflex Grade', '0 = Absent 1 = Diminished 2 = Normal 3 = Brisk / Increased 4 = Clonus', C_LT_GREEN, C_GREEN) add_space(doc,2) # ── 4. REFLEXES ─────────────────────────────────────────────────────────────── reflex_rows=[ ('subhdr','DEEP TENDON REFLEXES'), ['Biceps Jerk', 'C5–C6','Grade: 0 1 2 3 4','Grade: 0 1 2 3 4',''], ['Brachioradialis', 'C5–C6','Grade: 0 1 2 3 4','Grade: 0 1 2 3 4',''], ['Triceps Jerk', 'C6–C7','Grade: 0 1 2 3 4','Grade: 0 1 2 3 4',''], ['Finger Flexor Jerk','C8–T1','Grade: 0 1 2 3 4','Grade: 0 1 2 3 4',''], ('subhdr','PATHOLOGICAL REFLEXES'), ['Hoffmann Sign\n(flick middle finger nail)', 'C8–T1', '☐ Absent ☐ Present\n(thumb adduction = UMN)', '☐ Absent ☐ Present',''], ['Inverted Supinator\n(tap brachioradialis)', 'C5–C6', '☐ Normal ☐ Inverted\n(finger flex = C7–C8 release)', '☐ Normal ☐ Inverted',''], ('subhdr','SUPERFICIAL / FRONTAL REFLEXES'), ['Abdominal Reflex\n(upper T7–T9)', 'T7–T9','☐ Present ☐ Absent','☐ Present ☐ Absent',''], ['Palmomental Reflex', 'Frontal','☐ Absent ☐ Present\n(frontal lobe sign)','',''], ] tbl=build_mixed_table(doc,'4. REFLEXES', [4.6,1.5,4.0,4.0,3.9], ['Reflex','Root','Right','Left','Notes'], reflex_rows) add_space(doc,4) # ── 5. COORDINATION ─────────────────────────────────────────────────────────── coord_rows=[ ['Finger–Nose Test', '☐ Normal ☐ Dysmetria\n☐ Intention tremor', '☐ Normal ☐ Dysmetria\n☐ Intention tremor',''], ['Rapid Alternating\nMovements (RAM)', '☐ Normal ☐ Slow\n☐ Irregular (dysdiadochokinesia)', '☐ Normal ☐ Slow\n☐ Irregular (dysdiadochokinesia)',''], ['Rebound Test\n(cerebellar check)', '☐ Normal ☐ Rebound +ve','☐ Normal ☐ Rebound +ve',''], ['Fine Motor / Dexterity\n(button / coin test)', '☐ Normal ☐ Impaired','☐ Normal ☐ Impaired',''], ['Handwriting Sample', '☐ Normal ☐ Micrographia\n☐ Tremulous ☐ Dyspraxic','',''], ] tbl=build_mixed_table(doc,'5. COORDINATION (Cerebellar / Proprioceptive Function)', [4.0,4.8,5.2,4.0], ['Test','Right','Left / Bilateral','Notes'], coord_rows) add_space(doc,4) # ── 6. SPECIAL / SCREENING TESTS ───────────────────────────────────────────── spec_rows=[ ['Pronator Drift\n(10 s, eyes closed)', '☐ Absent ☐ Present','☐ Absent ☐ Present', 'UMN weakness screen'], ['Thumb Abduction\n(Abd. Poll. Br.)', '☐ Normal ☐ Weak','☐ Normal ☐ Weak', 'Median nerve (T1) / CTS'], ['Finger Abduction\n(index–little)', '☐ Normal ☐ Weak','☐ Normal ☐ Weak', 'Ulnar nerve (T1)'], ['Wrist / Finger\nExtension', '☐ Normal ☐ Weak','☐ Normal ☐ Weak', 'Radial nerve (C7)'], ['Power Grip\n(handshake)', '☐ Normal ☐ Reduced','☐ Normal ☐ Reduced', 'Overall hand strength'], ['Intrinsic Muscle\nBulk (interossei)', '☐ Normal ☐ Guttered','☐ Normal ☐ Guttered', 'Ulnar nerve / T1'], ] tbl=build_mixed_table(doc,'6. SPECIAL / SCREENING TESTS', [4.5,4.3,4.3,4.9], ['Test','Right','Left','Significance'], spec_rows, sec_color=C_SUB_BLUE) add_space(doc,4) # ── 7. UMN vs LMN SUMMARY ──────────────────────────────────────────────────── summ_rows=[ ['Muscle Bulk', 'Normal / mild disuse atrophy','Marked wasting',''], ['Fasciculations', 'Absent', 'Present',''], ['Tone', 'Increased (spasticity)', 'Decreased (flaccidity)',''], ['Pronator Drift', 'Present (corticospinal)', 'Absent',''], ['Deep Reflexes', 'Hyperreflexia', 'Diminished / Absent',''], ['Hoffmann Sign', 'Positive', 'Negative',''], ['Power', 'Reduced (pyramidal pattern)', 'Reduced (root/nerve pattern)',''], ['Coordination', 'May be impaired', 'Intact (unless sensory)',''], ] tbl=build_mixed_table(doc,'7. PATTERN SUMMARY (UMN vs LMN)', [4.0,4.8,4.8,4.4], ['Feature','UMN (Central)','LMN (Peripheral)','Patient Findings'], summ_rows, sec_color=C_GREEN) add_space(doc,2) # Pattern selector pat=doc.add_table(rows=1,cols=2); pat.style='Table Grid' set_col_widths(pat,[5.0,13.0]) set_cell_bg(pat.rows[0].cells[0],C_ORANGE_LT); set_cell_bg(pat.rows[0].cells[1],C_ORANGE_LT) set_table_outer_border(pat,color=C_ACCENT,sz='6') para_in_cell(pat.rows[0].cells[0],'Overall Clinical Pattern:',bold=True,size=9,color=C_DARK_BLUE) para_in_cell(pat.rows[0].cells[1], '☐ Normal ☐ UMN ☐ LMN ☐ Mixed ☐ Cerebellar ☐ Extrapyramidal ☐ Functional',size=9) add_space(doc,4) # ── 8. CLINICAL NOTES ──────────────────────────────────────────────────────── notes=doc.add_table(rows=9,cols=1); notes.style='Table Grid' set_col_widths(notes,[18.0]) add_section_header(notes,'8. CLINICAL SUMMARY & PLAN') notes_fields=[ ('Impression / Provisional Diagnosis:',True),('',''),('',''), ('Further Investigations Requested:',True),('',''), ('Management / Referral Plan:',True),('',''),('',''), ] for i,(label,is_label) in enumerate(notes_fields): cell=notes.rows[i+1].cells[0] set_cell_bg(cell,C_PALE_BLUE if is_label else C_WHITE) cell.vertical_alignment=WD_ALIGN_VERTICAL.CENTER para_in_cell(cell,label,bold=bool(is_label),size=9,color=C_DARK_BLUE if is_label else C_BLACK) set_table_outer_border(notes); add_space(doc,5) # Signature sig=doc.add_table(rows=1,cols=3); sig.style='Table Grid' set_col_widths(sig,[6.5,6.5,5.0]) para_in_cell(sig.rows[0].cells[0],'Examiner Signature: ___________________',size=9) para_in_cell(sig.rows[0].cells[1],'Designation: _______________________',size=9) para_in_cell(sig.rows[0].cells[2],'Date: ________________',size=9) set_table_outer_border(sig,color=C_MID_GREY,sz='4'); add_space(doc,3) # Footer fp=doc.add_paragraph() fp.paragraph_format.space_before=Pt(2); fp.paragraph_format.space_after=Pt(0) pPr=fp._p.get_or_add_pPr(); pBdr=OxmlElement('w:pBdr') top_el=OxmlElement('w:top'); top_el.set(qn('w:val'),'single'); top_el.set(qn('w:sz'),'4') top_el.set(qn('w:space'),'1'); top_el.set(qn('w:color'),'CCCCCC') pBdr.append(top_el); pPr.append(pBdr) r1=fp.add_run('Sequence: Inspection → Tone → Power → Reflexes → Coordination → Special Tests') r1.italic=True; r1.font.size=Pt(7.5); r1.font.name='Calibri'; r1.font.color.rgb=hex_rgb('888888') fp.add_run(' ') r3=fp.add_run("Harrison's 22E · Bradley & Daroff's Neurology · Das Manual of Clinical Surgery") r3.italic=True; r3.font.size=Pt(7.5); r3.font.name='Calibri'; r3.font.color.rgb=hex_rgb('AAAAAA') out='/tmp/workspace/motor-assessment/Upper_Limb_Motor_Assessment.docx' doc.save(out) print(f"Saved: {out}") PYEOF

Your Word file is ready:

Upper Limb Motor Assessment

Word Document · DOCX


What's inside - all 8 sections, upper limb specific:
SectionUpper Limb Content
Patient DetailsName, Age/Sex, Hospital No., Date, Examiner, Dominant side
1. InspectionBulk, fasciculations, involuntary movements, pronator drift, posture (wrist drop/claw hand), thenar/hypothenar wasting, skin changes, arm circumference
2. ToneCharacter (spasticity/rigidity/paratonia), cogwheel/lead-pipe rigidity at wrist, joint-by-joint (shoulder/elbow/forearm/wrist) with ↑/↓ tickboxes
3. Power16 movements grouped into 4 sub-sections (Shoulder / Elbow / Wrist & Forearm / Hand & Fingers) with nerve roots, MRC 0/1/2/3/4⁻/4/4⁺/5 circle-grade format
4. ReflexesBiceps (C5–C6), brachioradialis (C5–C6), triceps (C6–C7), finger flexor (C8–T1) + Hoffmann sign, inverted supinator sign, abdominal, palmomental
5. CoordinationFinger-nose test, RAM / dysdiadochokinesia, rebound test, fine motor/dexterity, handwriting sample
6. Special / Screening TestsPronator drift, thumb abduction (median/CTS screen), finger abduction (ulnar screen), wrist extension (radial nerve), power grip, interossei bulk
7. UMN vs LMN SummarySide-by-side table + clinical pattern selector (Normal / UMN / LMN / Mixed / Cerebellar / Extrapyramidal / Functional)
8. Clinical SummaryImpression, investigations, management plan, signature block
This is a shared conversation. Sign in to Orris to start your own chat.