diff --git a/frontend/src/components/ExperimentCalendar.jsx b/frontend/src/components/ExperimentCalendar.jsx
index 2d53ae7..63deb5b 100644
--- a/frontend/src/components/ExperimentCalendar.jsx
+++ b/frontend/src/components/ExperimentCalendar.jsx
@@ -53,25 +53,22 @@ function DayPanel({ date, animals, allStatuses, template, experimentId, selected
const fieldLabel = fieldOptions.find((f) => f.value === selectedField)?.label;
- // Bar chart: this day's field value per animal (numeric only, filtered to animals present that day)
+ // Bar chart: animals with analysis_summary on this day → Success/Failure/Total from that summary
const barData = useMemo(() => {
- if (!selectedField) return [];
- const isBuiltin = BUILTIN_KEYS_SET.has(selectedField);
return animalsWithData
.map((animal) => {
const status = statusByAnimal[animal.id];
- if (!status) return null;
- const raw = isBuiltin ? status[selectedField] : status.custom_fields?.[selectedField];
- if (raw === null || raw === undefined || String(raw).trim() === '') return null;
- const num = parseFloat(raw);
- if (isNaN(num)) return null;
+ const summary = status?.analysis_summary;
+ if (!summary || summary.total === 0) return null;
return {
name: animal.animal_name || animal.animal_id_string || animal.id.slice(0, 8),
- value: num,
+ total: summary.total,
+ success: summary.counts?.Success ?? 0,
+ failure: summary.counts?.Failure ?? 0,
};
})
.filter(Boolean);
- }, [animalsWithData, statusByAnimal, selectedField]);
+ }, [animalsWithData, statusByAnimal]);
return (
@@ -120,16 +117,17 @@ function DayPanel({ date, animals, allStatuses, template, experimentId, selected
{/* Bar chart: overall per-subject completeness */}
{barData.length > 0 && (
-
- {fieldLabel ?? 'Field'} — this session
-
+ Session metrics
- [v, fieldLabel ?? 'Value']} />
-
+
+
+
+
+
diff --git a/frontend/tests/ExperimentCalendar.test.jsx b/frontend/tests/ExperimentCalendar.test.jsx
index 7aefa27..589cfcf 100644
--- a/frontend/tests/ExperimentCalendar.test.jsx
+++ b/frontend/tests/ExperimentCalendar.test.jsx
@@ -38,7 +38,7 @@ function todayStr() {
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}
-function makeStatus(animalId, date, customFields = {}, vitals = null) {
+function makeStatus(animalId, date, customFields = {}, vitals = null, analysisSummary = null) {
return {
id: `s-${animalId}-${date}`,
animal_id: animalId,
@@ -48,6 +48,7 @@ function makeStatus(animalId, date, customFields = {}, vitals = null) {
treatment: null,
notes: null,
custom_fields: customFields,
+ analysis_summary: analysisSummary,
};
}
@@ -248,10 +249,11 @@ describe('day click modal', () => {
// ── Bar chart (inside day modal) ──────────────────────────────────────────────
describe('bar chart inside day modal', () => {
- it('renders bar chart with that day\'s numeric field values', async () => {
+ it('renders session metrics bar chart when analysis_summary has data', async () => {
const dateStr = todayStr();
+ const summary = { total: 18, counts: { Success: 5, Failure: 13 }, success_rate: 0.28 };
const statuses = [
- makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, { 'ww000000-0000-0000-0000-000000000099': '325' }),
+ makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, { 'ww000000-0000-0000-0000-000000000099': '325' }, null, summary),
];
render(
,
@@ -262,31 +264,16 @@ describe('bar chart inside day modal', () => {
});
});
- it('omits animals with empty or non-numeric field value from bar chart', async () => {
+ it('hides bar chart when no animal has analysis_summary with total > 0', async () => {
const dateStr = todayStr();
- // Rat A has numeric weight; Rat B has empty weight — Rat B excluded from chart but shown in day list
const statuses = [
makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, { 'ww000000-0000-0000-0000-000000000099': '325' }),
- makeStatus('a2000000-0000-0000-0000-000000000002', dateStr, {}),
];
render(
,
);
fireEvent.click(screen.getByTestId(`day-${dateStr}`));
await waitFor(() => screen.getByRole('dialog'));
- expect(screen.getByTestId('bar-chart')).toBeInTheDocument();
- });
-
- it('hides bar chart when no animals have a numeric value for the selected field that day', async () => {
- const dateStr = todayStr();
- // vitals is text — no bar chart
- const statuses = [makeStatus('a1000000-0000-0000-0000-000000000001', dateStr, {}, 'HR 72')];
- render(
-
,
- );
- fireEvent.change(screen.getByTestId('field-select'), { target: { value: 'vitals' } });
- fireEvent.click(screen.getByTestId(`day-${dateStr}`));
- await waitFor(() => screen.getByRole('dialog'));
expect(screen.queryByTestId('bar-chart')).not.toBeInTheDocument();
});
});