feat(scheduler): each row shows its last three runs, an interrupted run as such, and the ad-hoc runs beside the jobs
A row's one dot said how the LAST run went, and an interrupted run fell through to "idle" — a grey dot that read as "no run recorded". The row now leads with its last three runs, newest first, each named for a screen reader and in its tooltip (how it went, when, who started it); an interrupted run is a ring, set apart from a skipped run and a failure, and its line says the server restarted under it. A channel's full-history backfill gets its own "Ad-hoc runs" block: its runs, its channel and what it fetched, no interval and no run button — it needs its target. AD_HOC_JOBS names every such job; the backend-id contract holds their names in both languages, and run_ad_hoc refuses any other id.
This commit is contained in:
@@ -10,7 +10,7 @@ import { vocabKey } from "../lib/vocab";
|
||||
// the registries straight from the backend sources:
|
||||
// - `sysconfig.SPECS` → `config.groups.<group>`, `config.fields.<key>.label` + `.hint`, and for a
|
||||
// "choice" key `config.choices.<key>.<option>` — the options read from its Literal in config.py
|
||||
// - `scheduler.JOB_INTERVALS` → `scheduler.jobs.<id>` + `scheduler.jobDesc.<id>`
|
||||
// - `scheduler.JOB_INTERVALS` and `scheduler.AD_HOC_JOBS` → `scheduler.jobs.<id>` + `scheduler.jobDesc.<id>`
|
||||
// - the `audit` action enum → `auditActions.<id>`; the `quota` action enum → `quotaActions.<id>`
|
||||
// and the other way round: a name left behind for an id the backend dropped is dead weight that reads
|
||||
// as a live setting to the next person (`plex_sync_interval_min` moved to the scheduler long ago).
|
||||
@@ -56,11 +56,18 @@ const choicesOf = (attr: string): string[] => {
|
||||
const choiceIds = specs
|
||||
.filter((s) => s.type === "choice")
|
||||
.flatMap((s) => choicesOf(s.attr).map((c) => `config.choices.${s.key}.${c}`));
|
||||
const schedulerPy = backend("scheduler.py");
|
||||
// The interval jobs, and the ad-hoc ones (`AD_HOC_JOBS`, a tuple of constants named beside it).
|
||||
const jobs = [
|
||||
...(backend("scheduler.py").split("JOB_INTERVALS")[1] ?? "")
|
||||
.split("}")[0]!
|
||||
.matchAll(/"(\w+)": settings\./g),
|
||||
].map((m) => m[1]!);
|
||||
...[
|
||||
...(schedulerPy.split("JOB_INTERVALS")[1] ?? "")
|
||||
.split("}")[0]!
|
||||
.matchAll(/"(\w+)": settings\./g),
|
||||
].map((m) => m[1]!),
|
||||
...[...(/^AD_HOC_JOBS = \(([^)]*)\)/m.exec(schedulerPy)?.[1] ?? "").matchAll(/\b[A-Z_]+\b/g)].map(
|
||||
(m) => new RegExp(`^${m[0]} = "(\\w+)"$`, "m").exec(schedulerPy)?.[1] ?? m[0],
|
||||
),
|
||||
];
|
||||
const enumValues = (file: string) =>
|
||||
[...backend(file).matchAll(/^\s+[A-Z_]+ = "(\w+)"$/gm)].map((m) => m[1]!);
|
||||
const auditActions = enumValues("audit.py");
|
||||
|
||||
Reference in New Issue
Block a user