Elicit Step wise medical history in rheumatoid arthritis

Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Loading Image
I now have all the information needed. Here is the comprehensive stepwise medical history for rheumatoid arthritis:

Stepwise Medical History in Rheumatoid Arthritis

Severe advanced rheumatoid arthritis of both hands showing ulnar deviation at MCPs, swan-neck deformity, dorsal tendon swelling, and muscle wasting
Severe advanced RA: ulnar deviation at MCPs, swan-neck deformity, dorsal tendon swelling, and thenar wasting

Step 1 — Demographic Profile (Biodata)

  • Age: Peak onset is 40-60 years; RA can occur at any age including juvenile form (<16 years = JIA)
  • Sex: Female predominance (F:M = 3:1); sex hormones influence disease onset and severity
  • Occupation: Ask about repetitive joint use (manual labour, typing); disability impacting work is common
  • Marital status / living alone: Relevant for functional assessment and support

Step 2 — Chief Complaints

Elicit in the patient's own words, with duration for each:
  1. Joint pain (arthralgia/arthritis) - most common presenting complaint
  2. Joint swelling
  3. Morning stiffness
  4. Difficulty using hands / reduced grip
  5. Fatigue and generalized weakness
  6. Deformity (in chronic/advanced disease)

Step 3 — History of Presenting Illness (HOPI)

This is the core of the history. Address each complaint systematically:

A. Joint Pain

  • Site: Which joints are involved first? Classic RA starts in the small joints of the hands (PIP and MCP joints) and toes (MTP joints). Importantly, DIP joints are typically spared - their involvement suggests psoriatic arthritis or OA
  • Onset: Insidious (most common in RA) vs. acute; some patients have a palindromic pattern (migratory, episodic attacks)
  • Character: Dull aching; throbbing pain suggests acute inflammation
  • Radiation: Does it radiate? Cervical spine involvement can cause neck pain radiating to arms (C1-C2 subluxation)
  • Aggravating / Relieving factors: Pain worsens with activity, improves with warmth and gentle movement; pain is also reportedly aggravated during rainy seasons and at full/new moon
  • Severity: Score on a 0-10 numerical rating scale
  • Progression: Which joints were involved first? How did disease spread (additive vs. migratory vs. simultaneous)?
  • Symmetry: RA is characteristically bilateral and symmetric - involvement of both wrists, both MCPs, both PIPs

B. Joint Swelling

  • Which joints? Symmetrical involvement of small joints of hands and feet is characteristic
  • Is swelling soft (synovial effusion) or boggy (synovial proliferation/pannus)?
  • Is it persistent or intermittent?

C. Morning Stiffness - The Key Diagnostic Feature

  • Duration of morning stiffness - must be elicited precisely; in RA it lasts >1 hour (often hours) and is a hallmark feature
  • Distinguishes RA from OA (where stiffness lasts <30 minutes)
  • Ask: "When you wake up, how long does it take before your joints are as loose as they'll be for the rest of the day?"
  • Stiffness is relieved by warmth and activity

D. Functional Impairment

  • Difficulty gripping objects, opening jars, buttoning clothes
  • Difficulty walking, climbing stairs
  • Activities of daily living (ADL) assessment

E. Extra-articular Symptoms (Review of Systems Pertaining to RA)

