mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(doctor-report): keep a report with a past end date inside its dates
A report whose end date lies in the past listed medications that started after it, the dose history up to today and the latest injection even when it came later. It now lists a medication only if its course had begun by the end date (or it had a dose scheduled inside the window) and had not ended before the start, and dose history and the last injection stop at the end date.
This commit is contained in:
1 parent
9d2fd1db14
commit
ff4aa32f6d
2 files changed
+174
-3
No files matched your search
@@ -189,7 +189,34 @@ export async function collectDoctorReportData(
|
||||
}),
|
||||
wantsMedications
|
||||
? prisma.medication.findMany({
|
||||
where: { userId, active: true },
|
||||
// The report describes its window, and a link can pin that window
|
||||
// in the past: a medication whose course had not begun by the
|
||||
// window's end, or had ended before it began, is not part of it.
|
||||
// One with a dose scheduled inside the window was taken then,
|
||||
// whenever it was entered. Dose history and the last intake stop
|
||||
// at the window's end too, so a report for March does not show a
|
||||
// dose set in June.
|
||||
where: {
|
||||
userId,
|
||||
active: true,
|
||||
AND: [
|
||||
{
|
||||
OR: [
|
||||
{ startsOn: { lte: end } },
|
||||
{ startsOn: null, createdAt: { lte: end } },
|
||||
{
|
||||
intakeEvents: {
|
||||
some: {
|
||||
deletedAt: null,
|
||||
scheduledFor: { gte: start, lte: end },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ OR: [{ endsOn: null }, { endsOn: { gte: start } }] },
|
||||
],
|
||||
},
|
||||
include: {
|
||||
schedules: {
|
||||
select: { ...SCHEDULE_COMPLIANCE_SELECT, label: true },
|
||||
@@ -205,9 +232,12 @@ export async function collectDoctorReportData(
|
||||
},
|
||||
},
|
||||
pauseEras: { select: { pausedAt: true, resumedAt: true } },
|
||||
doseChanges: { orderBy: { effectiveFrom: "asc" } },
|
||||
doseChanges: {
|
||||
where: { effectiveFrom: { lte: end } },
|
||||
orderBy: { effectiveFrom: "asc" },
|
||||
},
|
||||
intakeEvents: {
|
||||
where: { takenAt: { not: null } },
|
||||
where: { takenAt: { not: null, lte: end } },
|
||||
orderBy: { takenAt: "desc" },
|
||||
take: 1,
|
||||
select: { takenAt: true, injectionSite: true },
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* A doctor report whose window ends in the past describes that window only.
|
||||
*
|
||||
* A share link can pin the end date. The medication list, the dose history
|
||||
* and the last injection were read as of today, so a report for the first
|
||||
* quarter listed a medication started in June, a dose set in June and an
|
||||
* injection logged last week.
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
process.env.ENCRYPTION_KEY ??=
|
||||
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
|
||||
import { collectDoctorReportData } from "@/lib/doctor-report-data";
|
||||
import { ALL_LEAF_IDS } from "@/lib/report-selection/catalogue";
|
||||
import { selectionFromLeaves } from "@/lib/report-selection/selection";
|
||||
|
||||
import { getPrismaClient, truncateAllTables } from "./setup";
|
||||
|
||||
vi.mock("@/lib/db-compat", () => ({
|
||||
ensureDbCompatibility: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
const RANGE = {
|
||||
start: new Date("2026-01-01T00:00:00.000Z"),
|
||||
end: new Date("2026-03-31T23:59:59.999Z"),
|
||||
days: 90,
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await truncateAllTables(getPrismaClient());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("doctor report with a fixed end date", () => {
|
||||
it("leaves out medications, doses and injections that came after the window", async () => {
|
||||
const prisma = getPrismaClient();
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
username: "fixed-window",
|
||||
email: "fixed-window@example.test",
|
||||
timezone: "UTC",
|
||||
},
|
||||
});
|
||||
const during = await prisma.medication.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
name: "Semaglutide",
|
||||
dose: "0.5 mg",
|
||||
treatmentClass: "GLP1",
|
||||
startsOn: new Date("2026-01-10T00:00:00.000Z"),
|
||||
createdAt: new Date("2026-01-10T00:00:00.000Z"),
|
||||
},
|
||||
});
|
||||
await prisma.medication.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
name: "Started later",
|
||||
dose: "10 mg",
|
||||
startsOn: new Date("2026-06-01T00:00:00.000Z"),
|
||||
createdAt: new Date("2026-06-01T00:00:00.000Z"),
|
||||
},
|
||||
});
|
||||
await prisma.medication.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
name: "Entered later, no start date",
|
||||
dose: "5 mg",
|
||||
createdAt: new Date("2026-07-01T00:00:00.000Z"),
|
||||
},
|
||||
});
|
||||
// Entered after the window, but with a dose scheduled inside it: it was
|
||||
// taken then, so it belongs on the report.
|
||||
const enteredLater = await prisma.medication.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
name: "Entered later, taken in March",
|
||||
dose: "20 mg",
|
||||
createdAt: new Date("2026-08-01T00:00:00.000Z"),
|
||||
},
|
||||
});
|
||||
await prisma.medicationIntakeEvent.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
medicationId: enteredLater.id,
|
||||
scheduledFor: new Date("2026-03-05T08:00:00.000Z"),
|
||||
takenAt: new Date("2026-03-05T08:10:00.000Z"),
|
||||
},
|
||||
});
|
||||
await prisma.medicationDoseChange.createMany({
|
||||
data: [
|
||||
{
|
||||
medicationId: during.id,
|
||||
effectiveFrom: new Date("2026-01-10T00:00:00.000Z"),
|
||||
doseValue: 0.25,
|
||||
doseUnit: "mg",
|
||||
},
|
||||
{
|
||||
medicationId: during.id,
|
||||
effectiveFrom: new Date("2026-06-15T00:00:00.000Z"),
|
||||
doseValue: 1,
|
||||
doseUnit: "mg",
|
||||
},
|
||||
],
|
||||
});
|
||||
await prisma.medicationIntakeEvent.createMany({
|
||||
data: [
|
||||
{
|
||||
userId: user.id,
|
||||
medicationId: during.id,
|
||||
scheduledFor: new Date("2026-03-20T08:00:00.000Z"),
|
||||
takenAt: new Date("2026-03-20T08:05:00.000Z"),
|
||||
},
|
||||
{
|
||||
userId: user.id,
|
||||
medicationId: during.id,
|
||||
scheduledFor: new Date("2026-07-20T08:00:00.000Z"),
|
||||
takenAt: new Date("2026-07-20T08:05:00.000Z"),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const data = await collectDoctorReportData(
|
||||
user.id,
|
||||
RANGE,
|
||||
selectionFromLeaves(ALL_LEAF_IDS),
|
||||
);
|
||||
|
||||
expect(data.medications?.map((m) => m.name).sort()).toEqual([
|
||||
"Entered later, taken in March",
|
||||
"Semaglutide",
|
||||
]);
|
||||
const glp1 = data.glp1?.medications[0];
|
||||
expect(glp1?.doseHistory.map((d) => d.value)).toEqual([0.25]);
|
||||
expect(glp1?.currentDose?.value).toBe(0.25);
|
||||
expect(glp1?.lastInjection?.date).toBe("2026-03-20T08:05:00.000Z");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user