mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 14:40:52 +00:00
fix(sleep): one day total for every sleep reader, and the latest night is a night
A day's sleep is the main night plus its naps (daySleepMinutes). The series, the sleep score and the composition already read it that way; the sleep list, the CSV export and the correlation sleep channel read the main night alone, so a day of naps listed as 0 h and an average over the list counted it as a night without sleep. The list now carries the day total in value and the main night alone in the new mainAsleepMinutes field; the nap caption reads as included. GET /api/sleep/night without a date answered the latest day holding any sleep, so a nap after the last night left the hypnogram with no night to draw. It now walks back to the latest night with a main sleep; nap days stay reachable by date. The SQL night page bounded a night by its asleep sessions only, so an in-bed-only session of the same night fell outside a chunked read that the one-night read counted. The span now covers every session of the night.
This commit is contained in:
1 parent
3004f55875
commit
63bbf0931c
20 files changed
+339
-94
No files matched your search
+19
-14
@@ -17,6 +17,7 @@
|
||||
import { formatInUserTz } from "./tz/format";
|
||||
import {
|
||||
reconstructSleepSessions,
|
||||
daySleepMinutes,
|
||||
pickMainNightAndNaps,
|
||||
type SleepStageRow,
|
||||
} from "./analytics/sleep-night";
|
||||
@@ -142,9 +143,9 @@ interface ExportMeasurement {
|
||||
*
|
||||
* v1.11.5 — SLEEP_DURATION is stored one row per STAGE per night. A flat
|
||||
* export emitted a wall of per-stage minutes with no night attribution.
|
||||
* By default sleep rows are now COLLAPSED to one record per night (the
|
||||
* main session's TIME ASLEEP per the nap convention) carrying the stage
|
||||
* breakdown in `notes` so the single uniform CSV reads as one night, not
|
||||
* By default sleep rows are now COLLAPSED to one record per wake day (the
|
||||
* day's TIME ASLEEP, main night plus naps, `daySleepMinutes`) carrying the
|
||||
* main night's stage breakdown and the naps in `notes` so the single uniform CSV reads as one night, not
|
||||
* many stages. Pass `granularity: "raw"` to keep the per-stage rows for
|
||||
* power users. Storage is untouched either way.
|
||||
*/
|
||||
@@ -224,11 +225,14 @@ export function formatMeasurementsForExport(
|
||||
const nightRecords: Array<{ at: Date; rec: ExportableRecord }> = [];
|
||||
for (const daySessions of byDay.values()) {
|
||||
const { main, naps } = pickMainNightAndNaps(daySessions);
|
||||
if (!main) continue;
|
||||
const stageParts = (Object.entries(main.stages) as Array<[string, number]>)
|
||||
.filter(([, mins]) => mins > 0)
|
||||
.map(([stage, mins]) => `${stage}=${Math.round(mins)}m`)
|
||||
.join(" ");
|
||||
const lead = main ?? naps.at(-1);
|
||||
if (!lead) continue;
|
||||
const stageParts = main
|
||||
? (Object.entries(main.stages) as Array<[string, number]>)
|
||||
.filter(([, mins]) => mins > 0)
|
||||
.map(([stage, mins]) => `${stage}=${Math.round(mins)}m`)
|
||||
.join(" ")
|
||||
: "";
|
||||
const napNote =
|
||||
naps.length > 0
|
||||
? ` naps=${naps.length}(${Math.round(
|
||||
@@ -236,16 +240,17 @@ export function formatMeasurementsForExport(
|
||||
)}m)`
|
||||
: "";
|
||||
const awakeningNote =
|
||||
main.awakenings > 0 ? ` awakenings=${main.awakenings}` : "";
|
||||
main && main.awakenings > 0 ? ` awakenings=${main.awakenings}` : "";
|
||||
nightRecords.push({
|
||||
at: main.end,
|
||||
at: lead.end,
|
||||
rec: {
|
||||
type: "SLEEP_DURATION",
|
||||
// Headline value = the main night's TIME ASLEEP in minutes.
|
||||
value: Math.round(main.asleepMinutes),
|
||||
// Headline value = the day's TIME ASLEEP in minutes, the main night
|
||||
// plus its naps (`daySleepMinutes`), as the list and the series read.
|
||||
value: Math.round(daySleepMinutes({ main, naps })),
|
||||
unit: "minutes",
|
||||
measuredAt: formatTimestamp(main.end, userTz),
|
||||
source: main.source ?? "",
|
||||
measuredAt: formatTimestamp(lead.end, userTz),
|
||||
source: lead.source ?? "",
|
||||
notes: `${stageParts}${napNote}${awakeningNote}`.trim(),
|
||||
glucoseContext: "",
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user