| 123456789101112131415161718192021222324252627282930313233 |
- // Generated by scripts/build-workshop-skill.mjs; maintain the Skill report-statistics.mjs source.
- /** A semantic table within the existing report schema; no new section.kind or API fields. */
- export const STATISTICS_SECTION_ID = 'statistics-summary';
- export const STATISTICS_COLUMNS = Object.freeze(['维度/主题', '提及数 n', '有效样本 N', '统计口径与范围', '数据解释与限制', '产品含义', '证据']);
- const invalid = () => { throw Object.assign(new Error('REPORT_STATISTICS_INVALID'), { code: 'REPORT_STATISTICS_INVALID' }); };
- const hasText = value => typeof value === 'string' && Boolean(value.trim());
- const count = value => value === null || Number.isSafeInteger(value) && value >= 0;
- // The board's CJK typography pass may replace ASCII spaces with thin spaces.
- const columnKey = value => typeof value === 'string' ? value.replace(/\s/gu, '') : null;
- export function statisticsRows(section) {
- if (section?.id !== STATISTICS_SECTION_ID) return null;
- if (section.kind !== 'table' || !Array.isArray(section.data?.cols) || section.data.cols.length !== STATISTICS_COLUMNS.length
- || section.data.cols.some((column, index) => columnKey(column) !== columnKey(STATISTICS_COLUMNS[index]))
- || !Array.isArray(section.data?.rows) || !section.data.rows.length) invalid();
- return section.data.rows.map(row => {
- if (!Array.isArray(row) || row.length !== 7) invalid();
- const [label, numerator, denominator, scope, interpretation, implication, evidenceId] = row;
- if (![label, scope, interpretation, implication].every(hasText) || typeof evidenceId !== 'string'
- || !count(numerator) || !count(denominator)
- || numerator !== null && denominator !== null && numerator > denominator) invalid();
- const ratio = numerator !== null && denominator !== null && denominator > 0 ? numerator / denominator : null;
- return { label, numerator, denominator, scope, interpretation, implication, evidenceId,
- ratio, percent: ratio === null ? null : (ratio * 100).toFixed(1) + '%' };
- });
- }
- export function validateReportStatistics(report) {
- for (const chapter of report?.chapters || []) for (const section of chapter.sections || []) {
- const rows = statisticsRows(section);
- if (rows?.some(row => row.evidenceId && !Object.hasOwn(report.evidence || {}, row.evidenceId))) invalid();
- }
- }
|