Skip to content

PROMIS Comprehensive Implementation Plan

Quick Summary

What Details
Phase 1 Domain bundling with URL parameters
Phase 2 Add Global Health (PGH-7) and Upper Extremity domains
Phase 3 T-Score conversion with lookup tables
Phase 4 CAT (Computer Adaptive Testing) implementation
Phase 5 Bundle Builder UI for researchers
Backward Compatible Yes - no params = current 25-item assessment

Phase 1: Domain Bundling (Core)

Files to Modify

  1. client/src/config.ts - Add bundle definitions
  2. client/src/types/index.ts - Add PROMISDomain and bundle types
  3. client/src/tasks/promis.ts - Accept domain selection, update scoring
  4. client/src/main.ts - Parse URL params, update dev menu
  5. client/src/i18n/locales/en.json - Bundle translations
  6. client/src/i18n/locales/fr.json - French translations
  7. client/src/pages/results.ts - Handle partial domain results
  8. server/app/models/cognitive_data.py - Make domain scores optional

Overview

Implement a system to allow researchers to select specific PROMIS domains (bundles) rather than always administering all 6 domains. This enables condition-specific assessments as described in the PROMIS bundles documentation.

URL Parameter Design

# Pre-defined bundle:
?task=promis&bundle=neurology

# Custom domain selection:
?task=promis&domains=fatigue,anxiety,painInterference

# Full assessment (default):
?task=promis

Implementation Steps

Step 1: Define Bundle Types and Configuration

File: client/src/config.ts

Add PROMIS bundle configuration:

promis: {
  // Pre-defined bundles based on docs/promis-bundles.md
  bundles: {
    full: {
      name: 'Full Assessment',
      domains: ['mobility', 'anxiety', 'depression', 'fatigue', 'peerRelationships', 'painInterference'],
      includePainIntensity: true,
    },
    neurology: {
      name: 'Neurology Bundle',
      description: 'Mobility, Fatigue, Pain Interference, Anxiety, Peer Relationships',
      domains: ['mobility', 'fatigue', 'painInterference', 'anxiety', 'peerRelationships'],
      includePainIntensity: true,
    },
    chronicPain: {
      name: 'Chronic Pain Bundle',
      description: 'Pain Interference, Pain Intensity, Fatigue, Mobility, Depression, Anxiety',
      domains: ['painInterference', 'fatigue', 'mobility', 'depression', 'anxiety'],
      includePainIntensity: true,
    },
    rareDisease: {
      name: 'Rare Disease Bundle',
      description: 'Mobility, Fatigue, Pain Interference, Anxiety',
      domains: ['mobility', 'fatigue', 'painInterference', 'anxiety'],
      includePainIntensity: true,
    },
    mentalHealth: {
      name: 'Mental Health Bundle',
      description: 'Anxiety, Depression, Peer Relationships',
      domains: ['anxiety', 'depression', 'peerRelationships'],
      includePainIntensity: false,
    },
    fatigueOnly: {
      name: 'Fatigue Focus',
      description: 'Fatigue domain only',
      domains: ['fatigue'],
      includePainIntensity: false,
    },
  },
  defaultBundle: 'full',
}

Step 2: Create Bundle Types

File: client/src/types/index.ts

Add new types for PROMIS bundling:

export type PROMISDomain =
  | 'mobility'
  | 'anxiety'
  | 'depression'
  | 'fatigue'
  | 'peerRelationships'
  | 'painInterference';

export interface PROMISBundleConfig {
  name: string;
  description?: string;
  domains: PROMISDomain[];
  includePainIntensity: boolean;
}

export type PROMISBundleName =
  | 'full'
  | 'neurology'
  | 'chronicPain'
  | 'rareDisease'
  | 'mentalHealth'
  | 'fatigueOnly'
  | 'custom';

Step 3: Update PROMIS Task to Accept Domain Selection

File: client/src/tasks/promis.ts

Modify createPROMISTimeline to accept optional domain configuration:

