kanban-dev-webapp/scripts/compute.ts
Dave 19e9dc29ac feat(dashboard): remove session-clustering paragraph from methodology
The methodology panel still explains 'what hours are' and 'what they cannot
see'. The session-clustering detail — the constants — was the part a client
might want to interrogate rather than just be told. With this removed, the
constants are still stored inside the payload (so a future question can be
answered) but the visible panel is shorter.

Also dropped the now-unused destructured local in Methodology().
2026-09-16 15:30:46 +02:00

79 lines
2.3 KiB
TypeScript

/**
* Standalone recompute helper.
* Not part of the product — this is a CLI shim that performs the same flow
* as the integration flow without pulling in server-only modules.
*/
import PocketBase from "pocketbase";
import {
DEFAULT_SESSION_CONSTANTS,
} from "../src/lib/metrics/sessions";
import {
computeProjectMetrics,
} from "../src/lib/metrics/compute";
const PB_URL = process.env.NEXT_PUBLIC_PB_URL!;
async function main() {
const projectId = process.argv[2];
if (!projectId) throw new Error("Usage: tsx scripts/compute.ts <projectId>");
const pb = new PocketBase(PB_URL);
pb.autoCancellation(false);
await pb.collection("_superusers").authWithPassword(
process.env.PB_SUPERUSER_EMAIL!,
process.env.PB_SUPERUSER_PASSWORD!
);
const commits = await pb.collection("commits").getFullList({
filter: `project = "${projectId}"`,
fields: "type,scope,authored_at,is_merge",
});
const project = await pb.collection("projects").getOne(projectId);
const constants = {
sessionGapMinutes:
Number((project as Record<string, unknown>).session_gap_minutes) ||
DEFAULT_SESSION_CONSTANTS.sessionGapMinutes,
preSessionMinutes:
Number((project as Record<string, unknown>).pre_session_minutes) ||
DEFAULT_SESSION_CONSTANTS.preSessionMinutes,
postSessionMinutes:
Number((project as Record<string, unknown>).post_session_minutes) ||
DEFAULT_SESSION_CONSTANTS.postSessionMinutes,
};
const metrics = computeProjectMetrics(
commits.map((c) => ({
type: c.type ?? "other",
scope: c.scope ?? "",
authoredAt: c.authored_at,
isMerge: Boolean(c.is_merge),
})),
constants
);
console.log("Recomputed:", metrics.totalCommits, "commits,", metrics.monthly.length, "months");
console.log("Monthly:");
for (const mo of metrics.monthly) {
console.log(" ", mo);
}
const payload = {
project: projectId,
computed_at: new Date().toISOString(),
payload: metrics,
};
const existing = await pb
.collection("project_metrics")
.getFirstListItem(`project = "${projectId}"`)
.catch(() => null);
if (existing) {
await pb.collection("project_metrics").update(existing.id, payload);
} else {
await pb.collection("project_metrics").create(payload);
}
console.log("Stored.");
}
main().catch((e) => { console.error(e); process.exit(1); });