File size: 6,559 Bytes
fe2f79f ca816a7 fe2f79f ca816a7 fe2f79f b9727b3 fe2f79f b9727b3 ca816a7 b9727b3 ca816a7 b9727b3 ca816a7 b9727b3 fe2f79f b9727b3 ca816a7 b9727b3 ca816a7 b9727b3 f0f0df4 19d10a4 b9727b3 ca816a7 b9727b3 42c5945 ca816a7 42c5945 b9727b3 42c5945 b9727b3 42c5945 ca816a7 b9727b3 fe2f79f b9727b3 19d10a4 b9727b3 19d10a4 b9727b3 fe2f79f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | import { PrismaClient } from '@prisma/client';
import fs from 'fs';
import path from 'path';
/**
* Seeds the database with the curriculum loaded from JSON files.
* Safe to call multiple times — checks for existing data before inserting/updating.
*/
export async function seedDatabase(prisma: PrismaClient): Promise<{ seeded: boolean; message: string }> {
const tracksDir = path.resolve(__dirname, '../content/tracks');
const modulesDir = path.resolve(__dirname, '../content/modules');
let totalSynced = 0;
// 1. Sync regular tracks
if (fs.existsSync(tracksDir)) {
const trackFiles = fs.readdirSync(tracksDir).filter(f => f.endsWith('.json'));
console.log(`[SEED] Found ${trackFiles.length} track files.`);
for (const file of trackFiles) {
await syncTrackFile(prisma, path.join(tracksDir, file));
totalSynced++;
}
}
// 2. Sync modular adaptive content
if (fs.existsSync(modulesDir)) {
const moduleFiles = fs.readdirSync(modulesDir).filter(f => f.endsWith('.json'));
console.log(`[SEED] Found ${moduleFiles.length} modular files.`);
for (const file of moduleFiles) {
await syncModuleFile(prisma, path.join(modulesDir, file));
totalSynced++;
}
}
return { seeded: true, message: `✅ ${totalSynced} files synchronized successfully.` };
}
async function syncTrackFile(prisma: PrismaClient, filePath: string) {
const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
if (!content.trackId || !Array.isArray(content.days)) return;
const track = await prisma.track.upsert({
where: { id: content.trackId },
update: {
title: content.title,
description: content.description || '',
duration: Math.ceil(content.days.reduce((max: number, d: any) => Math.max(max, d.dayNumber), 0)),
language: content.language,
},
create: {
id: content.trackId,
title: content.title,
description: content.description || '',
duration: Math.ceil(content.days.reduce((max: number, d: any) => Math.max(max, d.dayNumber), 0)),
language: content.language,
}
});
for (const day of content.days) {
await upsertTrackDay(prisma, track.id, day.dayNumber, {
title: day.title || `Jour ${day.dayNumber}`,
lessonText: day.lessonText,
exercisePrompt: day.exercisePrompt,
exerciseType: day.exerciseType || 'TEXT',
exerciseCriteria: day.exerciseCriteria || null,
badges: day.badges || null,
audioUrl: day.audioUrl || null,
imageUrl: day.imageUrl || null,
videoUrl: day.videoUrl || null,
videoCaption: day.videoCaption || null,
buttonsJson: day.buttonsJson || null,
});
}
}
async function syncModuleFile(prisma: PrismaClient, filePath: string) {
try {
const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
if (!content.meta?.id) return;
const trackTitle = `Module: ${content.meta.secteur || content.meta.id}`;
const trackDescription = `Adaptive module for ${content.meta.secteur || 'general purpose'}`;
let langue = (content.meta.langue || 'FR').toUpperCase();
if (langue === 'WO') langue = 'WOLOF';
if (langue !== 'FR' && langue !== 'WOLOF') langue = 'FR';
const track = await prisma.track.upsert({
where: { id: content.meta.id },
update: {
title: trackTitle,
description: trackDescription,
duration: Array.isArray(content.modules) ? content.modules.length : 1,
language: langue as any,
},
create: {
id: content.meta.id,
title: trackTitle,
description: trackDescription,
duration: Array.isArray(content.modules) ? content.modules.length : 1,
language: langue as any,
}
});
if (Array.isArray(content.modules)) {
// Multi-day module
for (let i = 0; i < content.modules.length; i++) {
const mod = content.modules[i];
const dayNumber = i + 1;
await upsertTrackDay(prisma, track.id, dayNumber, {
title: mod.id || `M${dayNumber}`,
lessonText: mod.scenario?.lessonText || mod.content?.FR?.lessonText || '',
exercisePrompt: mod.exercice?.question || mod.content?.FR?.exercisePrompt || '',
exerciseType: mod.exercice?.type || 'TEXT',
exerciseCriteria: mod.exercice?.criteria || mod.exercice || null,
buttonsJson: mod.content ? { content: mod.content } : null,
badges: null,
audioUrl: null,
});
}
} else {
// Single-day module (the root is the module)
await upsertTrackDay(prisma, track.id, 1, {
title: content.meta.id,
lessonText: content.content?.FR?.lessonText || '',
exercisePrompt: content.content?.FR?.exercisePrompt || '',
exerciseType: 'TEXT',
exerciseCriteria: {
rules_engine: content.rules_engine,
scoring: content.scoring
},
buttonsJson: content.content ? { content: content.content } : null,
badges: null,
audioUrl: null,
});
}
} catch (err: any) {
console.error(`[SEED] Failed to sync module file ${filePath}:`, err.message);
}
}
async function upsertTrackDay(prisma: PrismaClient, trackId: string, dayNumber: number, data: any) {
const existing = await prisma.trackDay.findFirst({
where: { trackId, dayNumber }
});
if (existing) {
await prisma.trackDay.update({
where: { id: existing.id },
data: {
...data,
videoUrl: data.videoUrl || null,
videoCaption: data.videoCaption || null,
}
});
} else {
await prisma.trackDay.create({
data: {
...data,
trackId,
dayNumber,
videoUrl: data.videoUrl || null,
videoCaption: data.videoCaption || null,
}
});
}
}
|