From bb27d4437ff6d47e1c4e93ee46bd7beaad596730 Mon Sep 17 00:00:00 2001 From: Brian Bjarke Jensen Date: Tue, 11 Nov 2025 13:45:54 +0100 Subject: [PATCH] changed overview graphs binning --- src/baby_monitor/static/diapers.html | 133 ++++++++++++++++++++------ src/baby_monitor/static/feedings.html | 101 +++++++++++++++---- src/baby_monitor/static/sleep.html | 123 ++++++++++++++++++------ 3 files changed, 281 insertions(+), 76 deletions(-) diff --git a/src/baby_monitor/static/diapers.html b/src/baby_monitor/static/diapers.html index be13c30..6e7e603 100644 --- a/src/baby_monitor/static/diapers.html +++ b/src/baby_monitor/static/diapers.html @@ -365,7 +365,7 @@
-

Diaper Changes Per Day (Last ${currentDaysRange} Day${currentDaysRange !== 1 ? "s" : ""})

+

Diaper Changes (Last ${currentDaysRange} Day${currentDaysRange !== 1 ? "s" : ""})

@@ -627,36 +627,115 @@ ); } - // Create days based on currentDaysRange (including today) - for (let i = currentDaysRange - 1; i >= 0; i--) { - const date = new Date(now); - date.setDate(date.getDate() - i); - date.setHours(0, 0, 0, 0); + // If 24 hours selected, show 3-hour aggregates + if (currentDaysRange === 1) { + // Create 8 three-hour buckets + for (let i = 7; i >= 0; i--) { + const periodStart = new Date(now); + periodStart.setHours(periodStart.getHours() - (i * 3), 0, 0, 0); + + const periodEnd = new Date(periodStart); + periodEnd.setHours(periodEnd.getHours() + 3); - const dateStr = date.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - }); - labels.push(dateStr); + const startLabel = periodStart.toLocaleTimeString("en-US", { + hour: "numeric", + hour12: true, + }); + const endLabel = periodEnd.toLocaleTimeString("en-US", { + hour: "numeric", + hour12: true, + }); + labels.push(`${startLabel}-${endLabel}`); - // Count poop and pee diapers for this day - const nextDay = new Date(date); - nextDay.setDate(nextDay.getDate() + 1); + // Count poop and pee diapers for this 3-hour period + const periodChanges = filteredChanges.filter((dc) => { + const changeDate = new Date(dc.change_time); + return changeDate >= periodStart && changeDate < periodEnd; + }); - const dayChanges = filteredChanges.filter((dc) => { - const changeDate = new Date(dc.change_time); - return changeDate >= date && changeDate < nextDay; - }); + const poopCount = periodChanges.filter( + (dc) => dc.poop_amount !== null, + ).length; + const peeCount = periodChanges.filter( + (dc) => dc.pee_amount !== null, + ).length; - const poopCount = dayChanges.filter( - (dc) => dc.poop_amount !== null, - ).length; - const peeCount = dayChanges.filter( - (dc) => dc.pee_amount !== null, - ).length; + poopData.push(poopCount); + peeData.push(peeCount); + } + } else if (currentDaysRange === 3) { + // Show 8-hour aggregates for 3 days (9 buckets total) + for (let i = 8; i >= 0; i--) { + const periodStart = new Date(now); + periodStart.setHours(periodStart.getHours() - (i * 8), 0, 0, 0); + + const periodEnd = new Date(periodStart); + periodEnd.setHours(periodEnd.getHours() + 8); - poopData.push(poopCount); - peeData.push(peeCount); + // Format label based on the 8-hour period + const dayStr = periodStart.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + const startHour = periodStart.getHours(); + let periodLabel; + if (startHour >= 0 && startHour < 8) { + periodLabel = `${dayStr} Night`; + } else if (startHour >= 8 && startHour < 16) { + periodLabel = `${dayStr} Morning`; + } else { + periodLabel = `${dayStr} Evening`; + } + labels.push(periodLabel); + + // Count poop and pee diapers for this 8-hour period + const periodChanges = filteredChanges.filter((dc) => { + const changeDate = new Date(dc.change_time); + return changeDate >= periodStart && changeDate < periodEnd; + }); + + const poopCount = periodChanges.filter( + (dc) => dc.poop_amount !== null, + ).length; + const peeCount = periodChanges.filter( + (dc) => dc.pee_amount !== null, + ).length; + + poopData.push(poopCount); + peeData.push(peeCount); + } + } else { + // Create days based on currentDaysRange (including today) + for (let i = currentDaysRange - 1; i >= 0; i--) { + const date = new Date(now); + date.setDate(date.getDate() - i); + date.setHours(0, 0, 0, 0); + + const dateStr = date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + labels.push(dateStr); + + // Count poop and pee diapers for this day + const nextDay = new Date(date); + nextDay.setDate(nextDay.getDate() + 1); + + const dayChanges = filteredChanges.filter((dc) => { + const changeDate = new Date(dc.change_time); + return changeDate >= date && changeDate < nextDay; + }); + + const poopCount = dayChanges.filter( + (dc) => dc.poop_amount !== null, + ).length; + const peeCount = dayChanges.filter( + (dc) => dc.pee_amount !== null, + ).length; + + poopData.push(poopCount); + peeData.push(peeCount); + } } return { labels, poopData, peeData }; @@ -724,7 +803,7 @@ beginAtZero: true, title: { display: true, - text: "Number of Diapers", + text: "Number of Changes", font: { size: 13, weight: "600", diff --git a/src/baby_monitor/static/feedings.html b/src/baby_monitor/static/feedings.html index 9e38f5e..8da30d5 100644 --- a/src/baby_monitor/static/feedings.html +++ b/src/baby_monitor/static/feedings.html @@ -402,7 +402,7 @@
-

