Files
experiments-database/backend/prisma/schema.prisma
T
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

79 lines
2.1 KiB
Plaintext

generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Experiment {
id String @id @default(uuid())
title String
created_at DateTime @default(now())
template Json @default("[]")
subject_info_template Json @default("[]")
animals Animal[]
@@map("experiments")
}
model Animal {
id String @id @default(uuid())
experiment_id String
animal_id_string String
animal_name String
subject_info Json?
experiment Experiment @relation(fields: [experiment_id], references: [id], onDelete: Cascade)
daily_statuses DailyStatus[]
@@map("animals")
}
model DailyStatus {
id String @id @default(uuid())
animal_id String
date DateTime @db.Date
experiment_description String?
vitals String?
treatment String?
notes String?
custom_fields Json?
analysis_summary Json?
animal Animal @relation(fields: [animal_id], references: [id], onDelete: Cascade)
analyses DailyAnalysis[]
@@map("daily_statuses")
}
model DailyAnalysis {
id String @id @default(uuid())
daily_status_id String
daily_status DailyStatus @relation(fields: [daily_status_id], references: [id], onDelete: Cascade)
created_at DateTime @default(now())
file_name String?
timestamp_col String
note_col String
classifications Json
total_note_rows Int
session_end_ts Float?
sequence Json
category_counts Json
consecutive_stats Json
runs Json
@@map("daily_analyses")
}
model AuditLog {
id String @id @default(uuid())
table_name String
record_id String
action String
changes Json
timestamp DateTime @default(now())
@@map("audit_logs")
}