Files
natural-language-report-ser…/src/runner/run.js
2026-09-27 00:15:18 +02:00

50 lines
1.9 KiB
JavaScript

import { update, uuid, sparqlEscapeUri, sparqlEscapeString, sparqlEscapeDateTime } from 'mu';
import { seed } from './seed.js';
import { fetchColumns } from './columns.js';
import { assemble } from './assemble.js';
import { startShape } from './profile.js';
const REPORT_CLASS = process.env.REPORT_CLASS || 'http://lblod.data.gift/vocabularies/reporting/Report';
const CSV_SEPARATOR = process.env.CSV_SEPARATOR || ';';
// run(spec, profile) → the CSV text. seed → columns → assemble → CSV.
export async function run(spec, profile) {
const subjects = await seed(spec, startShape(profile, spec));
const values = await fetchColumns(subjects, spec);
const rows = assemble(subjects, values, spec);
return rows.map(row => row.map(csvCell).join(CSV_SEPARATOR)).join('\n');
}
// A cell is quoted only when it holds the separator, a quote or a newline;
// a quote inside a cell is doubled, per RFC 4180.
function csvCell(cell) {
const s = String(cell ?? '');
return s.includes(CSV_SEPARATOR) || s.includes('"') || /[\n\r]/.test(s)
? `"${s.replace(/"/g, '""')}"`
: s;
}
// One report pointing at the logical file URI of its CSV.
export async function registerReport(title, fileUri) {
const id = uuid();
const reportUri = `http://data.lblod.info/id/reports/${id}`;
await update(`
INSERT DATA {
${sparqlEscapeUri(reportUri)} a ${sparqlEscapeUri(REPORT_CLASS)} ;
${sparqlEscapeUri('http://mu.semte.ch/vocabularies/core/uuid')} ${sparqlEscapeString(id)} ;
${sparqlEscapeUri('http://purl.org/dc/terms/title')} ${sparqlEscapeString(title)} ;
${sparqlEscapeUri('http://purl.org/dc/terms/created')} ${sparqlEscapeDateTime(new Date())} ;
${sparqlEscapeUri('http://www.w3.org/ns/prov#generated')} ${sparqlEscapeUri(fileUri)} .
}`);
return reportUri;
}
export function slug(title) {
return (title || 'report').toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 80) || 'report';
}