Leave a day that doesn't count off the trends entirely
The Sleep and Walk trends already kept a day marked "not counted" out of the window average and out of the "yesterday" comparison. What they still drew was that day's own curve, when it was the day you had selected — as the boldest line on the panel. So the single day you had said not to trust was the one the chart led with, against references that had carefully excluded it. It is left off now, along with its legend chip and, for the sleep trend, the projected tail that continued it. What remains is the average and yesterday, which is what you would want to look at on a day like that. This reverses part of an earlier fix. That one stopped the curve being drawn as a flat zero, on the reasoning that marking a day means "don't let it drag the average" rather than "pretend nothing happened". The flat zero was certainly wrong, but so was the conclusion: a real curve for an untrusted day is still the wrong thing to lead with. Absent is the honest third option. The walk trend gets the same treatment. It is the same panel in different units, and the two disagreeing about what a marked day means would be worse than either answer.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
// The sleep and walk trends draw the selected day against yesterday and the
|
||||
// window average. A day marked "not counted" has to be absent from all three,
|
||||
// and the one that kept slipping through was the selected day itself — it is
|
||||
// the boldest line on the panel, so it reads as the answer.
|
||||
import { load } from "./extract.mjs";
|
||||
import { suite, eq, ok, report } from "./assert.mjs";
|
||||
|
||||
const DAY = 86_400_000;
|
||||
const SEL = new Date(2026, 8, 20); // the day under the cursor
|
||||
const at = (day, hour) => new Date(2026, 8, day, hour).getTime();
|
||||
|
||||
let excluded = new Set();
|
||||
let windowDays = 7;
|
||||
|
||||
const curves = load({
|
||||
names: ["startOfDay", "pairWindows", "sleepWindows", "sleepTrendCurves"],
|
||||
stubs: {
|
||||
selectedDay: () => SEL,
|
||||
ymd: (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`,
|
||||
isExcluded: (d) => excluded.has(d.getDate()),
|
||||
chartDays: () => windowDays,
|
||||
},
|
||||
});
|
||||
|
||||
// A night's sleep on each of several days, so every curve has something to draw.
|
||||
const slept = (day, fromHour, toHour) => ([
|
||||
{ id: `s${day}a`, type: "sleep-start", at: at(day, fromHour) },
|
||||
{ id: `s${day}b`, type: "sleep-end", at: at(day, toHour) },
|
||||
]);
|
||||
const week = [16, 17, 18, 19, 20].flatMap(d => slept(d, 1, 5));
|
||||
|
||||
suite("the selected day's own curve");
|
||||
{
|
||||
excluded = new Set();
|
||||
const c = curves.sleepTrendCurves(week);
|
||||
ok(c.today && c.today.length > 1, "a normal day is drawn");
|
||||
eq(c.dayExcluded, false, "…and not flagged as excluded");
|
||||
|
||||
excluded = new Set([20]); // the selected day
|
||||
const m = curves.sleepTrendCurves(week);
|
||||
eq(m.today, null, "a day marked 'not counted' is not drawn at all");
|
||||
eq(m.dayExcluded, true, "…and says so, so the legend can drop its chip");
|
||||
eq(m.projected, null, "…and nothing is projected from a curve that isn't there");
|
||||
ok(m.avg && m.avg.length > 1, "the average it would have been read against survives");
|
||||
ok(m.yesterday && m.yesterday.length > 1, "so does yesterday");
|
||||
}
|
||||
|
||||
suite("the comparison day and the average");
|
||||
{
|
||||
excluded = new Set([19]); // yesterday, relative to the 20th
|
||||
const c = curves.sleepTrendCurves(week);
|
||||
eq(c.yesterday, null, "a marked yesterday is dropped rather than drawn flat");
|
||||
ok(c.today && c.today.length > 1, "the selected day is unaffected by it");
|
||||
|
||||
// Every day but the selected one marked: nothing left to average over.
|
||||
excluded = new Set([16, 17, 18, 19]);
|
||||
const none = curves.sleepTrendCurves(week);
|
||||
eq(none.avg, null, "an average with no days left to average is null, not zero");
|
||||
ok(none.today && none.today.length > 1, "…and the selected day still draws");
|
||||
}
|
||||
|
||||
suite("a marked day never contributes to the average");
|
||||
{
|
||||
// The 19th sleeps far longer than the rest. With it counted the average is
|
||||
// dragged up; marked, it should leave no trace.
|
||||
const lopsided = [...[16, 17, 18].flatMap(d => slept(d, 1, 3)), ...slept(19, 1, 23), ...slept(20, 1, 3)];
|
||||
windowDays = 7;
|
||||
|
||||
excluded = new Set();
|
||||
const withIt = curves.sleepTrendCurves(lopsided).avg[24].y;
|
||||
excluded = new Set([19]);
|
||||
const without = curves.sleepTrendCurves(lopsided).avg[24].y;
|
||||
ok(withIt > without, "marking the outlier lowers the average it was inflating");
|
||||
eq(Math.round(without), 2, "…back to the two hours the remaining days actually slept");
|
||||
}
|
||||
|
||||
export default report("trend-curves");
|
||||
Reference in New Issue
Block a user