Screen actively - these occur predominantly in seropositive (RF/ACPA-positive) patients:
SystemSymptoms to Ask
ConstitutionalFatigue (often pronounced), weight loss, low-grade fever, malaise
SkinSubcutaneous nodules (firm, non-tender, extensor surfaces - forearms, olecranon)
EyesDry eyes (Sicca/keratoconjunctivitis sicca), red eye (episcleritis, scleritis), visual changes
CardiovascularChest pain, palpitations, breathlessness (pericarditis, pleuritis, premature CAD)
RespiratoryDry cough, breathlessness (ILD, pleural effusion, rheumatoid nodules in lungs, rarely bronchiolitis obliterans)
NeurologicalTingling/numbness in hands (carpal tunnel syndrome - very common in RA), weakness in legs (cervical myelopathy from C1-C2 subluxation), stocking-glove neuropathy
ENTHoarseness, throat fullness (cricoarytenoid joint involvement)
HaematologicalPallor, easy bruising (anaemia of chronic disease; in Felty's syndrome: splenomegaly + neutropenia)
Urinary/RenalAnkle swelling (amyloidosis in long-standing RA)

Step 4 — Past Medical History

Ask specifically about:
  1. Previous episodes of joint pain/swelling - RA may have a palindromic prodrome
  2. History of tuberculosis, gonorrhoea, syphilis, typhoid or pneumonia - relevant to differential diagnosis of infective/reactive arthritis
  3. Previous trauma to joints - can precipitate or complicate OA
  4. Previous investigations - prior RF testing, X-rays, any previous diagnosis
  5. Previous treatment - steroids, NSAIDs, DMARDs; response to treatment helps confirm diagnosis
  6. Cardiovascular disease, diabetes, osteoporosis - important comorbidities and treatment considerations
  7. Thyroid disease - hypothyroidism commonly co-exists with RA and causes rheumatic symptoms

Step 5 — Treatment History

  • Current medications: NSAIDs, glucocorticoids (dose, duration), conventional DMARDs (methotrexate, hydroxychloroquine, sulfasalazine, leflunomide), biologic DMARDs (TNF inhibitors, IL-6 inhibitors, abatacept, rituximab), targeted synthetic DMARDs (JAK inhibitors - tofacitinib, baricitinib)
  • Response to treatment: Any improvement? Side effects?
  • Steroid history: Long-term use raises risk of osteoporosis, infections, adrenal suppression
  • NSAID use: GI bleeding risk, renal function monitoring required
  • Vaccinations: Ask about hepatitis B, pneumococcal, influenza status (essential before starting biologics)

Step 6 — Family History

  • Rheumatoid arthritis or autoimmune diseases in first-degree relatives - RA has a significant heritable component (~60% genetic contribution); risk in first-degree relatives is 3-5x higher
  • Haemophilia, gout, tuberculosis, rheumatic fever - relevant to differentials
  • Cardiovascular disease - RA patients have increased cardiovascular mortality; family history adds further risk
  • Family history of periodontal disease is also emerging as relevant (shared microbiome and citrullination pathways)

Step 7 — Personal / Social History

  • Smoking: A major environmental risk factor for seropositive RA; smoking promotes citrullination via PAD2 enzyme induction in alveolar macrophages; smokers have much higher ACPA levels (57% vs 7% in non-smokers)
  • Alcohol: Interacts with methotrexate (hepatotoxicity)
  • Diet: Mediterranean-style diet is protective; obesity worsens inflammation
  • Occupation: Joint stress; disability and work absence are early features even in pre-clinical disease
  • Physical activity level
  • Living situation: Support at home; ability to carry out ADLs independently
  • Stress / mental health: Psychological stress is a potential trigger; depression is a common comorbidity

Step 8 — Menstrual and Obstetric History (in women)

  • Pregnancy: RA classically improves during pregnancy (especially 2nd and 3rd trimester) and often flares postpartum
  • Oral contraceptive use: May be protective against developing RA
  • Menarche/menopause: Onset around or after menopause is common; hormonal influence on autoimmunity
  • Breastfeeding: Associated with postpartum flares

Step 9 — Systemic Review (remaining systems)

Briefly screen for:
  • GI: nausea, diarrhoea (NSAID/DMARD side effects; GI amyloidosis in chronic RA)
  • Genitourinary: proteinuria, oedema (amyloidosis, gold-induced membranous nephropathy)
  • CNS: memory, mood (depression in chronic pain)

Putting it Together: ACR/EULAR 2010 Classification Criteria (Diagnostic Aid)

When taking history, keep in mind the scoring framework you are building toward:
DomainScore
Joint involvement: 1 large joint = 0; 2-10 large joints = 1; 1-3 small joints = 2; 4-10 small joints = 3; >10 joints (at least 1 small) = 50-5
Serology: RF or ACPA negative = 0; low positive = 2; high positive (>3x ULN) = 30-3
Duration of synovitis: <6 weeks = 0; ≥6 weeks = 10-1
Acute phase reactants: Normal CRP and ESR = 0; Abnormal = 10-1
Score ≥6 = Definite RA. The history of duration of joint symptoms (≥6 weeks), pattern of joint involvement, and systemic features directly feeds into this scoring.

Summary: Red Flags in History Suggesting Poor Prognosis

Eliciting these in the history signals a need for aggressive early DMARD therapy:
  • High-titer RF or ACPA positivity
  • Many small joints involved at onset (score ≥5 joint domain)
  • Persistent elevated CRP/ESR
  • Early functional limitation
  • Extra-articular features
  • Erosions on imaging at presentation

Sources: Goldman-Cecil Medicine (9780323930345), Firestein & Kelley's Textbook of Rheumatology (9780323935364), S Das Manual on Clinical Surgery 13th Ed (9788190568104)

Generate a detailed patient history template for RA initial assessment

Asking for Preferences
Reading File
Running Command

mkdir -p /home/daytona/workspace/ra-template && ls /home/daytona/workspace/ra-template

Writing File

~/ra-template/generate_ra_template.py

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import os

OUTPUT_PATH = "/home/daytona/workspace/ra-template/RA_Initial_Assessment_Template.pdf"

# ── Colour palette ────────────────────────────────────────────────────────────
DEEP_BLUE    = colors.HexColor("#1A3A5C")
MID_BLUE     = colors.HexColor("#2E6DA4")
LIGHT_BLUE   = colors.HexColor("#D6E8F7")
ACCENT_RED   = colors.HexColor("#C0392B")
LIGHT_GREY   = colors.HexColor("#F4F6F8")
MID_GREY     = colors.HexColor("#BDC3C7")
DARK_GREY    = colors.HexColor("#2C3E50")
WHITE        = colors.white
FIELD_BG     = colors.HexColor("#FAFBFC")
SECTION_HDR  = colors.HexColor("#EBF3FB")

W, H = A4   # 210 x 297 mm

doc = SimpleDocTemplate(
    OUTPUT_PATH,
    pagesize=A4,
    topMargin=18*mm,
    bottomMargin=18*mm,
    leftMargin=18*mm,
    rightMargin=18*mm,
    title="Rheumatoid Arthritis – Initial Patient Assessment",
    author="Rheumatology Department",
)

styles = getSampleStyleSheet()

# ── Custom paragraph styles ───────────────────────────────────────────────────
def PS(name, parent="Normal", **kw):
    return ParagraphStyle(name, parent=styles[parent], **kw)

TITLE_STYLE  = PS("DocTitle",  fontSize=18, textColor=WHITE,        leading=22, alignment=TA_CENTER, spaceAfter=2)
SUBTITLE_S   = PS("DocSub",    fontSize=10, textColor=LIGHT_BLUE,   leading=13, alignment=TA_CENTER, spaceAfter=0)
SEC_HEAD     = PS("SecHead",   fontSize=10, textColor=WHITE,        leading=14, alignment=TA_LEFT,   spaceBefore=4, spaceAfter=2, fontName="Helvetica-Bold")
SUB_HEAD     = PS("SubHead",   fontSize=9,  textColor=MID_BLUE,    leading=12, alignment=TA_LEFT,   spaceBefore=3, spaceAfter=1, fontName="Helvetica-Bold")
LABEL        = PS("Label",     fontSize=8,  textColor=DARK_GREY,   leading=10, alignment=TA_LEFT,   fontName="Helvetica-Bold")
BODY         = PS("Body",      fontSize=8,  textColor=DARK_GREY,   leading=11, alignment=TA_LEFT)
SMALL        = PS("Small",     fontSize=7,  textColor=colors.HexColor("#7F8C8D"), leading=9, alignment=TA_LEFT)
NOTE         = PS("Note",      fontSize=7.5, textColor=ACCENT_RED, leading=10, alignment=TA_LEFT,  fontName="Helvetica-Oblique")
FIELD_LABEL  = PS("FLabel",    fontSize=7.5, textColor=colors.HexColor("#5D6D7E"), leading=10, fontName="Helvetica-Bold")

PAGE_W = W - 36*mm   # usable width

# ── Helper: blank write-in line ───────────────────────────────────────────────
def field_row(label, lines=1, height=7*mm):
    """Single labelled field with underline."""
    inner = [
        [Paragraph(label, FIELD_LABEL), ""],
    ]
    col_w = PAGE_W / 2
    t = Table(inner, colWidths=[col_w*0.38, col_w*0.62])
    t.setStyle(TableStyle([
        ("VALIGN",       (0,0), (-1,-1), "BOTTOM"),
        ("LINEBELOW",    (1,0), (1,0), 0.5, MID_GREY),
        ("BOTTOMPADDING",(0,0), (-1,-1), 2),
        ("TOPPADDING",   (0,0), (-1,-1), 2),
    ]))
    return t

def two_fields(lbl1, lbl2):
    """Two labelled fields side by side."""
    col = PAGE_W / 2
    data = [[Paragraph(lbl1, FIELD_LABEL), "", Paragraph(lbl2, FIELD_LABEL), ""]]
    t = Table(data, colWidths=[col*0.38, col*0.62, col*0.38, col*0.62])
    t.setStyle(TableStyle([
        ("VALIGN",       (0,0), (-1,-1), "BOTTOM"),
        ("LINEBELOW",    (1,0), (1,0), 0.5, MID_GREY),
        ("LINEBELOW",    (3,0), (3,0), 0.5, MID_GREY),
        ("BOTTOMPADDING",(0,0), (-1,-1), 2),
        ("TOPPADDING",   (0,0), (-1,-1), 2),
    ]))
    return t

def three_fields(lbl1, lbl2, lbl3):
    """Three labelled fields side by side."""
    col = PAGE_W / 3
    data = [[Paragraph(lbl1, FIELD_LABEL), "", Paragraph(lbl2, FIELD_LABEL), "", Paragraph(lbl3, FIELD_LABEL), ""]]
    t = Table(data, colWidths=[col*0.42, col*0.58, col*0.42, col*0.58, col*0.42, col*0.58])
    t.setStyle(TableStyle([
        ("VALIGN",       (0,0), (-1,-1), "BOTTOM"),
        ("LINEBELOW",    (1,0), (1,0), 0.5, MID_GREY),
        ("LINEBELOW",    (3,0), (3,0), 0.5, MID_GREY),
        ("LINEBELOW",    (5,0), (5,0), 0.5, MID_GREY),
        ("BOTTOMPADDING",(0,0), (-1,-1), 2),
        ("TOPPADDING",   (0,0), (-1,-1), 2),
    ]))
    return t

def section_header(title, icon=""):
    """Blue section header band."""
    data = [[Paragraph(f"{icon}  {title}", SEC_HEAD)]]
    t = Table(data, colWidths=[PAGE_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,-1), DEEP_BLUE),
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING",  (0,0), (-1,-1), 8),
    ]))
    return t

