diff --git a/src/lib/import/labs-csv.ts b/src/lib/import/labs-csv.ts index 86e4b202c..3c874dbfe 100644 --- a/src/lib/import/labs-csv.ts +++ b/src/lib/import/labs-csv.ts @@ -275,6 +275,10 @@ export async function importLabsCsv(args: { }): Promise { const { userId, tz, query } = args; const dryRun = query.dryRun; + // One zone for everything: the file's times are read in it, and the + // calendar day a reading is a duplicate on is cut in it too. Cutting the + // days in the account's zone while reading the file in another split one + // draw day in two, or folded two into one. const timezone = query.timezone ?? tz; const report = new RecordImportReport("labs", dryRun); const index: MarkerIndex = await loadMarkerIndex(prisma, userId); @@ -365,7 +369,9 @@ export async function importLabsCsv(args: { } // 2. Duplicates against the record, one query for the chunk. - const days = planned.map((p) => labReadingDay(p.row.takenAt, tz)).sort(); + const days = planned + .map((p) => labReadingDay(p.row.takenAt, timezone)) + .sort(); const existingKeys = new Set(); if (planned.length > 0) { const from = labReadingDaySearchRange(days[0]).gte; @@ -396,10 +402,10 @@ export async function importLabsCsv(args: { }, }); for (const e of existing) { - for (const k of readingKeys(e, tz)) existingKeys.add(k); + for (const k of readingKeys(e, timezone)) existingKeys.add(k); // A reading filed under a marker also answers to its printed name. if (e.biomarkerId) { - for (const k of readingKeys({ ...e, biomarkerId: null }, tz)) + for (const k of readingKeys({ ...e, biomarkerId: null }, timezone)) existingKeys.add(k); } } @@ -418,10 +424,10 @@ export async function importLabsCsv(args: { sourceUnit: row.unit, }; const keys = [ - ...readingKeys(probe, tz), - ...readingKeys({ ...probe, biomarkerId: null }, tz), + ...readingKeys(probe, timezone), + ...readingKeys({ ...probe, biomarkerId: null }, timezone), ...(marker - ? readingKeys({ ...probe, biomarkerId: marker.id }, tz) + ? readingKeys({ ...probe, biomarkerId: marker.id }, timezone) : []), ]; if (keys.some((k) => existingKeys.has(k) || seen.has(k))) { @@ -506,7 +512,7 @@ export async function importLabsCsv(args: { }); for (const k of [ ...keys, - ...readingKeys({ ...probe, biomarkerId: biomarker.id }, tz), + ...readingKeys({ ...probe, biomarkerId: biomarker.id }, timezone), ]) seen.add(k); report.valid += 1; diff --git a/tests/integration/record-csv-import.test.ts b/tests/integration/record-csv-import.test.ts index 665fc6da6..7164cab39 100644 --- a/tests/integration/record-csv-import.test.ts +++ b/tests/integration/record-csv-import.test.ts @@ -176,6 +176,33 @@ describe("lab CSV import", () => { }); }); +describe("lab CSV import in a zone of the file's own", () => { + it("cuts the duplicate day in the zone the file is read in", async () => { + // Read in New York, these are draws on two days (1 and 2 March). In the + // account's Berlin they both fall on 2 March, which used to make the + // second a duplicate of the first. + const csv = [ + "Analyt;Wert;Einheit;Datum", + "Zone marker;7;mg/dL;2025-03-01T22:30", + "Zone marker;7;mg/dL;2025-03-02T02:00", + ].join("\n"); + const result = await importLabsCsv({ + userId: USER, + tz: TZ, + stream: textToStream(csv), + maxBytes: 1_000_000, + query: labsCsvQuerySchema.parse({ + analyte: "0", + value: "1", + unit: "2", + date: "3", + timezone: "America/New_York", + }), + }); + expect(result).toMatchObject({ inserted: 2, duplicates: 0 }); + }); +}); + describe("medication CSV import", () => { const MEDS = [ "name,dose,times,as needed,start,end",