feat: implement curriculum management system including automated generation, enrichment, and versioning workflows

This commit is contained in:
RaymondVerhoef
2026-05-24 19:50:20 +02:00
parent 8e01b21a50
commit c5e23c77cd
15 changed files with 1354 additions and 623 deletions

View File

@@ -1,7 +1,7 @@
import * as db from './db';
import { callLLM, cachedSystem } from './llm';
import { EMIT_QUIZ_QUESTIONS_TOOL } from './llmTools';
import { getCurriculumTopic, getQuarterForWeek } from './curriculumService';
import { getCurrentWeekContent } from './curriculumService';
import { shuffle, sample } from './random';
const QUIZ_SYSTEM = `You are a quiz generator for Respellion, an internal IT company learning platform.
@@ -85,38 +85,26 @@ Options must be prefixed "A) ", "B) ", "C) ", "D) ". Make questions specific and
return emitted.questions;
}
async function selectTestTopics(userId, weekNumber) {
async function selectTestTopics(userId, isoWeekNumber) {
const allTopics = await db.getTopics();
const topics = allTopics.filter(t => t.type !== 'fact' && t.learning_relevance !== 'exclude');
if (!topics || topics.length === 0) return { primaryTopic: null, reviewTopics: [], isReviewWeek: false };
try {
const { topic, curriculumEntry } = await getCurriculumTopic(weekNumber);
const weekContent = await getCurrentWeekContent(isoWeekNumber);
if (curriculumEntry?.is_review_week) {
const quarter = getQuarterForWeek(weekNumber);
const curriculum = await db.getCurriculum(new Date().getFullYear());
const quarterTopicIds = curriculum
.filter(w => w.quarter === quarter && w.topic_id && !w.is_review_week)
.map(w => w.topic_id);
const quarterTopics = topics.filter(t => quarterTopicIds.includes(t.id));
return {
primaryTopic: quarterTopics[0] || topics[0],
reviewTopics: quarterTopics.slice(1),
isReviewWeek: true,
};
}
if (topic) {
const others = topics.filter(t => t.id !== topic.id);
if (weekContent && weekContent.topics && weekContent.topics.length > 0) {
const primaryTopic = weekContent.topics[0]; // Use first topic as primary for now
const others = topics.filter(t => t.id !== primaryTopic.id);
const reviewTopics = sample(others, Math.min(5, others.length));
return { primaryTopic: topic, reviewTopics, isReviewWeek: false };
return { primaryTopic, reviewTopics, isReviewWeek: false };
}
} catch (e) {
console.warn('[Test] Curriculum lookup failed, falling back to hash:', e.message);
}
const str = `${userId}:${weekNumber}`;
// Fallback hash-based assignment
const str = `${userId}:${isoWeekNumber}`;
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = (hash << 5) - hash + str.charCodeAt(i);