export function createPROMISTimeline(
  jsPsych: JsPsych,
  options?: {
    domains?: PROMISDomain[];
    includePainIntensity?: boolean;
    bundleName?: string;
  }
): object[]

Key changes:

  • Filter DOMAIN_ORDER based on selected domains
  • Make Pain Intensity page conditional on includePainIntensity
  • Update progress indicators to reflect actual domain count
  • Store bundle metadata in trial data for analysis

Step 4: Update Summary Calculation

File: client/src/tasks/promis.ts

Modify calculatePROMISSummary to handle partial domain sets:

  • Return null for domains that weren't administered
  • Add administered_domains array to summary
  • Add bundle_name to summary for reference

Step 5: Update Backend Model

File: server/app/models/cognitive_data.py

Update PROMISSummary to support optional domains:

class PROMISSummary(BaseModel):
    completed: bool
    items_answered: int
    bundle_name: Optional[str] = None
    administered_domains: list[str] = Field(default_factory=list)
    # Make all domain scores optional
    mobility_raw: Optional[int] = None
    mobility_max: Optional[int] = None
    # ... same for other domains

Step 6: Add Bundle Selection to Dev Menu & Parse URL Parameters

File: client/src/main.ts

6a. Add helper function to parse PROMIS options:

function getPROMISOptions(): { domains?: PROMISDomain[]; bundle?: string } | null {
  const params = new URLSearchParams(window.location.search);
  const bundle = params.get('bundle');
  const domains = params.get('domains');

  if (bundle) {
    return { bundle };
  }
  if (domains) {
    return { domains: domains.split(',') as PROMISDomain[] };
  }
  return null; // Use default (full)
}

6b. Update PROMIS task invocation:

// Task 25: PROMIS
if (!devTask || devTask === 'all' || devTask === 'promis' || devTask === '25') {
  const promisOptions = getPROMISOptions();
  timeline.push(createTaskIntroTrial('promis', 25, TOTAL_TASKS));
  timeline.push(...createPROMISTimeline(jsPsych, promisOptions));
}

6c. Add bundle links to dev menu:

<div style="margin-top: 1.5rem; border-top: 1px solid #ddd; padding-top: 1rem;">
  <h3 style="color: #0277BD;">PROMIS Bundles</h3>
  <a href="?task=promis" class="dev-btn">Full Assessment (25 items)</a>
  <a href="?task=promis&bundle=neurology" class="dev-btn">Neurology Bundle</a>
  <a href="?task=promis&bundle=chronicPain" class="dev-btn">Chronic Pain Bundle</a>
  <a href="?task=promis&bundle=rareDisease" class="dev-btn">Rare Disease Bundle</a>
  <a href="?task=promis&bundle=mentalHealth" class="dev-btn">Mental Health Bundle</a>
  <a href="?task=promis&domains=fatigue,anxiety" class="dev-btn">Custom: Fatigue + Anxiety</a>
</div>

Step 7: Update i18n for Bundle UI

File: client/src/i18n/locales/en.json and fr.json

Add bundle names and descriptions:

"promis": {
  "bundles": {
    "full": { "name": "Full Assessment", "description": "All 6 domains + pain intensity" },
    "neurology": { "name": "Neurology", "description": "Optimized for neurological conditions" },
    // ...
  },
  "domainDescriptions": {
    "mobility": "Questions about physical function and movement",
    "anxiety": "Questions about worry, nervousness, and fear",
    // ...
  }
}

Step 8: Update Results Display

File: client/src/pages/results.ts

Modify renderPROMISSection to:

  • Only display administered domains
  • Show bundle name if available
  • Handle null/undefined domain scores gracefully

Files to Modify

File Changes
client/src/config.ts Add PROMIS bundle definitions
client/src/types/index.ts Add PROMISDomain, PROMISBundleConfig types
client/src/tasks/promis.ts Accept domain selection, update scoring
client/src/main.ts Parse bundle URL param, update dev menu
client/src/i18n/locales/en.json Add bundle translations
client/src/i18n/locales/fr.json Add bundle translations (French)
client/src/pages/results.ts Handle partial domain results
server/app/models/cognitive_data.py Make domain scores optional