def sub_header(title):
    """Light-blue sub-section header."""
    data = [[Paragraph(title, SUB_HEAD)]]
    t = Table(data, colWidths=[PAGE_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,-1), SECTION_HDR),
        ("TOPPADDING",   (0,0), (-1,-1), 3),
        ("BOTTOMPADDING",(0,0), (-1,-1), 3),
        ("LEFTPADDING",  (0,0), (-1,-1), 6),
        ("LINEBELOW",    (0,0), (-1,-1), 0.8, MID_BLUE),
    ]))
    return t

def checkbox_row(items, cols=3):
    """Row of tick-box options."""
    # Pad to fill cols
    while len(items) % cols != 0:
        items.append("")
    rows = [items[i:i+cols] for i in range(0, len(items), cols)]
    cell_w = PAGE_W / cols
    data = []
    for row in rows:
        data.append([Paragraph(f"☐  {item}" if item else "", BODY) for item in row])
    t = Table(data, colWidths=[cell_w]*cols)
    t.setStyle(TableStyle([
        ("VALIGN",       (0,0), (-1,-1), "MIDDLE"),
        ("TOPPADDING",   (0,0), (-1,-1), 2),
        ("BOTTOMPADDING",(0,0), (-1,-1), 2),
        ("LEFTPADDING",  (0,0), (-1,-1), 4),
    ]))
    return t

def text_box(label, height=14*mm):
    """Large multi-line text entry box."""
    data = [
        [Paragraph(label, FIELD_LABEL)],
        [""],
    ]
    t = Table(data, colWidths=[PAGE_W], rowHeights=[None, height])
    t.setStyle(TableStyle([
        ("BOX",          (0,0), (-1,-1), 0.5, MID_GREY),
        ("BACKGROUND",   (0,0), (-1,-1), FIELD_BG),
        ("TOPPADDING",   (0,0), (-1,-1), 3),
        ("LEFTPADDING",  (0,0), (-1,-1), 5),
        ("VALIGN",       (0,0), (-1,-1), "TOP"),
    ]))
    return t

def spacer(h=3*mm):
    return Spacer(1, h)

def hr():
    return HRFlowable(width=PAGE_W, thickness=0.4, color=MID_GREY, spaceAfter=2, spaceBefore=2)

# ── VAS / NRS scale ───────────────────────────────────────────────────────────
def pain_scale():
    """0-10 NRS pain scale boxes."""
    boxes = [str(i) for i in range(11)]
    col_w = PAGE_W / 11
    data = [boxes]
    t = Table(data, colWidths=[col_w]*11, rowHeights=[8*mm])
    style = [
        ("ALIGN",        (0,0), (-1,-1), "CENTER"),
        ("VALIGN",       (0,0), (-1,-1), "MIDDLE"),
        ("FONTNAME",     (0,0), (-1,-1), "Helvetica-Bold"),
        ("FONTSIZE",     (0,0), (-1,-1), 9),
        ("BOX",          (0,0), (-1,-1), 0.8, DEEP_BLUE),
        ("INNERGRID",    (0,0), (-1,-1), 0.5, DEEP_BLUE),
    ]
    # Colour: green→yellow→red
    green_cols  = [0,1,2,3]
    yellow_cols = [4,5,6]
    red_cols    = [7,8,9,10]
    for c in green_cols:
        style.append(("BACKGROUND", (c,0), (c,0), colors.HexColor("#D5F5E3")))
    for c in yellow_cols:
        style.append(("BACKGROUND", (c,0), (c,0), colors.HexColor("#FEF9E7")))
    for c in red_cols:
        style.append(("BACKGROUND", (c,0), (c,0), colors.HexColor("#FADBD8")))
    t.setStyle(TableStyle(style))
    return t

# ── Joint diagram table ───────────────────────────────────────────────────────
def joint_involvement_table():
    joints = [
        "MCP joints (2-5)",
        "PIP joints",
        "Wrists",
        "Elbows",
        "Shoulders",
        "Knees",
        "Ankles",
        "MTP joints",
        "Cervical spine",
        "Hips",
        "TMJ",
        "Cricoarytenoid",
    ]
    header = [
        Paragraph("Joint", FIELD_LABEL),
        Paragraph("Right", FIELD_LABEL),
        Paragraph("Left", FIELD_LABEL),
        Paragraph("Bilateral", FIELD_LABEL),
        Paragraph("Tenderness", FIELD_LABEL),
        Paragraph("Swelling", FIELD_LABEL),
        Paragraph("Warmth", FIELD_LABEL),
        Paragraph("Deformity", FIELD_LABEL),
    ]
    col_widths = [PAGE_W*0.20, PAGE_W*0.08, PAGE_W*0.08, PAGE_W*0.10,
                  PAGE_W*0.14, PAGE_W*0.10, PAGE_W*0.10, PAGE_W*0.10]
    rows = [header]
    for i, j in enumerate(joints):
        bg = LIGHT_GREY if i % 2 == 0 else WHITE
        rows.append([
            Paragraph(j, BODY), "☐", "☐", "☐", "☐", "☐", "☐", "☐"
        ])
    t = Table(rows, colWidths=col_widths)
    ts = TableStyle([
        ("BACKGROUND",   (0,0), (-1,0),  SECTION_HDR),
        ("FONTNAME",     (0,0), (-1,0),  "Helvetica-Bold"),
        ("FONTSIZE",     (0,0), (-1,-1), 8),
        ("ALIGN",        (1,0), (-1,-1), "CENTER"),
        ("VALIGN",       (0,0), (-1,-1), "MIDDLE"),
        ("ROWBACKGROUNDS",(0,1),(-1,-1), [LIGHT_GREY, WHITE]),
        ("INNERGRID",    (0,0), (-1,-1), 0.3, MID_GREY),
        ("BOX",          (0,0), (-1,-1), 0.8, MID_BLUE),
        ("TOPPADDING",   (0,0), (-1,-1), 3),
        ("BOTTOMPADDING",(0,0), (-1,-1), 3),
        ("LEFTPADDING",  (0,0), (-1,-1), 4),
    ])
    t.setStyle(ts)
    return t

