78 lines
4.3 KiB
JavaScript
78 lines
4.3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
|
|
import { classifyPreviousPage, destinationForPageId } from './lib/previous-wiki.mjs';
|
|
|
|
const live = JSON.parse(await readFile('to-be-studied/previous-wiki/2026-08-03/manifest.json', 'utf8'));
|
|
const migration = JSON.parse(await readFile('docs/migration-manifest.json', 'utf8'));
|
|
const supplemental = JSON.parse(await readFile('docs/supplemental-content.json', 'utf8'));
|
|
const publishedLiveCourses = new Map(supplemental
|
|
.filter(({ category }) => category === 'live-course-recovery')
|
|
.map((entry) => [entry.source, entry.destination]));
|
|
const represented = new Map(migration.entries.map((entry) => [
|
|
entry.source.replace(/^pages\//, '').replace(/\.txt$/, '').replaceAll('/', ':'),
|
|
entry.destination,
|
|
]));
|
|
|
|
const records = [];
|
|
for (const page of live.pages) {
|
|
const source = await readFile(`to-be-studied/previous-wiki/2026-08-03/${page.localPath}`, 'utf8');
|
|
let status = classifyPreviousPage(page.id, new Set(represented.keys()), source);
|
|
const plannedDestination = status === 'migrate' ? destinationForPageId(page.id) : '';
|
|
if (status === 'migrate' && publishedLiveCourses.get(page.url) === plannedDestination) status = 'migrated';
|
|
const destination = status === 'represented'
|
|
? represented.get(page.id)
|
|
: status === 'migrate' || status === 'migrated'
|
|
? plannedDestination
|
|
: '';
|
|
const reason = {
|
|
represented: 'Already represented by the selected export migration.',
|
|
migrate: 'Substantive public course page missing from the selected export.',
|
|
migrated: 'Substantive public course page migrated from the live DokuWiki.',
|
|
empty: 'Explicit placeholder page with no course details.',
|
|
excluded: 'Outside the Bachelor, Master AI, and Master DSDM course-detail scope.',
|
|
}[status];
|
|
records.push({ id: page.id, url: page.url, status, destination, reason, localPath: page.localPath });
|
|
}
|
|
for (const failure of live.failures) {
|
|
records.push({ id: failure.id, url: failure.url, status: 'failed', destination: '', reason: failure.error, localPath: '' });
|
|
}
|
|
records.sort((a, b) => a.id.localeCompare(b.id));
|
|
|
|
const header = ['page_id', 'source_url', 'status', 'destination', 'captured_source', 'reason'];
|
|
const tsv = [header, ...records.map((record) => [
|
|
record.id,
|
|
record.url,
|
|
record.status,
|
|
record.destination,
|
|
record.localPath ? `to-be-studied/previous-wiki/2026-08-03/${record.localPath}` : '',
|
|
record.reason,
|
|
])].map((row) => row.join('\t')).join('\n');
|
|
await writeFile('docs/live-course-recovery.tsv', `${tsv}\n`);
|
|
|
|
const counts = new Map();
|
|
for (const record of records) counts.set(record.status, (counts.get(record.status) ?? 0) + 1);
|
|
const report = `# Previous live-wiki course recovery
|
|
|
|
Audit date: 2026-08-03
|
|
|
|
The previous DokuWiki was crawled recursively through its public sitemap at the pinned Vimmex origin. The crawl was read-only and completed without authentication.
|
|
|
|
| Outcome | Pages |
|
|
| --- | ---: |
|
|
| Already represented in the selected export migration | ${counts.get('represented') ?? 0} |
|
|
| Course pages migrated | ${counts.get('migrated') ?? 0} |
|
|
| Course pages still pending | ${counts.get('migrate') ?? 0} |
|
|
| Empty course placeholders | ${counts.get('empty') ?? 0} |
|
|
| Outside the course-detail scope | ${counts.get('excluded') ?? 0} |
|
|
| Capture failures | ${counts.get('failed') ?? 0} |
|
|
| **Total public pages audited** | **${records.length}** |
|
|
|
|
The selected filesystem export was internally complete for its declared 29-source scope, but it was not a complete export of the public DokuWiki. The recursive live sitemap exposed ${records.length} pages. After excluding ${counts.get('empty') ?? 0} explicit placeholders and non-course material, ${counts.get('migrated') ?? 0} substantive course pages were migrated and ${counts.get('migrate') ?? 0} remain pending.
|
|
|
|
The machine-readable classification and source-to-destination mappings are in \`docs/live-course-recovery.tsv\`. Captured raw sources and the crawl manifest are under \`to-be-studied/previous-wiki/2026-08-03/\`.
|
|
`;
|
|
await writeFile('docs/live-course-recovery.md', report);
|
|
|
|
console.log(`Classified ${records.length} pages: ${counts.get('migrated') ?? 0} migrated, ${counts.get('migrate') ?? 0} pending, ${counts.get('represented') ?? 0} represented, ${counts.get('empty') ?? 0} empty, ${counts.get('excluded') ?? 0} excluded, ${counts.get('failed') ?? 0} failed.`);
|