Merge pull request #1140 from MBombeck/ready/pr-1131

fix(import): leave out Apple Health samples HealthLog wrote itself
This commit is contained in:
Marc-André Bombeck authored and GitHub committed 2026-10-04 17:33:47 +02:00
commit 92f17c8ca4
15 files changed
+1095 -46

No files matched your search

@@ -0,0 +1,67 @@
/**
* The note under a finished Apple Health import that says how many records
* were left out because HealthLog wrote them into Apple Health itself.
*
* The count is often 1, and a single string rendered "1 Einträge". The note
* reads the plural tier of the active locale, so each locale's own singular
* (and the Polish few form) reaches the screen.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { I18nProvider } from "@/lib/i18n/context";
import type { MessageBundle } from "@/lib/i18n/load-locale";
import { locales, type Locale } from "@/lib/i18n/config";
import { AppleHealthOwnRecordsNote } from "../apple-health-import-card";
function bundle(locale: Locale): MessageBundle {
return JSON.parse(
readFileSync(join(process.cwd(), "messages", `${locale}.json`), "utf8"),
) as MessageBundle;
}
function render(locale: Locale, count: number): string {
return (
renderToStaticMarkup(
<I18nProvider initialLocale={locale} initialMessages={bundle(locale)}>
<AppleHealthOwnRecordsNote count={count} />
</I18nProvider>,
)
// Text between the tags; a test reading its own static markup, not a
// sanitiser for untrusted input.
.split(/<[^>]*>/)
.join("")
.trim()
);
}
describe("<AppleHealthOwnRecordsNote>", () => {
it("uses the singular for one record", () => {
expect(render("de", 1)).toMatch(/^1 Eintrag,/);
expect(render("en", 1)).toMatch(/^1 record that/);
expect(render("es", 1)).toContain("1 registro que");
expect(render("fr", 1)).toMatch(/^1 enregistrement que/);
expect(render("it", 1)).toMatch(/^1 registrazione che/);
expect(render("pl", 1)).toContain("1 wpis,");
});
it("uses the plural for several, and the Polish few form for 2 to 4", () => {
expect(render("de", 5)).toMatch(/^5 Einträge,/);
expect(render("en", 5)).toMatch(/^5 records that/);
expect(render("pl", 3)).toContain("3 wpisy,");
expect(render("pl", 5)).toContain("5 wpisów,");
});
it("renders a sentence with the count in every locale", () => {
for (const locale of locales) {
for (const count of [1, 2, 5]) {
const text = render(locale, count);
expect(text, `${locale} ${count}`).toContain(String(count));
expect(text, `${locale} ${count}`).not.toContain("writtenByHealthLog");
}
}
});
});
@@ -31,6 +31,12 @@ interface JobStatus {
totals?: { recordsRead?: number; rowsUpserted?: number };
clinical?: { skipped?: number };
cumulativeEstimates?: { days?: number; rows?: number };
/** Records HealthLog wrote into Apple Health itself, left out here. */
writtenByHealthLog?: {
byMarker?: number;
byExternalId?: number;
matchedManual?: number;
};
/** HK types deferred by design — read but not (yet) imported. */
deferred?: Record<string, number>;
/** Per-key drop counters — plain HK types are unsupported-by-design,
@@ -247,6 +253,22 @@ export function AppleHealthSkipLines({
);
}
/** How many records the import left out because HealthLog wrote them. */
export function AppleHealthOwnRecordsNote({ count }: { count: number }) {
const { tCount } = useTranslations();
return (
<p
data-testid="import-apple-health-own-skipped"
className="text-muted-foreground text-sm"
>
{tCount(
"settings.sections.export.import.appleHealth.writtenByHealthLog",
count,
)}
</p>
);
}
export function AppleHealthImportCard() {
const { t } = useTranslations();
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -342,6 +364,10 @@ export function AppleHealthImportCard() {
const status = statusQuery.data ?? null;
const isRunning = status !== null && !TERMINAL_STATES.includes(status.status);
const isDone = status?.status === "done";
const ownRecordsSkipped =
(status?.result?.writtenByHealthLog?.byMarker ?? 0) +
(status?.result?.writtenByHealthLog?.byExternalId ?? 0) +
(status?.result?.writtenByHealthLog?.matchedManual ?? 0);
const isFailed = status?.status === "failed";
const busy = uploading || isRunning;
const percent = status?.progress?.percent ?? null;
@@ -492,6 +518,9 @@ export function AppleHealthImportCard() {
})}
</p>
)}
{isDone && ownRecordsSkipped > 0 && (
<AppleHealthOwnRecordsNote count={ownRecordsSkipped} />
)}
{isDone && (status?.result?.cumulativeEstimates?.days ?? 0) > 0 && (
<AppleHealthEstimateWarning
days={status?.result?.cumulativeEstimates?.days ?? 0}