Commit Graph

32 Commits

Author SHA1 Message Date
Experiments DB Dev 1b0f083cc5 feat(frontend): persist bucket config to animal on successful analysis
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 08:05:07 -04:00
Experiments DB Dev 75a343d617 feat(frontend): render numerical-bucket plots in CSV-analysis results
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 08:03:48 -04:00
Experiments DB Dev 39c01c567c feat(frontend): add field picker + numerical bucket UI in classify phase
Replaces "+ New category" with a Note/Numerical type-aware picker, extends
CategoryBucket to render type badges, bulk-add, and custom-bucket remove controls,
and deletes the addCategory() back-compat shim from Task 7.
2026-05-01 08:01:41 -04:00
Experiments DB Dev ef17ee0642 feat(frontend): seed CsvAnalysis buckets from animal config 2026-05-01 07:58:56 -04:00
Experiments DB Dev ceaa3be200 refactor(frontend): unify CsvAnalysis state under buckets[] 2026-05-01 07:56:22 -04:00
Experiments DB Dev b9b8a4ef1d feat(frontend): add computeNumericSeries helper 2026-05-01 07:51:22 -04:00
Experiments DB Dev 0b83795665 feat(frontend): add extractNumber helper 2026-05-01 07:50:56 -04:00
Experiments DB Dev 6a6e6b310a feat(frontend): add animalsApi.updateCsvConfig
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 07:49:41 -04:00
Experiments DB Dev cdff6732b0 feat: manual metrics editor + past-analyses inline viewer on daily status
- AnalysisSummaryCard gains an "Edit manually" affordance powered by
  the new ManualMetricsForm (add/remove categories, edit counts,
  recompute total + success rate).
- New PastAnalysesViewer renders a tab-style selector of saved CSV
  analyses with an inline AnalysisResultsView panel.
- Reorganizes the page so the manual-metrics path is reachable when no
  analysis_summary exists yet.
- Sync package-lock to the recharts ^2.15.4 already declared in package.json.
2026-05-01 07:40:10 -04:00
Experiments DB Dev 5c0703c271 feat: per-chart customization — size, Y range, X zoom, independent X/Y data
Each chart card now has a "▾ customize" button. Clicking it reveals a
settings bar (only rendered when active — zero cost when collapsed):

  Size      — S / M / L height selector
  Y range   — min / max inputs to set the YAxis domain
  X zoom    — min / max inputs to clip the displayed X domain
  X data    — per-chart X field override, independent from the shared
              global X selector (each chart can use a different field)
  Y lines   — (counts chart only) toggle individual category lines on/off
  ↺ Reset   — shown only when any setting has been changed from default

The global X-axis selector and tick-step control remain at the component
level as the persistent defaults. Per-chart overrides are session-only
(non-persistent), resetting on page load.

Performance: animalRows computation in ExperimentAnalysisCharts is now
split from the per-chart data step, so a metric/X change only recomputes
the affected chart. Per-chart data only recomputes when that chart's
xFieldOverride changes; shared data is reused otherwise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 14:36:19 -04:00
Experiments DB Dev d2c8d69718 perf: server-side caching + remove bulk status fetch from experiment page
- Add backend/src/lib/cache.js — in-process TTL Map cache with
  prefix-based invalidation; no external dependencies

- Cache /experiments/:id/calendar?field=X per (experimentId, field).
  Invalidated immediately on any DailyStatus write.

- Cache /experiments/:id/daily-statuses per (experimentId, date, analysisOnly).
  Invalidated immediately on any DailyStatus write (create/update/delete
  and analysis-summary saves all clear the relevant prefix).

- Add ?analysisOnly=true to /daily-statuses: filters rows where
  analysis_summary IS NOT NULL. Charts are the only consumer and already
  discard rows without analysis data; pre-filtering server-side avoids
  sending the full status payload (71 rows → 1 for this experiment).

- ExperimentCalendar: remove allStatuses prop, fetch calendar data
  directly via API. The component now issues one small GET per field
  selection change instead of the parent loading thousands of rows upfront.

- ExperimentDetail: pass analysisOnly:true to getDailyStatuses;
  drop allStatuses prop from ExperimentCalendar. Cold experiment page
  load no longer fetches every daily status record.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 14:03:59 -04:00
