Period day toggle, flow strength on calendar, end dates for past periods

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rope 2026-10-18 09:32:29 +01:00
parent 94372c6abc
commit ce66b857db
6 changed files with 149 additions and 55 deletions

View file

@ -5,6 +5,9 @@
})(typeof window === "undefined" ? globalThis : window, () => {
const MS_PER_DAY = 86400000;
const PERIOD_FLOWS = new Set(["period", "light", "medium", "heavy"]);
// "period" is a period day without a strength, so it has no level.
const FLOW_LEVELS = { light: 1, medium: 2, heavy: 3 };
const LEVEL_NAMES = [null, "light", "medium", "heavy"];
const DEFAULT_CYCLE = 28;
const DEFAULT_PERIOD = 5;
// Days from ovulation to the next period. Fairly constant between people,
@ -66,6 +69,23 @@
return { mean, sd: Math.sqrt(variance) };
}
// Typical strength on each day of a period (index 0 = first day), averaged
// over recent periods. Days nobody rated stay null.
function flowProfile(days, periodList, recent = 6) {
const sums = [];
const counts = [];
for (const period of periodList.slice(-recent)) {
for (let index = 0; index < period.length; index += 1) {
const entry = days[toIso(period.start + index)];
const level = entry && FLOW_LEVELS[entry.flow];
if (!level) continue;
sums[index] = (sums[index] || 0) + level;
counts[index] = (counts[index] || 0) + 1;
}
}
return Array.from({ length: sums.length }, (_, index) => (counts[index] ? Math.round(sums[index] / counts[index]) : null));
}
function fertileWindow(nextStart, previousEnd) {
const ovulation = nextStart - LUTEAL_DAYS;
if (ovulation <= previousEnd) return null;
@ -86,10 +106,12 @@
cycleLength: DEFAULT_CYCLE,
cycleSpread: null,
periodLength: DEFAULT_PERIOD,
periodSamples: 0,
current: null,
late: 0,
predictions: [],
fertile: []
fertile: [],
flowProfile: flowProfile(days, periodList)
};
if (!periodList.length) return summary;
@ -105,6 +127,7 @@
if (lengths.length) {
const mean = lengths.reduce((sum, value) => sum + value, 0) / lengths.length;
summary.periodLength = Math.min(10, Math.max(2, Math.round(mean)));
summary.periodSamples = lengths.length;
}
summary.current = {
@ -139,5 +162,5 @@
return summary;
}
return { PERIOD_FLOWS, LUTEAL_DAYS, MIN_CYCLE, MAX_CYCLE, toDay, toIso, periods, cycles, weightedStats, summarize };
return { PERIOD_FLOWS, FLOW_LEVELS, LEVEL_NAMES, LUTEAL_DAYS, MIN_CYCLE, MAX_CYCLE, toDay, toIso, periods, cycles, weightedStats, flowProfile, summarize };
});