← Files DataARCHIVED FILE
templates/data-app/base/examples/reports/contextual-stories/stories.mjs
7.6 KB · Oct 3, 2026 · 06:03 UTC
import assert from "node:assert/strict";
import { createHash, randomUUID } from "node:crypto";
import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { contextualCases, currentContext, exactDate } from "./context.mjs";
export { contextualCases } from "./context.mjs";
const sha256 = (input) => createHash("sha256").update(input).digest("hex");
const evidenceFile = "src/content/report/evidence/evidence.json";
const files = [evidenceFile, "src/content/report/stories.mjs", "src/content/report/context.mjs"];
/** Exact file-based left join. Dates are keys, not fuzzy event associations. */
export function joinContextRows(observations, contextRecords) {
return observations.toSorted((left, right) => left.date.localeCompare(right.date)).map((row) => {
const matches = contextRecords.filter((record) => record.date === row.date);
return { ...row, context: matches.length === 1 ? matches[0].text : "",
contextId: matches.length === 1 ? matches[0].id : "" };
});
}
export function createContextualStories(input, generatedAt = new Date().toISOString(), { id = randomUUID() } = {}) {
const evidence = JSON.parse(input);
assert.equal(evidence.fictional, true, "The example must disclose its fictional evidence");
assert.deepEqual(evidence.cases.map((entry) => entry.id).toSorted(), contextualCases.map((entry) => entry.id).toSorted());
const inputSha256 = sha256(input);
const queries = {};
const results = { inputSha256, cases: [] };
for (const story of contextualCases) {
const { observations, contextRecords } = evidence.cases.find((entry) => entry.id === story.id);
assert.ok(observations.length >= 2 && observations.every((row) => exactDate(row.date)
&& Number.isFinite(row[story.valueField]) && row[story.valueField] >= 0));
assert.equal(new Set(observations.map((row) => row.date)).size, observations.length, "Observation dates must be unique");
assert.ok(contextRecords.every((record) => typeof record.id === "string" && record.id
&& exactDate(record.date) && typeof record.text === "string" && record.text.trim() && record.text.length <= 160
&& !/[\u0000-\u001f\u007f]/u.test(record.text)), "Context records need exact dates and bounded factual text");
assert.equal(new Set(contextRecords.map((record) => record.id)).size, contextRecords.length, "Context record IDs must be unique");
if (story.id === "adjustment") assert.ok(observations.every((row) => Number.isFinite(row.oneTimeAmount)
&& row.oneTimeAmount >= 0 && row.oneTimeAmount <= row[story.valueField]), "One-time amounts must reconcile to posted billings");
if (story.id === "coverage") assert.ok(observations.every((row) => Number.isInteger(row.coveredLocations)
&& row.coveredLocations > 0), "Coverage needs a positive location count");
const rows = joinContextRows(observations, contextRecords);
const componentIds = [`context-${story.id}-answer`, `context-${story.id}-chart`];
const queryText = `joinContextRows(evidence.cases.find(entry => entry.id === ${JSON.stringify(story.id)}).observations, `
+ `evidence.cases.find(entry => entry.id === ${JSON.stringify(story.id)}).contextRecords)`;
const definition = (label, field, text) => ({ label, field, definition: text, componentIds,
sourceLineage: [{ files }] });
const recordText = contextRecords.map((record) => `${record.id} | ${record.date} | ${record.text}. ${record.detail ?? ""}`).join("\n");
const metricDefinitions = [
definition(story.title, story.valueField, `Recorded ${story.unit}. Values are fictional observations, not simulated intervention effects.`),
definition("Observation date", "date", `Exact UTC ${story.period} date of the measurement.`),
definition("Contextual record", "context", `Exact text joined from a fictional context record on its recorded date. Records: ${recordText || "None supplied."}`),
definition("Context record ID", "contextId", "Stable identifier of the single matching context record. Empty when no unambiguous record matches."),
];
if (story.id === "adjustment") metricDefinitions.push(definition("One-time billing amount (USD)", "oneTimeAmount",
"Amount of the catch-up invoice included in that week's posted billings. Billings excluding the entry = postedBillingsUsd - oneTimeAmount. This is an accounting subtraction, not an estimate of business impact."));
if (story.id === "coverage") metricDefinitions.push(definition("Monitored locations", "coveredLocations",
"Number of locations whose detected incidents are included in that week's total. A change in this count changes the reporting population."));
queries[story.queryId] = { label: story.title, reportingField: "date", rows, source: {
label: `Fictional ${story.id} observations and context records`, files, metricDefinitions,
query: { description: queryText, code: queryText },
evidenceFlow: [
{ title: "Read fictional evidence", detail: `${evidenceFile}; SHA-256 ${inputSha256}. Observations and records were authored together for this example.` },
{ title: "Join exact dates", detail: `${queryText}. Preserve observation values. Join context text and record ID only for one exact date match. Missing or conflicting records leave the context empty. No SQL or external service is used.` },
{ title: "Context records", detail: recordText || "No contextual record supplied for this case." },
],
} };
const record = currentContext(story, rows);
results.cases.push({ id: story.id, rowCount: rows.length, firstDate: rows[0].date, lastDate: rows.at(-1).date,
contextRecordIds: contextRecords.map((entry) => entry.id), rowsSha256: sha256(JSON.stringify(rows)),
...(story.id === "adjustment" ? { withoutOneTime: rows.map((row) => ({ date: row.date, postedBillingsUsd: row.postedBillingsUsd - row.oneTimeAmount })) } : {}),
annotationLabel: record?.context ?? null, annotationDate: record?.date ?? null });
}
const snapshot = { id, surface: "report", title: "Context that changes how you read the chart", generatedAt,
status: "fixture", filters: [], queries,
contextualStories: contextualCases.map((story) => ({ ...story,
annotationLabel: results.cases.find((entry) => entry.id === story.id).annotationLabel })) };
return { snapshot, results };
}
export function reproduceContextualStories(existing, input) {
const { snapshot: reviewed, results } = createContextualStories(input, existing.generatedAt, { id: existing.id ?? randomUUID() });
return { snapshot: { ...reviewed, ...existing, id: reviewed.id, queries: reviewed.queries, contextualStories: reviewed.contextualStories,
...(!existing.id && existing.title && !existing.legacyPresentationTitle ? { legacyPresentationTitle: existing.title } : {}) }, results };
}
if (process.argv[1] && existsSync(process.argv[1]) && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
const content = dirname(fileURLToPath(import.meta.url));
const project = resolve(content, "../../..");
if (content !== join(project, "src/content/report")) throw new Error("Run this reproducer in a generated report, or use contextual-stories/build.mjs.");
const { snapshot, results } = reproduceContextualStories(JSON.parse(readFileSync(join(project, "src/data.json"), "utf8")),
readFileSync(join(content, "evidence/evidence.json"), "utf8"));
writeFileSync(join(project, "src/data.json"), `${JSON.stringify(snapshot, null, 2)}\n`);
writeFileSync(join(content, "evidence/story-results.json"), `${JSON.stringify(results, null, 2)}\n`);
console.log(JSON.stringify(results));
}
SHA-256: 41fab303ba7e86d6f9160f63e4f1db3d36d3760eb96927cae40cc671b99f3207