# ── Extra-articular checklist table ──────────────────────────────────────────
def extra_articular_table():
    systems = [
        ("Constitutional",    "Fatigue  ☐    Weight loss  ☐    Low-grade fever  ☐    Malaise  ☐"),
        ("Skin / Nodules",    "Rheumatoid nodules  ☐    Vasculitis  ☐    Pyoderma gangrenosum  ☐"),
        ("Eyes",              "Dry eyes (sicca)  ☐    Episcleritis  ☐    Scleritis  ☐    Visual blurring  ☐"),
        ("Cardiovascular",    "Chest pain  ☐    Palpitations  ☐    Breathlessness on exertion  ☐    Pericarditis hx  ☐"),
        ("Respiratory",       "Dry cough  ☐    Breathlessness at rest  ☐    Pleuritic pain  ☐    Haemoptysis  ☐"),
        ("Neurological",      "Carpal tunnel syndrome  ☐    Tarsal tunnel  ☐    Cervical myelopathy  ☐    Peripheral neuropathy  ☐"),
        ("ENT",               "Hoarseness  ☐    Throat fullness  ☐    Stridor  ☐    Jaw pain (TMJ)  ☐"),
        ("Haematological",    "Pallor  ☐    Splenomegaly  ☐    Lymphadenopathy  ☐    Recurrent infections  ☐"),
        ("Renal",             "Oedema  ☐    Frothy urine (proteinuria)  ☐    Reduced urine output  ☐"),
        ("Gastrointestinal",  "Nausea/vomiting  ☐    GI bleeding  ☐    Mouth ulcers  ☐    Abdominal pain  ☐"),
    ]
    header = [Paragraph("System", FIELD_LABEL), Paragraph("Symptoms — circle / tick all present", FIELD_LABEL)]
    rows = [header]
    for s, sym in systems:
        rows.append([Paragraph(s, BODY), Paragraph(sym, BODY)])
    col_widths = [PAGE_W*0.20, PAGE_W*0.80]
    t = Table(rows, colWidths=col_widths)
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0),  SECTION_HDR),
        ("FONTNAME",      (0,0), (-1,0),  "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,-1), 7.5),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("ROWBACKGROUNDS",(0,1),(-1,-1),  [LIGHT_GREY, WHITE]),
        ("INNERGRID",     (0,0), (-1,-1), 0.3, MID_GREY),
        ("BOX",           (0,0), (-1,-1), 0.8, MID_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 3),
        ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("LEFTPADDING",   (0,0), (-1,-1), 4),
    ]))
    return t

# ── ACR/EULAR scoring table ───────────────────────────────────────────────────
def acr_eular_table():
    data = [
        [Paragraph("Domain", FIELD_LABEL), Paragraph("Criteria", FIELD_LABEL), Paragraph("Score", FIELD_LABEL), Paragraph("Tick", FIELD_LABEL)],
        [Paragraph("Joint Involvement\n(select highest)", BODY),
         Paragraph("1 medium-large joint", BODY), Paragraph("0", BODY), "☐"],
        ["", Paragraph("2-10 medium-large joints", BODY), Paragraph("1", BODY), "☐"],
        ["", Paragraph("1-3 small joints", BODY), Paragraph("2", BODY), "☐"],
        ["", Paragraph("4-10 small joints", BODY), Paragraph("3", BODY), "☐"],
        ["", Paragraph(">10 joints incl. ≥1 small joint", BODY), Paragraph("5", BODY), "☐"],
        [Paragraph("Serology\n(select highest)", BODY),
         Paragraph("RF negative AND ACPA negative", BODY), Paragraph("0", BODY), "☐"],
        ["", Paragraph("RF or ACPA low positive (≤3× ULN)", BODY), Paragraph("2", BODY), "☐"],
        ["", Paragraph("RF or ACPA high positive (>3× ULN)", BODY), Paragraph("3", BODY), "☐"],
        [Paragraph("Duration of\nSynovitis", BODY),
         Paragraph("<6 weeks", BODY), Paragraph("0", BODY), "☐"],
        ["", Paragraph("≥6 weeks", BODY), Paragraph("1", BODY), "☐"],
        [Paragraph("Acute Phase\nReactants", BODY),
         Paragraph("Normal CRP and ESR", BODY), Paragraph("0", BODY), "☐"],
        ["", Paragraph("Abnormal CRP or ESR", BODY), Paragraph("1", BODY), "☐"],
        [Paragraph("TOTAL SCORE", FIELD_LABEL), Paragraph("Score ≥6 = Definite RA", NOTE),
         Paragraph("__ / 10", FIELD_LABEL), ""],
    ]
    col_widths = [PAGE_W*0.22, PAGE_W*0.52, PAGE_W*0.14, PAGE_W*0.12]
    t = Table(data, colWidths=col_widths)
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0),   SECTION_HDR),
        ("FONTNAME",      (0,0), (-1,0),   "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,-1),  8),
        ("VALIGN",        (0,0), (-1,-1),  "MIDDLE"),
        ("ALIGN",         (2,0), (3,-1),   "CENTER"),
        ("SPAN",          (0,1), (0,5)),
        ("SPAN",          (0,6), (0,8)),
        ("SPAN",          (0,9), (0,10)),
        ("SPAN",          (0,11),(0,12)),
        ("INNERGRID",     (0,0), (-1,-1),  0.3, MID_GREY),
        ("BOX",           (0,0), (-1,-1),  0.8, MID_BLUE),
        ("BACKGROUND",    (0,-1),(-1,-1),  colors.HexColor("#EBF3FB")),
        ("FONTNAME",      (0,-1),(-1,-1),  "Helvetica-Bold"),
        ("TOPPADDING",    (0,0), (-1,-1),  3),
        ("BOTTOMPADDING", (0,0), (-1,-1),  3),
        ("LEFTPADDING",   (0,0), (-1,-1),  5),
        ("ROWBACKGROUNDS",(0,1), (-1,-2),  [LIGHT_GREY, WHITE]),
    ]))
    return t

