feat(inbox): a run notice from before the result rode along reads its one-liner back into one (E30 S2)
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { api, type AppNotification, type JobResult } from "../lib/api";
|
||||
import { jobResultText } from "../lib/jobResult";
|
||||
import { jobResultText, parseSummary } from "../lib/jobResult";
|
||||
import { notifyActionError } from "../lib/actionErrors";
|
||||
import { useLiveQuery } from "../lib/useLiveQuery";
|
||||
import { focusAccessRequestsTab } from "../lib/adminUsersTab";
|
||||
@@ -534,7 +534,8 @@ function NotificationRow({
|
||||
title = t(d.status === "error" ? "inbox.jobDone.titleError" : "inbox.jobDone.titleOk", {
|
||||
job,
|
||||
});
|
||||
body = d.result ? jobResultText(t, d.result) : d.summary || n.body;
|
||||
const result = d.result ?? parseSummary(d.summary);
|
||||
body = result ? jobResultText(t, result) : d.summary || n.body;
|
||||
} else if (isEmailUndelivered) {
|
||||
// Email is fail-soft: a drop leaves no error anywhere else, so this notice IS the trace. Name
|
||||
// the message that was lost, since that is what tells the operator what their users didn't get.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// one-liner was "recent={'channels_processed': 0, …}, deep={…}" — English keys and a Python repr.
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import i18n, { setLanguage } from "../i18n";
|
||||
import { jobResultText } from "./jobResult";
|
||||
import { jobResultText, parseSummary } from "./jobResult";
|
||||
|
||||
afterEach(() => setLanguage("en"));
|
||||
|
||||
@@ -39,3 +39,24 @@ describe("a job's result", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("an older notice's one-liner", () => {
|
||||
it("reads back into a result when flat", () => {
|
||||
expect(parseSummary("new=403, failed=396, fallback=375")).toEqual({
|
||||
new: 403,
|
||||
failed: 396,
|
||||
fallback: 375,
|
||||
});
|
||||
expect(parseSummary("enriched=0, skipped=tmdb_api_key not set")).toEqual({
|
||||
enriched: 0,
|
||||
skipped: "tmdb_api_key not set",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not when a phase is nested, or it is no summary at all", () => {
|
||||
expect(parseSummary("recent={'channels_processed': 0}, deep={'videos_added': 1}")).toBeNull();
|
||||
expect(parseSummary("sync paused")).toBeNull();
|
||||
expect(parseSummary("42")).toBeNull();
|
||||
expect(parseSummary(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,21 @@ import type { JobResult } from "./api";
|
||||
import { formatNumber } from "./format";
|
||||
import { vocabKey } from "./vocab";
|
||||
|
||||
/** A notice from before the result rode along as data holds only the server's one-liner: `_summarize`'s
|
||||
* "new=3, failed=0" (backend/app/scheduler.py). A flat one reads back into a result; a nested phase
|
||||
* (a Python dict repr) or anything else does not, and stays as it was. */
|
||||
export function parseSummary(summary: string | null | undefined): JobResult | null {
|
||||
if (!summary || /[{}[\]]/.test(summary)) return null;
|
||||
const out: JobResult = {};
|
||||
for (const part of summary.split(", ")) {
|
||||
const m = /^(\w+)=(.+)$/.exec(part);
|
||||
if (!m) return null;
|
||||
const [, key, raw] = m;
|
||||
out[key!] = /^-?\d+$/.test(raw!) ? Number(raw) : raw!;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** A scheduler job's result in the reader's language (E30): "új videó: 3 · sikertelen: 0". The server
|
||||
* stored "new=3, failed=0" — English keys, a Python dict repr for a nested phase. A count's key is
|
||||
* named from `scheduler.result`, a `skipped` / `reason` word from `scheduler.skip`; an unnamed key or
|
||||
|
||||
Reference in New Issue
Block a user