feat(frontend): buildMatrix pivots statuses into date×subject grid

This commit is contained in:
Experiments DB Dev
2026-07-06 15:37:33 -04:00
parent 3f8febfda3
commit b5578f6e24
2 changed files with 114 additions and 0 deletions
+59
View File
@@ -53,3 +53,62 @@ export function listExportableParams(dailyTemplate, statuses) {
return params;
}
// A cell has a value unless it is null, undefined, or the empty string.
// (0 and false are real values.)
function hasValue(v) {
return v !== null && v !== undefined && v !== '';
}
function dateKey(date) {
return String(date).slice(0, 10); // ISO datetime or date → YYYY-MM-DD
}
// Pivot statuses into { columns, rows }.
// columns: [{ id, header }] — all animals, ordered by name, headers
// disambiguated with animal_id_string on duplicate names.
// rows: [{ date, values }] — one per date that has ≥1 value; values are
// aligned to columns, blank cells are '' (empty string).
export function buildMatrix(statuses, animals, param) {
const sortedAnimals = [...(animals ?? [])].sort((a, b) =>
String(a.animal_name ?? '').localeCompare(String(b.animal_name ?? '')),
);
const nameCounts = new Map();
for (const a of sortedAnimals) {
const n = a.animal_name ?? '';
nameCounts.set(n, (nameCounts.get(n) ?? 0) + 1);
}
const columns = sortedAnimals.map((a) => {
const name = a.animal_name ?? '';
const header = nameCounts.get(name) > 1 ? `${name} (${a.animal_id_string ?? ''})` : name;
return { id: a.id, header };
});
const colIndex = new Map(columns.map((c, i) => [c.id, i]));
// cells: dateKey -> Map(animalId -> value); first status per (date, animal) wins.
const cells = new Map();
const datesWithData = new Set();
const ordered = [...(statuses ?? [])].sort((a, b) => dateKey(a.date).localeCompare(dateKey(b.date)));
for (const s of ordered) {
if (!colIndex.has(s.animal_id)) continue;
const dk = dateKey(s.date);
if (!cells.has(dk)) cells.set(dk, new Map());
const byAnimal = cells.get(dk);
if (byAnimal.has(s.animal_id)) continue; // first wins
const v = extractValue(s, param);
byAnimal.set(s.animal_id, v);
if (hasValue(v)) datesWithData.add(dk);
}
const rows = [...datesWithData].sort().map((dk) => {
const byAnimal = cells.get(dk);
const values = columns.map((c) => {
const v = byAnimal.has(c.id) ? byAnimal.get(c.id) : null;
return hasValue(v) ? v : '';
});
return { date: dk, values };
});
return { columns, rows };
}