# ── Disease activity scores ───────────────────────────────────────────────────
def das28_table():
    data = [
        [Paragraph("DAS28 Component", FIELD_LABEL),
         Paragraph("Value", FIELD_LABEL),
         Paragraph("DAS28 Interpretation", FIELD_LABEL),
         Paragraph("Threshold", FIELD_LABEL)],
        [Paragraph("Tender joint count (28)", BODY), "____",
         Paragraph("Remission", BODY), Paragraph("< 2.6", BODY)],
        [Paragraph("Swollen joint count (28)", BODY), "____",
         Paragraph("Low disease activity", BODY), Paragraph("2.6 – 3.2", BODY)],
        [Paragraph("ESR (mm/hr)  or  CRP (mg/L)", BODY), "____",
         Paragraph("Moderate disease activity", BODY), Paragraph("3.2 – 5.1", BODY)],
        [Paragraph("Patient global VAS (0-100 mm)", BODY), "____",
         Paragraph("High disease activity", BODY), Paragraph("> 5.1", BODY)],
        [Paragraph("DAS28 Score:", FIELD_LABEL), "____________",
         Paragraph("Interpretation:", FIELD_LABEL), "_______________"],
    ]
    col_widths = [PAGE_W*0.35, PAGE_W*0.15, PAGE_W*0.32, PAGE_W*0.18]
    t = Table(data, colWidths=col_widths)
    t.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,0),  SECTION_HDR),
        ("FONTNAME",      (0,0), (-1,0),  "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,-1), 8),
        ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
        ("INNERGRID",     (0,0), (-1,-1), 0.3, MID_GREY),
        ("BOX",           (0,0), (-1,-1), 0.8, MID_BLUE),
        ("ROWBACKGROUNDS",(0,1),(-1,-2),  [LIGHT_GREY, WHITE]),
        ("BACKGROUND",    (0,-1),(-1,-1), colors.HexColor("#EBF3FB")),
        ("FONTNAME",      (0,-1),(-1,-1), "Helvetica-Bold"),
        ("TOPPADDING",    (0,0), (-1,-1), 3),
        ("BOTTOMPADDING", (0,0), (-1,-1), 3),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ]))
    return t

# ─────────────────────────────────────────────────────────────────────────────
#  TITLE PAGE / HEADER
# ─────────────────────────────────────────────────────────────────────────────
def build_header():
    title_data = [
        [Paragraph("RHEUMATOID ARTHRITIS", TITLE_STYLE)],
        [Paragraph("Initial Patient Assessment Template", SUBTITLE_S)],
        [Paragraph("ACR/EULAR 2010 Classification Criteria | DAS28 | Disease Activity Monitoring", SUBTITLE_S)],
    ]
    t = Table(title_data, colWidths=[PAGE_W])
    t.setStyle(TableStyle([
        ("BACKGROUND",   (0,0), (-1,-1), DEEP_BLUE),
        ("TOPPADDING",   (0,0), (-1,-1), 7),
        ("BOTTOMPADDING",(0,0), (-1,-1), 6),
        ("LEFTPADDING",  (0,0), (-1,-1), 10),
    ]))
    return t


# ─────────────────────────────────────────────────────────────────────────────
#  BUILD DOCUMENT
# ─────────────────────────────────────────────────────────────────────────────
story = []

# Title
story.append(build_header())
story.append(spacer(4*mm))

# ── SECTION 1: PATIENT DEMOGRAPHICS ─────────────────────────────────────────
story.append(section_header("1.  PATIENT DEMOGRAPHICS"))
story.append(spacer(2*mm))
story.append(three_fields("Patient Name:", "Date of Birth:", "Age (yrs):"))
story.append(spacer(2*mm))
story.append(three_fields("Hospital / MRN:", "Date of Assessment:", "Clinician:"))
story.append(spacer(2*mm))
story.append(two_fields("Sex:  ☐ Male   ☐ Female   ☐ Other", "Marital Status:"))
story.append(spacer(2*mm))
story.append(two_fields("Occupation:", "Educational level:"))
story.append(spacer(2*mm))
story.append(two_fields("Address / Contact:", "Referred by:"))
story.append(spacer(4*mm))

# ── SECTION 2: CHIEF COMPLAINTS ─────────────────────────────────────────────
story.append(section_header("2.  CHIEF COMPLAINTS  (with duration)"))
story.append(spacer(2*mm))
complaints = [
    "Joint pain (arthralgia / arthritis)", "Joint swelling",
    "Morning stiffness", "Difficulty using hands / reduced grip",
    "Fatigue / generalised weakness", "Joint deformity",
    "Other:"
]
for i, c in enumerate(complaints):
    data = [[
        Paragraph(f"{i+1}.  {c}", BODY),
        Paragraph("Duration:", FIELD_LABEL),
        "",
    ]]
    t = Table(data, colWidths=[PAGE_W*0.55, PAGE_W*0.15, PAGE_W*0.30])
    t.setStyle(TableStyle([
        ("VALIGN",       (0,0), (-1,-1), "BOTTOM"),
        ("LINEBELOW",    (2,0), (2,0), 0.5, MID_GREY),
        ("TOPPADDING",   (0,0), (-1,-1), 2),
        ("BOTTOMPADDING",(0,0), (-1,-1), 2),
    ]))
    story.append(t)
story.append(spacer(4*mm))

# ── SECTION 3: HISTORY OF PRESENTING ILLNESS ────────────────────────────────
story.append(section_header("3.  HISTORY OF PRESENTING ILLNESS (HOPI)"))
story.append(spacer(2*mm))

# 3A – Joint Pain
story.append(sub_header("A.  Joint Pain"))
story.append(spacer(1*mm))

story.append(two_fields("Site (which joints first involved)?", "Current joints involved:"))
story.append(spacer(2*mm))

story.append(Paragraph("Onset:", FIELD_LABEL))
story.append(checkbox_row(["Insidious (gradual)", "Acute / sudden", "Palindromic (episodic, migratory)", "Polyarticular from start", "Oligoarticular from start"], cols=3))
story.append(spacer(2*mm))

story.append(Paragraph("Character of pain:", FIELD_LABEL))
story.append(checkbox_row(["Dull aching", "Throbbing", "Burning", "Sharp", "Deep boring"], cols=5))
story.append(spacer(2*mm))

story.append(Paragraph("Pattern of joint involvement:", FIELD_LABEL))
story.append(checkbox_row(["Symmetric bilateral", "Asymmetric", "Additive (new joints added)", "Migratory (moves joint to joint)", "Persistent in same joints"], cols=3))
story.append(spacer(2*mm))

story.append(Paragraph("Aggravating factors:", FIELD_LABEL))
story.append(checkbox_row(["Activity / movement", "Rest", "Cold / rainy weather", "New / full moon", "Morning / after inactivity", "Stress"], cols=3))
story.append(spacer(2*mm))