Experiments DB Dev 52d5fca5d1 feat: store matching metadata on session file registration
Each saved file now records three additional fields:
  matched_identifier    — the literal animal_id_string or animal_name
                          substring that matched in the filename
  animal_id_string_snap — snapshot of animal.animal_id_string at
                          registration time
  animal_name_snap      — snapshot of animal.animal_name at
                          registration time

This lets future consumers (scripts, exports, re-matching) identify
the subject from the file record alone, even if animal data changes.
The matched identifier is also shown in the drop preview UI (↳ 2852).

Tests: 25 total (+2 for new payload fields).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 13:41:36 -04:00
Experiments DB Dev 5cd6d2a8a3 fix: move session file registry above modification history
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 13:37:30 -04:00
Experiments DB Dev b73768ee29 feat: add session file registry to daily status detail page
FileDropZone now appears on each subject's individual daily status
page, scoped to that one status. Files are matched against that
subject's animal_id_string and animal_name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 13:36:33 -04:00
Experiments DB Dev 66e79c1780 fix: match filenames against both animal_id_string and animal_name
Multi-word names ("Rat A") now allow any word separator in the
filename — space, underscore, or dash — so "Rat_A_session.csv"
and "Rat-A-data.bin" both match. Single-word names and numeric IDs
are unchanged. Partial overlaps still rejected ("Rat" won't hit
"Rationale"). Tests: 5 new name-based match cases (24 total).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 13:33:10 -04:00
Experiments DB Dev daf36908ef feat: session file registry with drag-and-drop on day view
Adds a FileDropZone at the bottom of the day view where users can
drop all session files at once. Filenames are regex-matched to
subjects using their animal_id_string (word-boundary aware, so
"2852" won't match "28520"). Only metadata is recorded — files
are never uploaded.

Backend: new session_files table (id, daily_status_id, filename,
file_size BIGINT, file_type, last_modified, notes). Routes:
  POST /api/daily-statuses/:id/files  (batch create)
  GET  /api/daily-statuses/:id/files
  DELETE /api/session-files/:id

Frontend: FileDropZone shows per-subject match groups, unmatched
files, and a warning for subjects with no match. After saving,
registered files are displayed with delete capability.

test-files/: generate_test_files.sh creates 4 dummy files per
subject (2×bin, 2×csv) with correct naming for drag-and-drop
testing. Pre-generated for 2026-04-23 with IDs 2852/3076/3077/3078.

Tests: 19 tests covering drag state, regex matching, save payload,
partial-overlap prevention, multi-subject batching, and delete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 13:12:45 -04:00
Experiments DB Dev dc46af8d31 feat: inline cell editing and prev/next day navigation in day view
Clicking any table cell opens an inline input (text or textarea per
field type). Enter or blur saves; Escape cancels. Unchanged values
skip the API call. Custom fields merge with existing custom_fields;
builtin fields use their key directly.

Prev/next buttons at the bottom navigate to adjacent days that have
data for the selected field, preserving the ?field= param. Day list
is fetched via getCalendar and sorted chronologically.

Tests: 41 total (added 15 new tests for cell editing and day nav).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:59:34 -04:00
Experiments DB Dev c8f0c5f298 feat: replace calendar day modal with dedicated day-view page
Calendar day clicks now navigate to /experiments/:id/day/:date
(with ?field= param). ExperimentDayView shows a sticky-column table
of all subjects with data that day, inline edit via modal, and a
session-metrics bar chart from analysis_summary. Subject name links
to /daily-statuses/:id. Backend adds ?date= filtering to
GET /experiments/:id/daily-statuses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:46:35 -04:00
Experiments DB Dev 6ecc6dc19a test: document and test analysis_summary bar chart contract
5 tests cover the bar chart rules:
- Shows when analysis_summary.total > 0
- Hidden when no summary or total=0
- Multiple animals each with their own summary
- Animals excluded from day view (empty field) also excluded from chart
- animalsWithData now filters by selected field value (not just record presence)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:31:36 -04:00
Experiments DB Dev 5f3ad1d408 fix: bar chart uses analysis_summary from each day's status records
X-axis: animals with valid field data on that day.
Bars: Success / Failure / Total from status.analysis_summary for that day.
Each day shows its own session metrics. Animals without analysis_summary
or with total=0 are excluded from the chart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:29:15 -04:00
Experiments DB Dev b888c1b76d fix: day modal bar chart plots that session's field values per animal
X-axis: animal names, Y-axis: the selected field's numeric value recorded
that specific day. Animals with empty or non-numeric values are excluded.
Each day's chart is independent — different animals, different values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:24:16 -04:00
Experiments DB Dev 7439a1a101 fix: day modal bar chart shows experiment-wide session stats per subject
Shows Total / Success / Failure bars across ALL sessions for each animal
that has at least one valid (non-empty) value for the chosen field.
Animals with zero valid field entries are excluded from the chart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:21:52 -04:00
Experiments DB Dev 9b51250d8e fix: bar chart in day modal shows that day's field values per subject
Previously showed cumulative success/failure counts across all days.
Now shows the selected field's actual value for each subject on that
specific day — numeric values only, subjects with empty or non-numeric
values are excluded from the chart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:18:48 -04:00
Experiments DB Dev 8b40856205 fix: hide subjects with no data in day modal; fix failure bar rendering
- Day modal now only shows animals that have a status record for that day
- Removed '+ Add' button and 'No data' placeholder for absent subjects
- Fixed failure bars disappearing in recharts: removed radius from all bars,
  added minPointSize=2 on failure bar so zero-value bars remain visible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:13:05 -04:00
Experiments DB Dev d764bc66fb fix: move bar chart inside day modal instead of below calendar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 12:06:52 -04:00
Experiments DB Dev 755caca09e feat: permanent calendar below analytics, editable day modal, per-subject bar chart 2026-04-23 15:24:00 +00:00
Experiments DB Dev fe23085e0f fix: resolve DailyStatusForm test stall and add label accessibility
- Use module-level EMPTY_TEMPLATE constant as default prop to prevent
  useEffect infinite loop (new [] each render changed identity every cycle)
- Add htmlFor to date and dynamic field labels for proper a11y association
- Add role=alert to date error paragraph so tests can find it
- Update tests to pass a template with builtin fields (required by template-
  driven component design) and mock experimentsApi
- Add --max-old-space-size=8192 to test scripts to prevent OOM on full suite

All 72 frontend tests now pass across 7 suites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 10:41:17 -04:00
Experiments DB Dev 9535f86970 feat: add experiment calendar view with per-day subject counts
New ExperimentCalendar component added as a third view mode (List / Group / Calendar)
on the Experiment Detail page.

Backend:
- GET /api/experiments/:id/calendar?field=<fieldIdOrBuiltinKey>
  Returns { field, days: { "YYYY-MM-DD": count } } where count = number of
  subjects with a non-null/non-empty value for the chosen field on that date.
  Supports both builtin fields (vitals, treatment, notes, experiment_description)
  and custom fields addressed by fieldId UUID.

Frontend:
- ExperimentCalendar component: navigable monthly grid, field selector
  (defaults to first field with "weight" in label, else first active field),
  blue badges showing subject count per day, click to open modal with all
  subjects' data for that day.
- Integrated into ExperimentDetail as displayMode "calendar", persisted
  in localStorage alongside the existing list/group modes.
- experimentsApi.getCalendar() added to API client.

Tests:
- backend/tests/calendar.test.js: 8 unit tests (404, empty days, builtin
  count, custom field count, whitespace ignored, multi-month, field echo,
  missing param).
- frontend/tests/ExperimentCalendar.test.jsx: 13 RTL tests covering
  rendering, default field selection, count badges, month navigation,
  field-change re-fetch, day click modal, and multi-subject display.

All 47 backend + 13 calendar frontend tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 09:26:35 -04:00
Experiments DB Dev b7374777d3 feat(template): stable fieldId per field — custom_fields keyed by UUID, not label slug 2026-04-15 14:01:14 -04:00
Experiments DB Dev 86a56427b7 feat(template): per-experiment daily record template — rename/add/hide fields, custom_fields JSONB 2026-04-15 13:56:17 -04:00
Experiments DB Dev 9b3c883bf0 fix(security): rate limiting, CORS restriction, tableName allowlist, non-root Docker, migration entrypoint, aria-live 2026-04-15 13:25:18 -04:00
Experiments DB Dev c32a5d200d feat(frontend): React UI — pages, forms, audit log section, Tailwind, Vite build 2026-04-15 13:18:31 -04:00