fix(report): keep sport times chronological within each day

This commit is contained in:
fy59 2026-10-07 10:20:12 +02:00
parent bf5a06973d
commit e454c86354
6 changed files with 19 additions and 11 deletions

View file

@ -2,8 +2,9 @@
## Unreleased — 0.1.7 ## Unreleased — 0.1.7
- Present Sleep PDF nights and the completed-sport appendix newest first while - Present Sleep PDF nights and completed-sport days newest first while
preserving chronological timed content within each night. Add a restrained preserving chronological timed content within each night and sport day.
Add a restrained
printable header, period and generation date, and pale row separation. printable header, period and generation date, and pale row separation.
Remove unhelpful validation labels from agenda rows and PDF warnings; the Remove unhelpful validation labels from agenda rows and PDF warnings; the
editor now names the exact-revision synchronization publication action and editor now names the exact-revision synchronization publication action and

View file

@ -263,8 +263,9 @@ full-width heart-rate detail; it performs no database mutation. PDF preview
and download instead share an explicit inclusive range over night start dates, and download instead share an explicit inclusive range over night start dates,
initialized to the loaded history bounds and independent of detail selection. initialized to the loaded history bounds and independent of detail selection.
The vector PDF now presents selected nights newest first, while retaining The vector PDF now presents selected nights newest first, while retaining
chronological timed content within each night; its sport appendix is also chronological timed content within each night; its sport appendix shows newest
newest first. A light header, period, generation date and pale alternating rows days first and chronological sessions within each day. A light header, period,
generation date and pale alternating rows
improve print hierarchy in color and grayscale. Agenda rows and PDF omit the improve print hierarchy in color and grayscale. Agenda rows and PDF omit the
publication/validation labels. The editor still exposes the exact-revision publication/validation labels. The editor still exposes the exact-revision
publication action and state required for synchronization; PDF selection and publication action and state required for synchronization; PDF selection and

View file

@ -66,8 +66,9 @@ on dated pages instead of truncating silently. Review the chosen range before
export; a PDF is a local copy of potentially sensitive information. export; a PDF is a local copy of potentially sensitive information.
The PDF prints the newest selected night first and orders its sport appendix by The PDF prints the newest selected night first and orders its sport appendix by
actual session start, newest first. Events and medication intakes within each actual session day, newest first, with sessions chronological within each day.
night keep their chronological order. A light, printable header shows the Events and medication intakes within each night keep their chronological order.
A light, printable header shows the
selected period and generation date; alternating pale rows and a restrained selected period and generation date; alternating pale rows and a restrained
accent distinguish nights. Publication status and global validation warnings accent distinguish nights. Publication status and global validation warnings
are omitted from the report. This presentation does not alter Sleep facts, are omitted from the report. This presentation does not alter Sleep facts,

View file

@ -409,9 +409,10 @@ subset summaries cannot inherit facts from unselected nights. The same real
Firefox scenario exports a seven-night French PDF from the embedded production Firefox scenario exports a seven-night French PDF from the embedded production
bundle, extracts its text with `pdftotext` and rasterizes its A4 landscape page bundle, extracts its text with `pdftotext` and rasterizes its A4 landscape page
with `pdftoppm` for retained visual inspection. with `pdftoppm` for retained visual inspection.
The 0.1.7 PDF polish tests also assert newest-first night and sport appendix The 0.1.7 PDF polish tests also assert newest-first night and sport day order,
order, unchanged chronological intervals and medication intakes within a night, chronological sessions within each day, unchanged chronological intervals and
7/14/21/28-night pagination, and absence of global validation warnings. Web medication intakes within a night, 7/14/21/28-night pagination, and absence of
global validation warnings. Web
tests keep the exact-revision publication action and editing while excluding tests keep the exact-revision publication action and editing while excluding
publication labels from agenda rows. publication labels from agenda rows.

View file

@ -332,6 +332,8 @@ describe("sleep diary PDF", () => {
it("prints factual sport bands and a separate legible listing without changing sleep totals", async () => { it("prints factual sport bands and a separate legible listing without changing sleep totals", async () => {
const sessions = [ const sessions = [
{ identity: "se_previous", session_type: "training", label: "Yoga",
started_at: "2026-09-20T19:00:00+02:00", ended_at: "2026-09-20T19:30:00+02:00" },
{ identity: "se_morning", session_type: "training", label: "Musculation", { identity: "se_morning", session_type: "training", label: "Musculation",
started_at: "2026-09-21T05:00:00+02:00", ended_at: "2026-09-21T06:00:00+02:00" }, started_at: "2026-09-21T05:00:00+02:00", ended_at: "2026-09-21T06:00:00+02:00" },
{ identity: "se_evening", session_type: "training", label: "Étirements", { identity: "se_evening", session_type: "training", label: "Étirements",
@ -344,7 +346,8 @@ describe("sleep diary PDF", () => {
expect(content).toContain("Musculation"); expect(content).toContain("Musculation");
expect(content).toContain("05:00"); expect(content).toContain("05:00");
expect(content).toContain("Étirements"); expect(content).toContain("Étirements");
expect(content.indexOf("Étirements")).toBeLessThan(content.indexOf("Musculation")); expect(content.indexOf("Musculation")).toBeLessThan(content.indexOf("Étirements"));
expect(content.indexOf("Étirements")).toBeLessThan(content.indexOf("Yoga"));
expect(content).toContain("sommeil non renseigné"); expect(content).toContain("sommeil non renseigné");
expect(content).toContain("1 nuit"); expect(content).toContain("1 nuit");
expect((source.match(/\/Type \/Page /g) ?? []).length).toBe(2); expect((source.match(/\/Type \/Page /g) ?? []).length).toBe(2);

View file

@ -527,7 +527,8 @@ export function buildSleepDiaryPdf(
right.night_start_date.localeCompare(left.night_start_date) || right.night_start_date.localeCompare(left.night_start_date) ||
left.entry_id.localeCompare(right.entry_id)); left.entry_id.localeCompare(right.entry_id));
const orderedSport = [...sport].sort((left, right) => const orderedSport = [...sport].sort((left, right) =>
Date.parse(right.started_at) - Date.parse(left.started_at) || right.started_at.slice(0, 10).localeCompare(left.started_at.slice(0, 10)) ||
Date.parse(left.started_at) - Date.parse(right.started_at) ||
left.identity.localeCompare(right.identity)); left.identity.localeCompare(right.identity));
const first = reportStart ?? orderedEntries[orderedEntries.length - 1]?.night_start_date ?? "—"; const first = reportStart ?? orderedEntries[orderedEntries.length - 1]?.night_start_date ?? "—";
const last = reportEnd ?? orderedEntries[0]?.night_start_date ?? "—"; const last = reportEnd ?? orderedEntries[0]?.night_start_date ?? "—";