|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +TrialMap is a Vue.js 3 SPA that helps researchers optimize clinical trial eligibility criteria using real-world EHR data. It guides users through a 4-step wizard to select a cancer type, trial, endpoint, and eligibility criteria, then visualizes optimized pathway combinations. |
| 8 | + |
| 9 | +Deployed to GitHub Pages at `/TrialMap/` base path. All source code lives in the `web/` subdirectory. |
| 10 | + |
| 11 | +## Commands |
| 12 | + |
| 13 | +All commands must be run from the `web/` directory: |
| 14 | + |
| 15 | +```bash |
| 16 | +cd web |
| 17 | +npm install # Install dependencies |
| 18 | +npm run dev # Start dev server (localhost:5173) |
| 19 | +npm run build # Production build → web/dist/ |
| 20 | +npm run preview # Preview production build locally |
| 21 | +``` |
| 22 | + |
| 23 | +There are no lint or test scripts configured. |
| 24 | + |
| 25 | +## Architecture |
| 26 | + |
| 27 | +**View switching** is managed entirely in `web/src/Main.vue` via an `activeView` ref (`'home'` | `'step'` | `'result'`). There is no Vue Router — navigation is pure component conditional rendering. `StepView` is force-remounted via `stepViewKey` counter to ensure clean state resets. |
| 28 | + |
| 29 | +**PrimeVue** (v4) is the UI component library with auto-import via `unplugin-vue-components`. Components are resolved automatically — no explicit imports needed for PrimeVue components in templates. |
| 30 | + |
| 31 | +## Data Sources |
| 32 | + |
| 33 | +`web/public/data/meta_data.xlsx` has 3 sheets (parsed via `xlsx` library): |
| 34 | +- **Sheet 0 (metadata)**: One row per trial. Columns: `cancer_type`, `trial_name`, `line_of_therapy`, `treatment`, `control`. |
| 35 | +- **Sheet 1 (trial_criteria)**: Maps trials to their eligibility criteria. Columns: `trial_name`, `criteria_name`, `must_apply` (truthy = criteria cannot be relaxed). |
| 36 | +- **Sheet 2 (criteria)**: Criteria details. Columns: `criteria_name`, `criteria_description`, `type`. |
| 37 | + |
| 38 | +`web/public/data/trail_dataset/[TrialName]_results_web.csv` — Per-trial result files. Each row is one pathway (criteria combination). Columns include: `criteria` (comma-separated criteria names), `hr_os`, `hr_pfs`, `selog_hr_os`, `selog_hr_pfs`, `sucra_os`, `sucra_pfs`, `ae`, `ease`, `g_index`, `number_of_patients`, `path_id`. |
| 39 | + |
| 40 | +## Data Pipeline: StepView (Selection) |
| 41 | + |
| 42 | +In `StepView.vue`, on mount: |
| 43 | +1. Fetches `meta_data.xlsx`, parses all 3 sheets into `metadata`, `trialCriteriaRows`, `criteria` refs. |
| 44 | +2. **Step 1** — Extracts unique `cancer_type` values from sheet 0 → radio button list. |
| 45 | +3. **Step 2** — Filters sheet 0 by selected cancer type → unique `trial_name` list. Displays each trial's `line_of_therapy`, `treatment`, `control` metadata. |
| 46 | +4. **Step 3** — User selects endpoint (OS or PFS). PFS is disabled for AHNC cancer type (no progression data in Flatiron DB). |
| 47 | +5. **Step 2→3 transition** (`onclickNextTreatment`): Also derives criteria for the selected trial: |
| 48 | + - Filters sheet 1 by `trial_name` → `trialCriteria` (list of criteria names for this trial). |
| 49 | + - Computes `mustApplyCriteria` set (criteria with `must_apply` truthy in sheet 1). |
| 50 | + - Calls `computeAvailableTokensForTrial()`: fetches the trial CSV, parses all `criteria` column values (comma-split, normalized), builds a set of tokens that actually exist in the data. Falls back to sheet 1 names if CSV fetch fails. |
| 51 | + - Builds `filteredCriteria`: joins sheet 2 rows by `criteria_name`, adds `must_apply` and `no_data` flags. |
| 52 | +6. **Step 4** — Shows criteria table. `must_apply` criteria are pre-checked and disabled. User selects which criteria to enforce. |
| 53 | +7. On "Result" click: emits `{ selectedCancerType, selectedTreatment, selectedEndpoint, selectedCriteria }` to `Main.vue`. |
| 54 | + |
| 55 | +## Data Pipeline: ResultView (Visualization) |
| 56 | + |
| 57 | +In `ResultView.vue`, on mount: |
| 58 | +1. Re-fetches `meta_data.xlsx` independently (does not share state with StepView). Extracts trial metadata and criteria rows for the selected trial. |
| 59 | +2. Fetches `[TrialName]_results_web.csv`, parses with `xlsx` library. |
| 60 | +3. Calls `buildAllPathways(trailResult, selectedCriteria, selectedEndpoint, criteriaNameToIndex)`: |
| 61 | + - Normalizes all criteria tokens (lowercase, trimmed). |
| 62 | + - Builds `availableTokens` set from all CSV `criteria` column values. |
| 63 | + - Computes `effectiveSelected`: selected criteria that actually exist in the CSV (ignores missing ones for filtering). |
| 64 | + - Filters CSV rows: keeps rows where `criteria` column contains ALL `effectiveSelected` tokens. |
| 65 | + - Maps each row to a UI object with endpoint-specific fields: OS → `hr_os`/`selog_hr_os`/`sucra_os`, PFS → `hr_pfs`/`selog_hr_pfs`/`sucra_pfs`. |
| 66 | + - `pathName` is built from criteria indices (1-based) matching the criteria table order. |
| 67 | +4. Calls `computeNormalizationParams(allCsvRows, endpoint)`: computes min/max for each metric (HR, selogHR, AE, EASE, G-Index, # patients) across the **entire** CSV (not filtered). Some metrics are reversed (lower=better: HR, selogHR, AE, EASE). |
| 68 | +5. **Top 4 paths**: Default sort by `sucra` (descending). `updateDisplayedTop4()` takes first 4 from sorted `AllPathwaysResult`. |
| 69 | +6. **Virtual scrolling table**: Full pathway list uses PrimeVue's `virtualScrollerOptions` with lazy loading (`loadPathwaysLazy`). Placeholder objects fill the virtual array; real data is loaded in chunks as user scrolls. |
| 70 | +7. **Sorting**: User can sort by any column (HR, AE, # patients, EASE, G-Index, selogHR). `getSortedData()` re-sorts `AllPathwaysResult`. Default (SUCRA) sorts descending; column sorts respect `sortOrder`. Sort changes reset the virtual array and reload. |
| 71 | +8. **Origami plots (radar charts)**: Top 4 paths rendered as Chart.js radar charts. Values are normalized to [0.4, 1.0] range via `normalizeRangeValue()` — maps [min, max] → [0.4, 1.0] with optional reversal. Chart labels alternate between metric names and empty strings (gap spacers for visual separation). |
| 72 | +9. **Row selection interaction**: Selecting a row in the top paths table highlights corresponding criteria rows in the left-panel criteria table via `criteriaIndices` → `selectedCriteriaPath` → `criteriaRowClass`. |
| 73 | + |
| 74 | +## State Restoration |
| 75 | + |
| 76 | +When user clicks "Return to Selection" in ResultView: |
| 77 | +- Emits `returnToSelection` with `{ step: '4' }` to Main.vue. |
| 78 | +- Main.vue increments `stepViewKey` to force StepView remount, passes `initialStep='4'` and the previous `resultPayload` as `initialData`. |
| 79 | +- StepView's `restoreState()` re-derives all intermediate data (treatment list, criteria, etc.) from the saved selections, then jumps to step 4. |
| 80 | + |
| 81 | +## Deployment |
| 82 | + |
| 83 | +Pushing to `main` triggers the GitHub Actions workflow (`.github/workflows/main.yml`) which builds and deploys to GitHub Pages automatically. The Vite `base` is hardcoded to `/TrialMap/` in `vite.config.js`. |
0 commit comments