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¶
client/src/config.ts- Add bundle definitionsclient/src/types/index.ts- Add PROMISDomain and bundle typesclient/src/tasks/promis.ts- Accept domain selection, update scoringclient/src/main.ts- Parse URL params, update dev menuclient/src/i18n/locales/en.json- Bundle translationsclient/src/i18n/locales/fr.json- French translationsclient/src/pages/results.ts- Handle partial domain resultsserver/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_ORDERbased 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
nullfor domains that weren't administered - Add
administered_domainsarray to summary - Add
bundle_nameto 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)¶
- Overall health rating
- Quality of life
- Physical health
- Mental health (2 items)
- 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 itemsclient/src/i18n/locales/fr.json- Add PGH-7 translationsclient/src/config.ts- Add to bundles (especially Rare Disease)server/app/models/cognitive_data.py- Add GlobalHealthSummary
Bundle Integration¶
- Add
globalHealthdomain 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 itemsclient/src/i18n/locales/fr.json- Add UE translationsclient/src/config.ts- Add to neuromuscular bundleserver/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 conversionclient/src/tasks/promis.ts- Use T-score in summaryclient/src/pages/results.ts- Display T-scores with interpretationserver/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 calculationsclient/src/utils/cat-engine.ts(new) - CAT algorithmclient/src/data/promis-item-banks.ts(new) - IRT parametersclient/src/tasks/promis-cat.ts(new) - CAT version of PROMIS
URL Parameter¶
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¶
- Domain selection checkboxes
- Bundle presets dropdown
- Preview of selected items/duration
- Save/load custom configurations
- 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¶
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 handlingclient/src/i18n/locales/en.json- UI translationsclient/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¶
- Test each pre-defined bundle loads correct domains
- Verify scoring ignores non-administered domains
- Test results display with partial domain sets
- Verify backward compatibility (no bundle param = full assessment)
- Test French translations for bundle UI
Phase 2¶
- Verify PGH-7 items display correctly
- Verify Upper Extremity items display correctly
- Test scoring for new domains
- Test new bundles include new domains
Phase 3¶
- Verify raw-to-T-score conversion matches official tables
- Test interpretation labels
- Test results display with T-scores
Phase 4¶
- Verify item selection follows maximum information
- Test stopping rules trigger appropriately
- Verify theta estimation accuracy
- Compare CAT vs short form results
Phase 5¶
- Test domain selection UI
- Test URL generation
- Test preset loading
- 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)
PROMISDomaintype for the 6 PROMIS domainsPROMISBundleConfiginterfacePROMISBundleNametypePROMISOptionsinterface- Updated
TaskSummaries.promisto support optional domain scores
3. PROMIS Task Updates (tasks/promis.ts)
createPROMISTimelinenow accepts optionalPROMISOptionsresolvePromisOptionsfunction to handle bundle resolution- Dynamic timeline generation based on selected domains
calculatePROMISSummaryreturns only administered domain scores
4. URL Parameter Support (main.ts)
getPROMISOptions()function parses URL parameters- Supports:
?bundle=neurologyor?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_nameandadministered_domainsfields
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