mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
refactor(medications): extract the compliance slot-context builders
expectedSlotCountForDay and expectedSlotsBetween each rebuilt the same canonical-schedule and recurrence-context literals inline, differing only by id prefix and count-vs-collect. Extract toRecurrenceCtx and toCanonicalSchedule so both share one adapter; behaviour identical.
This commit is contained in:
1 parent
7e7702d6c9
commit
ad693cd862
1 file changed
+60
-52
@@ -28,7 +28,9 @@ import {
|
||||
} from "@/lib/medications/scheduling/cadence";
|
||||
import {
|
||||
occurrencesBetween,
|
||||
type CanonicalSchedule,
|
||||
type Occurrence,
|
||||
type RecurrenceContext,
|
||||
type ScheduleType,
|
||||
} from "@/lib/medications/scheduling/recurrence";
|
||||
import type { InjectionSiteKey } from "@/lib/medications/injection-sites";
|
||||
@@ -252,6 +254,53 @@ export function lastNonSkippedTakenAt(
|
||||
}, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.8.5 — adapt a compliance medication context to the canonical engine's
|
||||
* {@link RecurrenceContext}. The synthetic medication `id` only labels the
|
||||
* row for the engine's internal logging; the `idTag` keeps the two callers'
|
||||
* historical prefixes (`compliance-daily` vs `compliance-slots`) distinct.
|
||||
*/
|
||||
function toRecurrenceCtx(
|
||||
ctx: ComplianceMedicationContext,
|
||||
idTag: string,
|
||||
): RecurrenceContext {
|
||||
return {
|
||||
medication: {
|
||||
id: idTag,
|
||||
startsOn: ctx.startsOn,
|
||||
endsOn: ctx.endsOn,
|
||||
oneShot: ctx.oneShot,
|
||||
createdAt: ctx.createdAt,
|
||||
},
|
||||
timeZone: ctx.timeZone,
|
||||
lastIntakeAt: ctx.lastIntakeAt,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.8.5 — adapt a {@link ComplianceSchedule} to the canonical engine's
|
||||
* {@link CanonicalSchedule}, defaulting the optional fields the compliance
|
||||
* payload may omit. `id` is engine-internal labelling only.
|
||||
*/
|
||||
function toCanonicalSchedule(
|
||||
s: ComplianceSchedule,
|
||||
id: string,
|
||||
): CanonicalSchedule {
|
||||
return {
|
||||
id,
|
||||
rrule: s.rrule ?? null,
|
||||
rollingIntervalDays: s.rollingIntervalDays ?? null,
|
||||
timesOfDay: s.timesOfDay ?? [],
|
||||
daysOfWeek: s.daysOfWeek ?? null,
|
||||
windowStart: s.windowStart,
|
||||
windowEnd: s.windowEnd,
|
||||
reminderGraceMinutes: s.reminderGraceMinutes ?? null,
|
||||
scheduleType: s.scheduleType ?? "SCHEDULED",
|
||||
cyclicOnWeeks: s.cyclicOnWeeks ?? null,
|
||||
cyclicOffWeeks: s.cyclicOffWeeks ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.7.0 item 5 — count the expected dose slots a medication's schedules
|
||||
* emit inside `[dayStart, dayEnd)`, routed through the canonical engine.
|
||||
@@ -267,34 +316,10 @@ export function expectedSlotCountForDay(
|
||||
ctx: ComplianceMedicationContext,
|
||||
): number {
|
||||
let count = 0;
|
||||
const recurrenceCtx = {
|
||||
medication: {
|
||||
id: "compliance-daily",
|
||||
startsOn: ctx.startsOn,
|
||||
endsOn: ctx.endsOn,
|
||||
oneShot: ctx.oneShot,
|
||||
createdAt: ctx.createdAt,
|
||||
},
|
||||
timeZone: ctx.timeZone,
|
||||
lastIntakeAt: ctx.lastIntakeAt,
|
||||
};
|
||||
const recurrenceCtx = toRecurrenceCtx(ctx, "compliance-daily");
|
||||
for (let i = 0; i < schedules.length; i++) {
|
||||
const s = schedules[i];
|
||||
const canonical = {
|
||||
id: `compliance-daily-${i}`,
|
||||
rrule: s.rrule ?? null,
|
||||
rollingIntervalDays: s.rollingIntervalDays ?? null,
|
||||
timesOfDay: s.timesOfDay ?? [],
|
||||
daysOfWeek: s.daysOfWeek ?? null,
|
||||
windowStart: s.windowStart,
|
||||
windowEnd: s.windowEnd,
|
||||
reminderGraceMinutes: s.reminderGraceMinutes ?? null,
|
||||
scheduleType: s.scheduleType ?? ("SCHEDULED" as const),
|
||||
cyclicOnWeeks: s.cyclicOnWeeks ?? null,
|
||||
cyclicOffWeeks: s.cyclicOffWeeks ?? null,
|
||||
};
|
||||
count += occurrencesBetween(
|
||||
canonical,
|
||||
toCanonicalSchedule(schedules[i], `compliance-daily-${i}`),
|
||||
dayStart,
|
||||
// occurrencesBetween is inclusive of both ends; subtract 1 ms so a
|
||||
// slot exactly at the next day's midnight doesn't double-count.
|
||||
@@ -324,34 +349,17 @@ export function expectedSlotsBetween(
|
||||
to: Date,
|
||||
ctx: ComplianceMedicationContext,
|
||||
): Occurrence[] {
|
||||
const recurrenceCtx = {
|
||||
medication: {
|
||||
id: "compliance-slots",
|
||||
startsOn: ctx.startsOn,
|
||||
endsOn: ctx.endsOn,
|
||||
oneShot: ctx.oneShot,
|
||||
createdAt: ctx.createdAt,
|
||||
},
|
||||
timeZone: ctx.timeZone,
|
||||
lastIntakeAt: ctx.lastIntakeAt,
|
||||
};
|
||||
const recurrenceCtx = toRecurrenceCtx(ctx, "compliance-slots");
|
||||
const all: Occurrence[] = [];
|
||||
for (let i = 0; i < schedules.length; i++) {
|
||||
const s = schedules[i];
|
||||
const canonical = {
|
||||
id: `compliance-slots-${i}`,
|
||||
rrule: s.rrule ?? null,
|
||||
rollingIntervalDays: s.rollingIntervalDays ?? null,
|
||||
timesOfDay: s.timesOfDay ?? [],
|
||||
daysOfWeek: s.daysOfWeek ?? null,
|
||||
windowStart: s.windowStart,
|
||||
windowEnd: s.windowEnd,
|
||||
reminderGraceMinutes: s.reminderGraceMinutes ?? null,
|
||||
scheduleType: s.scheduleType ?? ("SCHEDULED" as const),
|
||||
cyclicOnWeeks: s.cyclicOnWeeks ?? null,
|
||||
cyclicOffWeeks: s.cyclicOffWeeks ?? null,
|
||||
};
|
||||
all.push(...occurrencesBetween(canonical, from, to, recurrenceCtx));
|
||||
all.push(
|
||||
...occurrencesBetween(
|
||||
toCanonicalSchedule(schedules[i], `compliance-slots-${i}`),
|
||||
from,
|
||||
to,
|
||||
recurrenceCtx,
|
||||
),
|
||||
);
|
||||
}
|
||||
return all.sort((a, b) => a.at.getTime() - b.at.getTime());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user