Feedings Per Day (Last ${currentDaysRange} Day${currentDaysRange !== 1 ? "s" : ""})

+

Feedings (Last ${currentDaysRange} Day${currentDaysRange !== 1 ? "s" : ""})

@@ -661,28 +661,91 @@ ); } - // Create days based on currentDaysRange (including today) - for (let i = currentDaysRange - 1; i >= 0; i--) { - const date = new Date(now); - date.setDate(date.getDate() - i); - date.setHours(0, 0, 0, 0); + // If 24 hours selected, show 3-hour aggregates + if (currentDaysRange === 1) { + // Create 8 three-hour buckets + for (let i = 7; i >= 0; i--) { + const periodStart = new Date(now); + periodStart.setHours(periodStart.getHours() - (i * 3), 0, 0, 0); + + const periodEnd = new Date(periodStart); + periodEnd.setHours(periodEnd.getHours() + 3); - const dateStr = date.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - }); - labels.push(dateStr); + const startLabel = periodStart.toLocaleTimeString("en-US", { + hour: "numeric", + hour12: true, + }); + const endLabel = periodEnd.toLocaleTimeString("en-US", { + hour: "numeric", + hour12: true, + }); + labels.push(`${startLabel}-${endLabel}`); - // Count feedings for this day - const nextDay = new Date(date); - nextDay.setDate(nextDay.getDate() + 1); + // Count feedings for this 3-hour period + const count = filteredFeedings.filter((f) => { + const feedingDate = new Date(f.start_time); + return feedingDate >= periodStart && feedingDate < periodEnd; + }).length; - const count = filteredFeedings.filter((f) => { - const feedingDate = new Date(f.start_time); - return feedingDate >= date && feedingDate < nextDay; - }).length; + data.push(count); + } + } else if (currentDaysRange === 3) { + // Show 8-hour aggregates for 3 days (9 buckets total) + for (let i = 8; i >= 0; i--) { + const periodStart = new Date(now); + periodStart.setHours(periodStart.getHours() - (i * 8), 0, 0, 0); + + const periodEnd = new Date(periodStart); + periodEnd.setHours(periodEnd.getHours() + 8); - data.push(count); + // Format label based on the 8-hour period + const dayStr = periodStart.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + const startHour = periodStart.getHours(); + let periodLabel; + if (startHour >= 0 && startHour < 8) { + periodLabel = `${dayStr} Night`; + } else if (startHour >= 8 && startHour < 16) { + periodLabel = `${dayStr} Morning`; + } else { + periodLabel = `${dayStr} Evening`; + } + labels.push(periodLabel); + + // Count feedings for this 8-hour period + const count = filteredFeedings.filter((f) => { + const feedingDate = new Date(f.start_time); + return feedingDate >= periodStart && feedingDate < periodEnd; + }).length; + + data.push(count); + } + } else { + // Create days based on currentDaysRange (including today) + for (let i = currentDaysRange - 1; i >= 0; i--) { + const date = new Date(now); + date.setDate(date.getDate() - i); + date.setHours(0, 0, 0, 0); + + const dateStr = date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + labels.push(dateStr); + + // Count feedings for this day + const nextDay = new Date(date); + nextDay.setDate(nextDay.getDate() + 1); + + const count = filteredFeedings.filter((f) => { + const feedingDate = new Date(f.start_time); + return feedingDate >= date && feedingDate < nextDay; + }).length; + + data.push(count); + } } return { labels, data }; diff --git a/src/baby_monitor/static/sleep.html b/src/baby_monitor/static/sleep.html index 10df90c..823b50a 100644 --- a/src/baby_monitor/static/sleep.html +++ b/src/baby_monitor/static/sleep.html @@ -536,42 +536,105 @@ return `${hours}h ${mins}m`; } - function prepareBarChartData(sleeps, timeRange) { - const days = {}; - const today = new Date(); - today.setHours(0, 0, 0, 0); + function prepareBarChartData(sleepSessions) { + const now = new Date(); + const labels = []; + const data = []; - // Initialize all days in range - for (let i = timeRange - 1; i >= 0; i--) { - const date = new Date(today); - date.setDate(date.getDate() - i); - const dateStr = date.toISOString().split("T")[0]; - days[dateStr] = 0; + // Filter by selected child if applicable + let filteredSessions = sleepSessions; + if (selectedChildId !== null) { + filteredSessions = sleepSessions.filter( + (session) => session.child_id === selectedChildId, + ); } - // Count sleeps per day - sleeps.forEach((sleep) => { - const date = new Date(sleep.start_time); - date.setHours(0, 0, 0, 0); - const dateStr = date.toISOString().split("T")[0]; - if (days.hasOwnProperty(dateStr)) { - days[dateStr]++; + // If 24 hours selected, show 3-hour aggregates + if (currentDaysRange === 1) { + // Create 8 three-hour buckets + for (let i = 7; i >= 0; i--) { + const periodStart = new Date(now); + periodStart.setHours(periodStart.getHours() - (i * 3), 0, 0, 0); + + const periodEnd = new Date(periodStart); + periodEnd.setHours(periodEnd.getHours() + 3); + + const startLabel = periodStart.toLocaleTimeString("en-US", { + hour: "numeric", + hour12: true, + }); + const endLabel = periodEnd.toLocaleTimeString("en-US", { + hour: "numeric", + hour12: true, + }); + labels.push(`${startLabel}-${endLabel}`); + + // Count sleep sessions for this 3-hour period + const count = filteredSessions.filter((session) => { + const startDate = new Date(session.start_time); + return startDate >= periodStart && startDate < periodEnd; + }).length; + + data.push(count); } - }); + } else if (currentDaysRange === 3) { + // Show 8-hour aggregates for 3 days (9 buckets total) + for (let i = 8; i >= 0; i--) { + const periodStart = new Date(now); + periodStart.setHours(periodStart.getHours() - (i * 8), 0, 0, 0); + + const periodEnd = new Date(periodStart); + periodEnd.setHours(periodEnd.getHours() + 8); - const labels = Object.keys(days).map((date) => { - const d = new Date(date); - return d.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - }); - }); + // Format label based on the 8-hour period + const dayStr = periodStart.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + const startHour = periodStart.getHours(); + let periodLabel; + if (startHour >= 0 && startHour < 8) { + periodLabel = `${dayStr} Night`; + } else if (startHour >= 8 && startHour < 16) { + periodLabel = `${dayStr} Morning`; + } else { + periodLabel = `${dayStr} Evening`; + } + labels.push(periodLabel); - return { - labels, - data: Object.values(days), - }; - } + // Count sleep sessions for this 8-hour period + const count = filteredSessions.filter((session) => { + const startDate = new Date(session.start_time); + return startDate >= periodStart && startDate < periodEnd; + }).length; + + data.push(count); + } + } else { + // Create days based on currentDaysRange (including today) + for (let i = currentDaysRange - 1; i >= 0; i--) { + const date = new Date(now); + date.setDate(date.getDate() - i); + date.setHours(0, 0, 0, 0); + + const dateStr = date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); + labels.push(dateStr); + + // Count sleep sessions for this day + const nextDay = new Date(date); + nextDay.setDate(nextDay.getDate() + 1); + + const count = filteredSessions.filter((session) => { + const startDate = new Date(session.start_time); + return startDate >= date && startDate < nextDay; + }).length; + + data.push(count); + } + } function prepareDurationDistributionData(sleeps) { const ranges = {