Bundle Definitions (from docs/promis-bundles.md)

Neurology Bundle (Core)

  • Physical Function – Mobility
  • Fatigue
  • Pain Interference
  • Anxiety
  • Peer Relationships

Chronic Pain Bundle (Core)

  • Pain Interference (primary)
  • Pain Intensity
  • Fatigue
  • Physical Function – Mobility
  • Depressive Symptoms
  • Anxiety

Rare Disease Bundle (Core)

  • Global Health (not currently implemented)
  • Physical Function – Mobility
  • Fatigue
  • Pain Interference
  • Anxiety

Phase 2: Additional Domains

2A: PROMIS Pediatric Global Health (PGH-7)

Source:HealthMeasures PROMIS Global Health

Overview

  • 7 items assessing overall physical, mental, and social health
  • Ages 8-17 (self-report) or 5-17 (parent proxy)
  • Single composite score of overall health
  • Raw score range: 7-35

Items (all use 5-point Likert scale)

  1. Overall health rating
  2. Quality of life
  3. Physical health
  4. Mental health (2 items)
  5. Social health (2 items)

Files to Create/Modify

  • client/src/tasks/promis-global-health.ts (new)
  • client/src/i18n/locales/en.json - Add PGH-7 items
  • client/src/i18n/locales/fr.json - Add PGH-7 translations
  • client/src/config.ts - Add to bundles (especially Rare Disease)
  • server/app/models/cognitive_data.py - Add GlobalHealthSummary

Bundle Integration

  • Add globalHealth domain to bundles that need it
  • Rare Disease bundle will include Global Health as recommended

2B: PROMIS Pediatric Upper Extremity (8 items)

Source:SRALAB PROMIS Pediatric Upper Extremity

Overview

  • 8 items from Upper Extremity short form v2.0
  • Ages 8-17 (self-report) or 5-17 (parent proxy)
  • Focus: shoulder, arm, hand activities
  • 7-day recall period
  • Raw score range: 8-40

Items (5-point ability scale)

Questions about activities like:

  • Writing
  • Using buttons
  • Opening containers
  • Self-care tasks (toothpaste, etc.)

Files to Create/Modify

  • client/src/tasks/promis-upper-extremity.ts (new)
  • client/src/i18n/locales/en.json - Add UE items
  • client/src/i18n/locales/fr.json - Add UE translations
  • client/src/config.ts - Add to neuromuscular bundle
  • server/app/models/cognitive_data.py - Add UpperExtremitySummary

New Bundle

neuromuscular: {
  name: 'Neuromuscular Bundle',
  domains: ['mobility', 'upperExtremity', 'fatigue', 'painInterference'],
  includePainIntensity: true,
}

Phase 3: T-Score Conversion

Source:HealthMeasures T-Score Maps

Overview

Convert raw scores to standardized T-scores (mean=50, SD=10) using official lookup tables.

Implementation

File: client/src/utils/promis-scoring.ts (new)

// Lookup tables for each domain (from official PROMIS manuals)
const T_SCORE_TABLES = {
  mobility: {
    // Raw score -> { tScore, standardError }
    4: { tScore: 18.3, se: 5.1 },
    5: { tScore: 22.4, se: 3.9 },
    // ... full table from manual
    20: { tScore: 57.8, se: 2.1 },
  },
  anxiety: { /* ... */ },
  depression: { /* ... */ },
  fatigue: { /* ... */ },
  peerRelationships: { /* ... */ },
  painInterference: { /* ... */ },
  globalHealth: { /* ... */ },
  upperExtremity: { /* ... */ },
};

export function rawToTScore(domain: PROMISDomain, rawScore: number): {
  tScore: number;
  standardError: number;
  percentile?: number;
}