story.append(Paragraph("Relieving factors:", FIELD_LABEL))
story.append(checkbox_row(["Warmth / heat", "Gentle movement", "NSAIDs / analgesics", "Steroids", "Rest"], cols=5))
story.append(spacer(2*mm))

story.append(Paragraph("Radiation:", FIELD_LABEL))
story.append(checkbox_row(["No radiation", "Neck → arm (cervical involvement)", "Wrist → hand (carpal tunnel)", "Other:"], cols=4))
story.append(spacer(2*mm))

story.append(Paragraph("Pain severity — NRS (circle number):", FIELD_LABEL))
story.append(spacer(1*mm))
story.append(pain_scale())
story.append(Paragraph("0 = No pain   |   10 = Worst imaginable pain", SMALL))
story.append(spacer(3*mm))

# 3B – Stiffness
story.append(sub_header("B.  Morning Stiffness  — KEY DIAGNOSTIC FEATURE"))
story.append(spacer(1*mm))
story.append(Paragraph("⚠  In RA, morning stiffness typically lasts >1 hour. Duration differentiates RA (>1 hr) from OA (<30 min).", NOTE))
story.append(spacer(2*mm))

data = [
    [Paragraph("Duration of morning stiffness:", FIELD_LABEL), "", ""],
    [Paragraph("☐  <30 minutes", BODY), Paragraph("☐  30–60 minutes", BODY), Paragraph("☐  >1 hour — specify: ___ hrs ___ mins", BODY)],
]
t = Table(data, colWidths=[PAGE_W/3]*3)
t.setStyle(TableStyle([
    ("SPAN",         (0,0), (2,0)),
    ("LINEBELOW",    (0,0), (2,0), 0.5, MID_GREY),
    ("TOPPADDING",   (0,0), (-1,-1), 2),
    ("BOTTOMPADDING",(0,0), (-1,-1), 2),
]))
story.append(t)
story.append(spacer(2*mm))
story.append(Paragraph("Is stiffness relieved by:  ☐ Warmth   ☐ Movement   ☐ Hot shower   ☐ Other: _______________", BODY))
story.append(spacer(3*mm))

# 3C – Swelling
story.append(sub_header("C.  Joint Swelling"))
story.append(spacer(1*mm))
story.append(Paragraph("Nature of swelling:  ☐ Soft (effusion)   ☐ Boggy (synovial proliferation/pannus)   ☐ Bony / hard (late disease)", BODY))
story.append(spacer(2*mm))
story.append(Paragraph("Distribution:  ☐ Symmetric   ☐ Asymmetric   ☐ Migratory", BODY))
story.append(spacer(3*mm))

# 3D – Functional Impact
story.append(sub_header("D.  Functional Impairment (HAQ proxy)"))
story.append(spacer(1*mm))
story.append(Paragraph("Can the patient perform the following? (☐ Yes  ☐ No  ☐ With difficulty)", FIELD_LABEL))
story.append(spacer(1*mm))
functional_items = [
    "Dress / button clothes", "Grip objects / open jar", "Walk on flat ground",
    "Climb stairs", "Turn taps / doorknobs", "Write / type",
    "Rise from chair unaided", "Carry shopping bag", "Sleep without pain waking them"
]
story.append(checkbox_row(functional_items, cols=3))
story.append(spacer(4*mm))

# ── SECTION 4: EXTRA-ARTICULAR MANIFESTATIONS ────────────────────────────────
story.append(section_header("4.  EXTRA-ARTICULAR MANIFESTATIONS  (review of systems)"))
story.append(spacer(2*mm))
story.append(Paragraph("More common in RF-positive / ACPA-positive seropositive patients. Tick all that are present.", SMALL))
story.append(spacer(2*mm))
story.append(extra_articular_table())
story.append(spacer(4*mm))

# ── SECTION 5: JOINT INVOLVEMENT MAP ─────────────────────────────────────────
story.append(section_header("5.  JOINT INVOLVEMENT MAP"))
story.append(spacer(2*mm))
story.append(Paragraph("Tick each joint involved. Note: RA characteristically SPARES DIP joints — involvement suggests PsA or OA.", NOTE))
story.append(spacer(2*mm))
story.append(joint_involvement_table())
story.append(spacer(2*mm))
story.append(two_fields("Total tender joint count (TJC):", "Total swollen joint count (SJC):"))
story.append(spacer(4*mm))

# ── SECTION 6: PAST MEDICAL HISTORY ──────────────────────────────────────────
story.append(section_header("6.  PAST MEDICAL HISTORY"))
story.append(spacer(2*mm))

pmh_items = [
    ("Previous episodes of joint pain / swelling?", "☐ Yes  ☐ No"),
    ("Tuberculosis?", "☐ Yes  ☐ No"),
    ("Gonorrhoea / sexually transmitted infection?", "☐ Yes  ☐ No"),
    ("Hepatitis B or C?", "☐ Yes  ☐ No"),
    ("Cardiovascular disease (IHD, HF, stroke)?", "☐ Yes  ☐ No"),
    ("Diabetes mellitus?", "☐ Yes  ☐ No"),
    ("Hypertension?", "☐ Yes  ☐ No"),
    ("Osteoporosis / osteopenia?", "☐ Yes  ☐ No"),
    ("Hypothyroidism?", "☐ Yes  ☐ No"),
    ("Sicca syndrome / Sjogren's?", "☐ Yes  ☐ No"),
    ("Malignancy?", "☐ Yes  ☐ No"),
    ("Previous joint surgery / arthroplasty?", "☐ Yes  ☐ No"),
    ("Serious infections (hospitalisation)?", "☐ Yes  ☐ No"),
    ("Other:  __________________________________", "☐ Yes  ☐ No"),
]
pmh_rows = [[Paragraph(q, BODY), Paragraph(a, BODY)] for q, a in pmh_items]
t = Table(pmh_rows, colWidths=[PAGE_W*0.75, PAGE_W*0.25])
t.setStyle(TableStyle([
    ("FONTSIZE",      (0,0), (-1,-1), 8),
    ("ROWBACKGROUNDS",(0,0),(-1,-1),  [LIGHT_GREY, WHITE]),
    ("INNERGRID",     (0,0), (-1,-1), 0.3, MID_GREY),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_BLUE),
    ("TOPPADDING",    (0,0), (-1,-1), 3),
    ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t)
story.append(spacer(4*mm))

# ── SECTION 7: DRUG / TREATMENT HISTORY ──────────────────────────────────────
story.append(section_header("7.  DRUG AND TREATMENT HISTORY"))
story.append(spacer(2*mm))

tx_headers = [
    Paragraph("Drug / Treatment", FIELD_LABEL),
    Paragraph("Dose", FIELD_LABEL),
    Paragraph("Duration", FIELD_LABEL),
    Paragraph("Response", FIELD_LABEL),
    Paragraph("Side Effects", FIELD_LABEL),
]
tx_rows = [tx_headers]
for _ in range(7):
    tx_rows.append(["", "", "", "", ""])
t = Table(tx_rows, colWidths=[PAGE_W*0.28, PAGE_W*0.14, PAGE_W*0.14, PAGE_W*0.22, PAGE_W*0.22],
          rowHeights=[None] + [8*mm]*7)
t.setStyle(TableStyle([
    ("BACKGROUND",    (0,0), (-1,0),  SECTION_HDR),
    ("FONTNAME",      (0,0), (-1,0),  "Helvetica-Bold"),
    ("FONTSIZE",      (0,0), (-1,-1), 8),
    ("INNERGRID",     (0,0), (-1,-1), 0.3, MID_GREY),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_BLUE),
    ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
    ("ROWBACKGROUNDS",(0,1),(-1,-1),  [LIGHT_GREY, WHITE]),
    ("TOPPADDING",    (0,0), (-1,-1), 3),
    ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("LEFTPADDING",   (0,0), (-1,-1), 4),
]))
story.append(t)
story.append(spacer(2*mm))

