From 8d7139b03136868514b1c9a8bab7c42fc996f3d3 Mon Sep 17 00:00:00 2001 From: Alexander Heldt Date: Sun, 20 Sep 2026 10:42:51 +0000 Subject: [PATCH] Stop a marked day distorting Timing, the trends, and its neighbour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Marking a day "not counted" was implemented by filtering its events out of the list the cross-day panels are given. That is too blunt a tool, because three of those panels are not asking "which days count": - Timing's marker is how long since the last pee, which is a question about now. With the marked day's events gone it answered from the day before — 27 hours instead of 2 in the case I reproduced, so the marker sat off the end of its band. - The sleep and walk trends draw the day you are looking at against yesterday and the average. Marking that day collapsed its own curve to a flat zero. Marking a day means don't let it drag the average, not pretend nothing happened on it. - A nap from 23:00 on a marked day to 07:00 the next morning lost its sleep-start, leaving a dangling sleep-end that pairWindows discards. The next day — not marked — lost seven hours it really slept. Nobody reported this one; it turned up while reproducing the other two. None of them needed the filtering, because each already excludes marked days itself and more precisely than deleting events can: gapsBetween throws away a gap that *touches* one, the trend loops skip them when averaging, weeklyData and the actograms zero and hatch them. The filter was a second mechanism fighting the first. Only the by-hour and training panels still get the filtered list — they bucket individual events and care about neither day boundaries nor spans, which is exactly what removing events does. --- README.md | 9 +++++++++ src/app.js | 28 ++++++++++++++++++++++------ src/changelog.json | 1 + 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d654cbe..8cad080 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,15 @@ heading, takes the day you're looking at out of the aggregates. - **What stops counting** is the behaviour: sleep hours, timeline and trend, walk minutes and patterns, pee/poo/meal counts, food, by-hour, the training grid, and the Timing panel's typical gaps. +- **Marking a day is not the same as deleting its events**, and only two panels + are handed a filtered list (by-hour and training, which bucket individual + events and care about neither day boundaries nor spans). The rest take the + whole log and exclude days themselves, because three things break if the + events simply go: "how long since the last pee" is a question about *now* and + answered from the wrong event; the trend curve for the day you are *looking + at* collapses to zero; and a nap from 23:00 on a marked day to 07:00 on the + next loses its `sleep-start`, leaving a dangling `sleep-end` and costing the + next day — which isn't marked — seven hours it really slept. - **What keeps counting** is weight and notes. A weigh-in and a vet note are records of fact, not behaviour a sparse logger distorts, so they stay on the weight curve and in the Notes log. diff --git a/src/app.js b/src/app.js index 1b3414d..0279fad 100644 --- a/src/app.js +++ b/src/app.js @@ -2792,12 +2792,28 @@ // logger distorts, so they count everywhere regardless. renderWeight(events); renderNotes(events); - // Everything that aggregates across days works from the counted list. - renderTiming(counted); - renderWeekly(counted); - renderSleepTimeline(counted); - renderWalkPatterns(counted); - renderSleepTrend(counted); + // These take the whole list even though they aggregate, because each + // already knows about marked days and does something more precise with + // them than dropping their events would: + // + // - gapsBetween throws away a gap that *touches* a marked day, and + // "how long since the last one" is a question about now, not about + // the window — with the events gone it answered from the wrong one. + // - the trend curves skip marked days when averaging, but the curve for + // the day you are looking at is about that day; marking it means + // "don't let it drag the average", not "pretend nothing happened". + // - weeklyData and the actograms zero and hatch a marked day themselves, + // and need the events either side of it: a nap from 23:00 on a marked + // day to 07:00 on the next belongs, for those seven hours, to the next + // day — which is not marked and should show them. Dropping the + // sleep-start left a dangling sleep-end and lost the window entirely. + renderTiming(events); + renderWeekly(events); + renderSleepTimeline(events); + renderWalkPatterns(events); + renderSleepTrend(events); + // These two bucket individual events with no notion of a day boundary or a + // span, so removing the marked days' events is exactly the right tool. renderHourHeatmap(counted); renderTraining(counted); } diff --git a/src/changelog.json b/src/changelog.json index 6389e9a..0edd551 100644 --- a/src/changelog.json +++ b/src/changelog.json @@ -1,4 +1,5 @@ [ + { "date": "2026-09-20", "text": "Fixed three things that went wrong around a day marked “not counted”. The Timing panel measured “how long since the last pee” from before the marked day rather than from the actual last one, so the marker sat far out to the right. The Sleep and Walk trends drew the marked day's own curve as a flat zero when you were looking at that day — marking a day means don't let it drag the average, not pretend nothing happened on it. And a nap that started on a marked day and ended the next morning vanished from that next day's figures, even though the next day wasn't marked and the puppy really did sleep those hours" }, { "date": "2026-09-09", "text": "The big asleep/awake card at the top of Today is gone, and both timers now live permanently in the frozen bar at the top — visible on every tab, wherever you have scrolled to. The card only existed on one tab and the timers hid themselves whenever it was on screen, which meant the thing you most often want at a glance was the thing you had to go and find. With only one place left to show them they have their seconds back too" }, { "date": "2026-09-08", "text": "The date now opens a small month grid of the app's own instead of the browser's date picker. The browser's one is a sheet that covers the screen, which is backwards when the reason to change day is to see what the numbers did on it — this one sits under the bar with the overview still visible and updating as you move. ← and → still step a day at a time; the grid is for jumping further, and the Today button now lives inside it. That is what made room for the walk timer and the sleep timer to sit on one row: the top bar no longer splits onto two rows on a phone, except on the very smallest. The two timers count in minutes now rather than seconds — the big card on Today still ticks in seconds, which is where you look if you want them" }, { "date": "2026-09-08", "text": "A walk in progress now has a timer in the frozen bar at the top, next to the asleep/awake one, counting from when the walk started — so you can see how long you have been out from any tab without going to look. Tapping it ends the walk, the same way tapping the sleep timer logs the sleep boundary. On a narrow phone two timers no longer fit beside the date controls, so the bar splits onto two rows while a walk is on and goes back to one when it ends. The big timer card on Today shows the walk too while there is one — you are awake on a walk either way, so the walk is the more useful of the two" },