export function interpretTScore(domain: PROMISDomain, tScore: number): {
  severity: 'normal' | 'mild' | 'moderate' | 'severe';
  description: string;
}

Interpretation Guidelines

  • T-score 50 = US population mean
  • For negative domains (anxiety, depression, fatigue, pain):
  • <55: Normal
  • 55-60: Mild
  • 60-70: Moderate
  • 70: Severe

  • For positive domains (mobility, peer relationships):
  • 45: Normal

  • 40-45: Mild impairment
  • 30-40: Moderate impairment
  • <30: Severe impairment

Files to Modify

  • client/src/utils/promis-scoring.ts (new) - Lookup tables and conversion
  • client/src/tasks/promis.ts - Use T-score in summary
  • client/src/pages/results.ts - Display T-scores with interpretation
  • server/app/models/cognitive_data.py - Add T-score fields

Phase 4: Computer Adaptive Testing (CAT)

Source:HealthMeasures CAT Overview

Overview

Implement adaptive item selection using Item Response Theory (IRT) to reduce burden while maintaining precision.

IRT Model: Graded Response Model (GRM)

Each item has:

  • Discrimination parameter (α): How well item differentiates ability levels
  • Threshold parameters (β₁, β₂, β₃, β₄): Difficulty levels for each response category

CAT Algorithm

interface CATState {
  domain: PROMISDomain;
  theta: number;           // Current ability estimate (starts at 0)
  standardError: number;   // Current precision (starts high)
  itemsAdministered: string[];
  responses: Record<string, number>;
}

// Item selection: Maximum Information
function selectNextItem(state: CATState, itemBank: ItemBank): Item {
  const availableItems = itemBank.filter(i => !state.itemsAdministered.includes(i.id));
  return maxBy(availableItems, item => itemInformation(item, state.theta));
}

// Ability estimation: Expected A Posteriori (EAP)
function updateTheta(state: CATState): number {
  // Bayesian estimation using prior and likelihood
}

// Stopping rules
function shouldStop(state: CATState): boolean {
  return (
    state.standardError < 0.3 ||           // Precision target met
    state.itemsAdministered.length >= 12 || // Max items reached
    (state.itemsAdministered.length >= 4 && state.standardError < 0.4) // Early stop
  );
}

Item Banks Required

Need to obtain official PROMIS IRT parameters for each domain:

  • Item ID
  • Discrimination (α)
  • Thresholds (β₁-β₄)

Files to Create

  • client/src/utils/irt-engine.ts (new) - IRT calculations
  • client/src/utils/cat-engine.ts (new) - CAT algorithm
  • client/src/data/promis-item-banks.ts (new) - IRT parameters
  • client/src/tasks/promis-cat.ts (new) - CAT version of PROMIS

URL Parameter

?task=promis&mode=cat&domains=anxiety,fatigue

Trade-offs

Aspect Short Forms CAT
Items Fixed (4-8) Variable (4-12)
Precision Good Excellent
Complexity Low High
Comparability Direct Requires IRT scoring

Phase 5: Bundle Builder UI

Overview

Visual interface for researchers to create custom domain combinations without URL parameters.

Features

  1. Domain selection checkboxes
  2. Bundle presets dropdown
  3. Preview of selected items/duration
  4. Save/load custom configurations
  5. Generate shareable URL

Implementation

File: client/src/pages/bundle-builder.ts (new)

interface BundleBuilderState {
  selectedDomains: PROMISDomain[];
  includePainIntensity: boolean;
  includeGlobalHealth: boolean;
  useCAT: boolean;
  customName?: string;
}

function renderBundleBuilder(): void {
  // Domain checkboxes with descriptions
  // Preset bundle dropdown
  // Estimated completion time
  // "Generate URL" button
  // "Start Assessment" button
}

URL Route

?bundle-builder  → Opens the bundle builder UI

Dev Menu Integration

Add "Bundle Builder" link to existing dev menu

