DocDoeAI / scripts /video-engine /export-grounded-artifacts.ts
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
65d68f5
Raw History Blame Contribute Delete
5.51 kB
import { existsSync } from "node:fs";
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import chapterManifest from "../../data/curriculum/kerala-sslc/physics/chapter-manifest.json";
import { buildCaptionCues, buildNarrationSegments } from "../../src/video-engine/physics/audioTiming";
import {
soundWavesLessonContract,
soundWavesVisualBeatGraph,
} from "../../src/video-engine/physics/data/soundWavesContinuousLesson";
import { validateLessonContract } from "../../src/video-engine/physics/validation";
const outputDir = join(
process.cwd(),
"data",
"curriculum",
"kerala-sslc",
"physics",
"generated",
soundWavesLessonContract.chapterId,
soundWavesLessonContract.lessonId,
);
async function writeJson(name: string, value: unknown) {
await writeFile(join(outputDir, name), `${JSON.stringify(value, null, 2)}\n`, "utf8");
}
async function main() {
await mkdir(outputDir, { recursive: true });
const issues = validateLessonContract(soundWavesLessonContract, soundWavesVisualBeatGraph);
const errors = issues.filter((issue) => issue.severity === "error");
if (errors.length) throw new Error(`Lesson contract failed validation: ${JSON.stringify(errors)}`);
await writeJson("lesson-contract.json", soundWavesLessonContract);
await writeJson("visual-beat-graph.json", soundWavesVisualBeatGraph);
await writeJson("narration-segments.json", buildNarrationSegments(soundWavesVisualBeatGraph));
await writeJson("captions.json", buildCaptionCues(soundWavesVisualBeatGraph));
await writeJson("coverage-report.json", {
lessonId: soundWavesLessonContract.lessonId,
chapterId: soundWavesLessonContract.chapterId,
coveredEntries: soundWavesLessonContract.sourceCoverage,
beatCount: soundWavesVisualBeatGraph.beats.length,
coveredBeatCount: new Set(soundWavesLessonContract.sourceCoverage.flatMap((entry) => entry.visualBeatIds)).size,
completeForLesson: true,
completeForChapter: false,
note: "This report covers the implemented wave-motion lesson, not the entire Sound Waves chapter.",
});
await writeJson("qa-report.json", {
lessonId: soundWavesLessonContract.lessonId,
passed: issues.every((issue) => issue.severity !== "error"),
issues,
gates: {
sourceReferences: true,
questionRevealOrder: true,
continuousTimeline: true,
captionChunking: true,
measuredAudioTiming: false,
productionAudio: false,
fullChapterCoverage: false,
},
});
const curriculumRoot = join(process.cwd(), "data", "curriculum", "kerala-sslc", "physics");
const beatsById = new Map(soundWavesVisualBeatGraph.beats.map((beat) => [beat.beatId, beat]));
const coverageEntries = soundWavesLessonContract.sourceCoverage.map((entry) => {
const beat = beatsById.get(entry.visualBeatIds[0]);
return {
sourceId: soundWavesLessonContract.teachingSequence[0]?.sourceReferences[0]?.sourceId,
pageNumber: entry.pageNumber,
blockId: entry.blockId,
chapterId: soundWavesLessonContract.chapterId,
lessonId: soundWavesLessonContract.lessonId,
narrationSegmentIds: entry.narrationBeatIds,
visualBeatIds: entry.visualBeatIds,
renderedTimestamp: beat
? { startMs: beat.estimatedStartMs, endMs: beat.estimatedEndMs }
: null,
};
});
await writeFile(
join(curriculumRoot, "textbook-coverage.json"),
`${JSON.stringify({
schemaVersion: 1,
generatorVersion: "docdoe-physics-video-v1",
entries: coverageEntries,
summary: {
implementedLessonCount: 1,
completeChapterCount: 0,
note: "Coverage is complete for the implemented wave-motion lesson only.",
},
}, null, 2)}\n`,
"utf8",
);
const chapters = chapterManifest.chapters.map((chapter) => {
const isSoundWaves = chapter.chapterId === soundWavesLessonContract.chapterId;
const representativePreviewPath = "outputs/video-engine/continuous-sound-waves/sound-waves-continuous-preview.mp4";
const representativePreviewReady = isSoundWaves && existsSync(join(process.cwd(), representativePreviewPath));
return {
chapterId: chapter.chapterId,
chapterNumber: chapter.chapterNumber,
title: chapter.title,
sourcePages: chapter.sourcePages,
sourceIngested: true,
status: isSoundWaves ? "representative_lesson_ready" : "source_ingested",
lessonIds: isSoundWaves ? [soundWavesLessonContract.lessonId] : [],
lowResolutionPreviewReady: representativePreviewReady,
representativePreviewPath: representativePreviewReady ? representativePreviewPath : null,
productionReady: false,
chapterCoverageComplete: false,
blockingGates: isSoundWaves
? ["measured_audio_timing", "production_audio", "full_chapter_coverage"]
: ["lesson_compilation", "visual_primitive_validation", "representative_preview"],
};
});
await writeFile(
join(curriculumRoot, "all-chapter-generation-manifest.json"),
`${JSON.stringify({
schemaVersion: 1,
board: chapterManifest.board,
classLevel: chapterManifest.classLevel,
subject: chapterManifest.subject,
chapters,
rollout: {
currentStage: "A",
nextGate: "Attach measured narration audio and validate the Sound Waves preview before Stage B.",
},
}, null, 2)}\n`,
"utf8",
);
console.log(JSON.stringify({ outputDir, lessonFiles: 6, curriculumFiles: 2, issues }, null, 2));
}
void main();