Add more statistics

This commit is contained in:
Alexander Heldt
2026-06-25 14:23:44 +00:00
parent 06c51bb715
commit 709a051afb
3 changed files with 66 additions and 0 deletions
+48
View File
@@ -473,6 +473,52 @@
document.getElementById("stat-poos").textContent = count("poo"); document.getElementById("stat-poos").textContent = count("poo");
} }
// Gaps (ms) between consecutive events of `type` logged within the last
// `days` days, sorted ascending. These intervals are what tell you how
// often the puppy needs to go out.
function gapsBetween(events, type, days = 7) {
const cutoff = startOfDay(new Date());
cutoff.setDate(cutoff.getDate() - (days - 1));
const from = cutoff.getTime();
const times = events
.filter(e => e.type === type && e.at >= from)
.map(e => e.at)
.sort((a, b) => a - b);
const gaps = [];
for (let i = 1; i < times.length; i++) gaps.push(times[i] - times[i - 1]);
return gaps.sort((a, b) => a - b);
}
// Median is used for the "typical" gap because it ignores the single long
// overnight gap each day, so it reflects real daytime frequency.
function median(sorted) {
if (sorted.length === 0) return null;
const mid = Math.floor(sorted.length / 2);
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
}
function renderTiming(events) {
const peeGaps = gapsBetween(events, "pee");
const pooGaps = gapsBetween(events, "poo");
const show = (id, ms) => {
document.getElementById(id).textContent = ms == null ? "—" : formatDuration(ms);
};
show("gap-pee", median(peeGaps));
show("gap-pee-min", peeGaps[0] ?? null);
show("gap-poo", median(pooGaps));
show("gap-poo-min", pooGaps[0] ?? null);
const hint = document.getElementById("timing-hint");
const typicalPee = median(peeGaps);
if (typicalPee != null) {
hint.textContent =
`Based on ${peeGaps.length} pee gap${peeGaps.length === 1 ? "" : "s"}. ` +
`Aim to take the puppy out a little before the typical ${formatDuration(typicalPee)} mark.`;
} else {
hint.textContent = "Log a few more pees and poos to see typical timings.";
}
}
function renderLasts(events) { function renderLasts(events) {
const setLast = (id, type) => { const setLast = (id, type) => {
const ev = lastEventOfType(events, type); const ev = lastEventOfType(events, type);
@@ -832,6 +878,7 @@
renderBigClock(events); renderBigClock(events);
renderStats(events); renderStats(events);
renderLasts(events); renderLasts(events);
renderTiming(events);
renderSleepWindows(events); renderSleepWindows(events);
renderWakeWindows(events); renderWakeWindows(events);
renderWeekly(events); renderWeekly(events);
@@ -1169,6 +1216,7 @@
renderBigClock(evs); renderBigClock(evs);
renderStats(evs); renderStats(evs);
renderLasts(evs); renderLasts(evs);
renderTiming(evs);
renderSleepWindows(evs); renderSleepWindows(evs);
renderWakeWindows(evs); renderWakeWindows(evs);
renderWeekly(evs); renderWeekly(evs);
+11
View File
@@ -75,6 +75,17 @@
</div> </div>
</section> </section>
<section class="timing">
<h2>Bathroom timing <span class="muted-note">(last 7 days)</span></h2>
<div class="lasts">
<div class="last-row"><span>Typical time between pees</span><span id="gap-pee"></span></div>
<div class="last-row"><span>Shortest between pees</span><span id="gap-pee-min"></span></div>
<div class="last-row"><span>Typical time between poos</span><span id="gap-poo"></span></div>
<div class="last-row"><span>Shortest between poos</span><span id="gap-poo-min"></span></div>
</div>
<p class="muted-note timing-hint" id="timing-hint"></p>
</section>
<section class="sleep"> <section class="sleep">
<h2>Sleep windows</h2> <h2>Sleep windows</h2>
<ul id="sleep-list" class="wake-list"></ul> <ul id="sleep-list" class="wake-list"></ul>
+7
View File
@@ -210,6 +210,13 @@ button.danger { background: var(--danger); }
.last-row:last-child { border-bottom: none; } .last-row:last-child { border-bottom: none; }
.last-row span:first-child { color: var(--muted); } .last-row span:first-child { color: var(--muted); }
.muted-note {
color: var(--muted);
font-size: 0.8rem;
font-weight: normal;
}
.timing-hint { margin: 10px 4px 0; line-height: 1.4; }
.history-controls { .history-controls {
display: flex; display: flex;
align-items: center; align-items: center;