mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
feat(cycle): symptom-by-phase patterns on the insights tab
Add the cycle-native counterpart to the phase×vitals crosstab: for each logged symptom, where in the cycle it clusters. A pure aggregator tallies each symptom's occurrences across the phase-labelled days (the same phase map the calendar uses), surfaces only symptoms logged on at least three phased days, and reports per-phase counts + the dominant phase + its share. The insights tab renders it as a proportional phase-hued bar with the dominant phase named in words (never colour-only), observational and never causal. Also correct the phase-crosstab OpenAPI row enum, which had drifted behind the runtime (mood + glucose channels were missing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d2da8d2657
commit
eec3557137
14 files changed
+447
-7
No files matched your search
@@ -35,6 +35,10 @@ import {
|
||||
MOOD_CHANNEL_KEY,
|
||||
} from "@/lib/cycle/phase-crosstab";
|
||||
import type { CrossMetricMeasurement } from "@/lib/insights/mood-aggregates";
|
||||
import {
|
||||
computeSymptomPhasePatterns,
|
||||
type SymptomDay,
|
||||
} from "@/lib/cycle/symptom-phase";
|
||||
import { addDays } from "@/lib/cycle/day-math";
|
||||
import { DEFAULT_TIMEZONE, moodDateKey } from "@/lib/mood/date-key";
|
||||
|
||||
@@ -79,6 +83,7 @@ export const GET = apiHandler(async () => {
|
||||
basalBodyTempC: true,
|
||||
ovulationTest: true,
|
||||
cervicalMucus: true,
|
||||
symptomLinks: { select: { symptom: { select: { key: true } } } },
|
||||
},
|
||||
}),
|
||||
// Passive wrist temperature feeds the temperature-trend ovulation layer
|
||||
@@ -196,6 +201,13 @@ export const GET = apiHandler(async () => {
|
||||
userPriorityJson,
|
||||
});
|
||||
|
||||
// Cycle-NATIVE insight: where each logged symptom clusters across the phases.
|
||||
const symptomDays: SymptomDay[] = dayLogRows.map((d) => ({
|
||||
date: d.date,
|
||||
keys: d.symptomLinks.map((l) => l.symptom.key),
|
||||
}));
|
||||
const symptomPatterns = computeSymptomPhasePatterns(symptomDays, phaseByDay);
|
||||
|
||||
annotate({
|
||||
action: { name: "cycle.insights.read" },
|
||||
meta: {
|
||||
@@ -205,6 +217,7 @@ export const GET = apiHandler(async () => {
|
||||
has_headline: headline !== null,
|
||||
lagged_discovered: lagged.discovered.length,
|
||||
lagged_pairs_tested: lagged.pairsTested,
|
||||
symptom_patterns: symptomPatterns.length,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -217,6 +230,7 @@ export const GET = apiHandler(async () => {
|
||||
fdrQ: lagged.fdrQ,
|
||||
minPairs: lagged.minPairs,
|
||||
},
|
||||
symptomPatterns,
|
||||
contrast: { high: "LUTEAL", low: "FOLLICULAR" },
|
||||
windowDays: WINDOW_DAYS,
|
||||
cyclesObserved: cycles.length,
|
||||
|
||||
Reference in new issue
Block a user