story.append(Paragraph("Vaccination status (essential before biologic therapy):", FIELD_LABEL))
story.append(spacer(1*mm))
story.append(checkbox_row(["Hepatitis B  ☐ Done  ☐ Not done", "Pneumococcal  ☐ Done  ☐ Not done",
                            "Influenza (annual)  ☐ Done  ☐ Not done",
                            "COVID-19  ☐ Done  ☐ Not done",
                            "Varicella Zoster  ☐ Done  ☐ Not done",
                            "Herpes Zoster  ☐ Done  ☐ Not done"], cols=3))
story.append(spacer(4*mm))

# ── SECTION 8: ALLERGIES ─────────────────────────────────────────────────────
story.append(section_header("8.  DRUG ALLERGIES AND ADVERSE REACTIONS"))
story.append(spacer(2*mm))
story.append(text_box("Drug / Agent → Type of reaction → Date (if known):", height=12*mm))
story.append(spacer(4*mm))

# ── SECTION 9: FAMILY HISTORY ────────────────────────────────────────────────
story.append(section_header("9.  FAMILY HISTORY"))
story.append(spacer(2*mm))

fh_items = [
    ("Rheumatoid arthritis (first-degree relatives)?", "☐ Yes  ☐ No  ☐ Unknown"),
    ("Other autoimmune disease (SLE, psoriasis, IBD, thyroid)?", "☐ Yes  ☐ No  ☐ Unknown"),
    ("Gout?", "☐ Yes  ☐ No  ☐ Unknown"),
    ("Tuberculosis?", "☐ Yes  ☐ No  ☐ Unknown"),
    ("Premature cardiovascular disease?", "☐ Yes  ☐ No  ☐ Unknown"),
    ("Malignancy?", "☐ Yes  ☐ No  ☐ Unknown"),
    ("Periodontal disease (gum disease)?", "☐ Yes  ☐ No  ☐ Unknown"),
]
fh_rows = [[Paragraph(q, BODY), Paragraph(a, BODY)] for q, a in fh_items]
t = Table(fh_rows, colWidths=[PAGE_W*0.70, PAGE_W*0.30])
t.setStyle(TableStyle([
    ("FONTSIZE",      (0,0), (-1,-1), 8),
    ("ROWBACKGROUNDS",(0,0),(-1,-1),  [LIGHT_GREY, WHITE]),
    ("INNERGRID",     (0,0), (-1,-1), 0.3, MID_GREY),
    ("BOX",           (0,0), (-1,-1), 0.8, MID_BLUE),
    ("TOPPADDING",    (0,0), (-1,-1), 3),
    ("BOTTOMPADDING", (0,0), (-1,-1), 3),
    ("LEFTPADDING",   (0,0), (-1,-1), 5),
    ("VALIGN",        (0,0), (-1,-1), "MIDDLE"),
]))
story.append(t)
story.append(spacer(2*mm))
story.append(text_box("Additional family history details:", height=10*mm))
story.append(spacer(4*mm))

# ── SECTION 10: PERSONAL / SOCIAL HISTORY ────────────────────────────────────
story.append(section_header("10.  PERSONAL AND SOCIAL HISTORY"))
story.append(spacer(2*mm))

story.append(Paragraph("Smoking status:", FIELD_LABEL))
story.append(Paragraph(
    "☐ Never smoked     ☐ Ex-smoker — Quit date: ___________   Pack-years: ______     ☐ Current smoker — Pack-years: ______",
    BODY))
story.append(Paragraph("Note: Smoking is a major environmental risk factor for seropositive RA (ACPA/RF). Promotes PAD enzyme-mediated citrullination.", NOTE))
story.append(spacer(2*mm))

story.append(Paragraph("Alcohol:", FIELD_LABEL))
story.append(Paragraph(
    "☐ None     ☐ Occasional (<14 units/week)     ☐ Regular (>14 units/week)  "
    "  Units/week: ______   (Critical: interacts with methotrexate — hepatotoxicity risk)",
    BODY))
story.append(spacer(2*mm))

story.append(two_fields("Occupation (current / previous):", "Occupational exposures:"))
story.append(spacer(2*mm))

story.append(Paragraph("Physical activity:", FIELD_LABEL))
story.append(checkbox_row(["Sedentary", "Light activity", "Moderate exercise", "Vigorous exercise"], cols=4))
story.append(spacer(2*mm))

story.append(two_fields("Diet (Mediterranean? Obesity?):", "BMI: ____  kg/m²"))
story.append(spacer(2*mm))

story.append(Paragraph("Living situation:", FIELD_LABEL))
story.append(checkbox_row(["Lives alone", "Lives with family", "Carer available at home",
                            "Independent in ADLs", "Requires ADL assistance"], cols=3))
story.append(spacer(2*mm))

story.append(two_fields("Socioeconomic status:", "Mental health / Depression screening:"))
story.append(spacer(4*mm))

# ── SECTION 11: MENSTRUAL / OBSTETRIC HISTORY (women) ────────────────────────
story.append(section_header("11.  MENSTRUAL AND OBSTETRIC HISTORY  (women only)"))
story.append(spacer(2*mm))
story.append(Paragraph("Note: RA classically IMPROVES during pregnancy (2nd/3rd trimester) and FLARES postpartum.", NOTE))
story.append(spacer(2*mm))