Files to Create/Modify

  • client/src/pages/bundle-builder.ts (new)
  • client/src/main.ts - Add route handling
  • client/src/i18n/locales/en.json - UI translations
  • client/src/i18n/locales/fr.json - French UI translations

Implementation Order

Phase Effort Dependencies Status
Phase 1: Domain Bundling Medium None ← IMPLEMENTING NOW
Phase 2A: Global Health (PGH-7) Low Phase 1 Planned
Phase 2B: Upper Extremity Low Phase 1 Planned
Phase 3: T-Score Conversion Medium Phases 1-2 Planned
Phase 4: CAT High Phases 1-3 Planned
Phase 5: Bundle Builder UI Medium Phases 1-2 Planned

Current Focus: Phase 1 only. Other phases will be implemented incrementally.


Testing Plan

Phase 1

  1. Test each pre-defined bundle loads correct domains
  2. Verify scoring ignores non-administered domains
  3. Test results display with partial domain sets
  4. Verify backward compatibility (no bundle param = full assessment)
  5. Test French translations for bundle UI

Phase 2

  1. Verify PGH-7 items display correctly
  2. Verify Upper Extremity items display correctly
  3. Test scoring for new domains
  4. Test new bundles include new domains

Phase 3

  1. Verify raw-to-T-score conversion matches official tables
  2. Test interpretation labels
  3. Test results display with T-scores

Phase 4

  1. Verify item selection follows maximum information
  2. Test stopping rules trigger appropriately
  3. Verify theta estimation accuracy
  4. Compare CAT vs short form results

Phase 5

  1. Test domain selection UI
  2. Test URL generation
  3. Test preset loading
  4. Test custom configuration persistence

Phase 1: PROMIS Domain Bundling - Complete

What Was Implemented

1. Bundle Definitions (config.ts)

  • 6 pre-defined bundles: full, neurology, chronicPain, rareDisease, mentalHealth, fatigueOnly
  • Each bundle specifies which domains to include and whether to include pain intensity

2. Type Definitions (types/index.ts)

  • PROMISDomain type for the 6 PROMIS domains
  • PROMISBundleConfig interface
  • PROMISBundleName type
  • PROMISOptions interface
  • Updated TaskSummaries.promis to support optional domain scores

3. PROMIS Task Updates (tasks/promis.ts)

  • createPROMISTimeline now accepts optional PROMISOptions
  • resolvePromisOptions function to handle bundle resolution
  • Dynamic timeline generation based on selected domains
  • calculatePROMISSummary returns only administered domain scores

4. URL Parameter Support (main.ts)

  • getPROMISOptions() function parses URL parameters
  • Supports: ?bundle=neurology or ?domains=fatigue,anxiety
  • Validation with fallback to full assessment

5. Dev Menu (main.ts)

  • Added PROMIS Bundles section with links to all pre-defined bundles
  • Example custom domain selection link

6. Results Display (results.ts)

  • Updated to handle partial domain sets
  • Only shows administered domains
  • Displays bundle name in header

7. Backend Model (cognitive_data.py)

  • All domain scores are now optional
  • Added bundle_name and administered_domains fields

Usage Examples

# Pre-defined bundles:
?task=promis&bundle=neurology     # 21 items
?task=promis&bundle=chronicPain   # 21 items  
?task=promis&bundle=rareDisease   # 17 items
?task=promis&bundle=mentalHealth  # 12 items
?task=promis&bundle=fatigueOnly   # 4 items

# Custom domain selection:
?task=promis&domains=fatigue,anxiety           # 9 items
?task=promis&domains=mobility,fatigue&pain=false  # 8 items (no pain intensity)

# Full assessment (default):
?task=promis                      # 25 items

Future Phases (Documented in Plan)

  • Phase 2: Global Health (PGH-7) + Upper Extremity domains
  • Phase 3: T-Score conversion
  • Phase 4: Computer Adaptive Testing (CAT)
  • Phase 5: Bundle Builder UI