story.append(three_fields("Menarche age:", "Menopause age:", "LMP:"))
story.append(spacer(2*mm))
story.append(two_fields("Gravida: ____  Para: ____", "Miscarriages: ____"))
story.append(spacer(2*mm))
story.append(Paragraph("Oral contraceptive use:  ☐ Never   ☐ Past   ☐ Current  (may be protective against RA onset)", BODY))
story.append(spacer(2*mm))
story.append(Paragraph("Postpartum flare experienced?  ☐ Yes   ☐ No", BODY))
story.append(spacer(2*mm))
story.append(two_fields("Current contraception (if relevant):", "Planning pregnancy?  ☐ Yes  ☐ No"))
story.append(spacer(4*mm))

# ── SECTION 12: ACR/EULAR CLASSIFICATION CRITERIA ───────────────────────────
story.append(section_header("12.  ACR/EULAR 2010 CLASSIFICATION CRITERIA  (Score ≥6 = Definite RA)"))
story.append(spacer(2*mm))
story.append(acr_eular_table())
story.append(spacer(4*mm))

# ── SECTION 13: DISEASE ACTIVITY ASSESSMENT ──────────────────────────────────
story.append(section_header("13.  DISEASE ACTIVITY ASSESSMENT  —  DAS28"))
story.append(spacer(2*mm))
story.append(das28_table())
story.append(spacer(2*mm))
story.append(two_fields("CDAI score:", "SDAI score:"))
story.append(spacer(4*mm))

# ── SECTION 14: INVESTIGATIONS REQUESTED ─────────────────────────────────────
story.append(section_header("14.  INVESTIGATIONS REQUESTED"))
story.append(spacer(2*mm))

story.append(sub_header("Serology / Immunology"))
story.append(checkbox_row([
    "Rheumatoid Factor (RF)", "Anti-CCP (ACPA)", "ANA",
    "Anti-dsDNA", "Anti-Sm", "ANCA",
    "Complement C3/C4", "Immunoglobulins", "Cryoglobulins",
], cols=3))
story.append(spacer(2*mm))

story.append(sub_header("Haematology / Biochemistry"))
story.append(checkbox_row([
    "Full Blood Count (FBC)", "ESR", "CRP",
    "Urea + Creatinine + eGFR", "Liver Function Tests", "Uric acid",
    "Fasting glucose / HbA1c", "Thyroid function (TSH)", "Lipid profile",
    "Serum calcium / phosphate", "Vitamin D (25-OH)", "Urine dipstick / PCR",
], cols=3))
story.append(spacer(2*mm))

story.append(sub_header("Imaging"))
story.append(checkbox_row([
    "X-ray hands / wrists (PA)", "X-ray feet (DP)", "X-ray cervical spine (lateral flexion/extension)",
    "Ultrasound joints (specify): ___________", "MRI (specify): ___________",
    "DEXA scan (bone density)",
    "Chest X-ray", "HRCT chest (ILD screen)", "Echocardiogram",
], cols=3))
story.append(spacer(2*mm))

story.append(sub_header("Pre-biologic / Pre-DMARD Screening"))
story.append(checkbox_row([
    "Mantoux / Quantiferon-TB Gold (TB screening)", "Hepatitis B sAg + sAb + cAb",
    "Hepatitis C antibody", "HIV (if indicated)",
    "VDRL / syphilis screen (if indicated)", "Varicella IgG titres",
], cols=2))
story.append(spacer(4*mm))

# ── SECTION 15: CLINICIAN ASSESSMENT & PLAN ──────────────────────────────────
story.append(section_header("15.  CLINICIAN ASSESSMENT AND MANAGEMENT PLAN"))
story.append(spacer(2*mm))
story.append(text_box("Diagnosis / Working Diagnosis:", height=10*mm))
story.append(spacer(2*mm))
story.append(text_box("Differential Diagnoses:", height=10*mm))
story.append(spacer(2*mm))
story.append(text_box("Management Plan (DMARDs, biologics, NSAIDs, steroids, physio, OT, surgical referral):", height=20*mm))
story.append(spacer(2*mm))
story.append(text_box("Patient Education Points Discussed:", height=12*mm))
story.append(spacer(2*mm))
story.append(two_fields("Review date:", "Rheumatologist signature:"))
story.append(spacer(4*mm))

# ── FOOTER NOTE ───────────────────────────────────────────────────────────────
story.append(hr())
story.append(Paragraph(
    "Template based on ACR/EULAR 2010 RA Classification Criteria | Aletaha D et al. Arthritis Rheum 2010;62:2569–2581 | "
    "Goldman-Cecil Medicine | Firestein & Kelley's Textbook of Rheumatology | S Das Manual on Clinical Surgery",
    SMALL
))
story.append(Paragraph("For clinical use only. Not a substitute for individual clinical judgement.", SMALL))

# ── BUILD ──────────────────────────────────────────────────────────────────────
doc.build(story)
print(f"PDF generated: {OUTPUT_PATH}")
Running Command

cd /home/daytona/workspace/ra-template && python generate_ra_template.py 2>&1

Running Command

ls -lh /home/daytona/workspace/ra-template/RA_Initial_Assessment_Template.pdf

Your PDF is ready. Download it here:

RA Initial Assessment Template

PDF Document · PDF


Here's what the template covers across 15 structured sections:
#SectionHighlights
1Patient DemographicsName, DOB, MRN, clinician, referral
2Chief Complaints7 common RA complaints with duration fields
3HOPIJoint pain (site, onset, character, pattern, aggravating/relieving), NRS pain scale (colour-coded 0-10), morning stiffness with tick-boxes, swelling characterisation, HAQ-proxy functional checklist
4Extra-articular Manifestations10-system table (constitutional, skin, eyes, CVS, respiratory, neuro, ENT, haem, renal, GI) with tick-box symptoms
5Joint Involvement Map12-joint grid with columns for R/L/bilateral, tenderness, swelling, warmth, deformity; TJC and SJC fields
6Past Medical History14-item checklist (TB, hepatitis, CV disease, osteoporosis, thyroid, malignancy etc.)
7Drug & Treatment History7-row medication table (drug, dose, duration, response, side effects) + vaccination status
8AllergiesFree-text reaction log
9Family HistoryRA, autoimmune, gout, TB, premature CVD, periodontal disease
10Personal / Social HistorySmoking (pack-years), alcohol (MTX interaction warning), diet/BMI, occupation, ADL independence
11Menstrual / Obstetric HistoryPregnancy improvement / postpartum flare note, OCP use, contraception
12ACR/EULAR 2010 CriteriaScoring table for joint involvement, serology, synovitis duration, acute phase reactants (score ≥6 = definite RA)
13Disease Activity — DAS28TJC/SJC/ESR/global VAS entry fields with DAS28 interpretation thresholds
14InvestigationsSerology, haematology, imaging, pre-biologic/pre-DMARD screening checklists
15Assessment & PlanDiagnosis, differentials, management plan, patient education, review date
This is a shared conversation. Sign in to Orris to start your own chat.