| import { PrismaClient } from '@prisma/client'; |
| import fs from 'fs'; |
| import path from 'path'; |
|
|
| |
| |
| |
| |
| 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'); |
|
|
| |
| const defaultOrg = await prisma.organization.upsert({ |
| where: { id: 'xamle-admin-org' }, |
| update: {}, |
| create: { |
| id: 'xamle-admin-org', |
| name: 'XAMLÉ Admin', |
| slug: 'admin' |
| } |
| }); |
|
|
| let totalSynced = 0; |
|
|
| |
| 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), defaultOrg.id); |
| totalSynced++; |
| } |
| } |
|
|
| |
| 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), defaultOrg.id); |
| totalSynced++; |
| } |
| } |
|
|
| return { seeded: true, message: `✅ ${totalSynced} files synchronized successfully.` }; |
| } |
|
|
| async function syncTrackFile(prisma: PrismaClient, filePath: string, organizationId: 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, |
| organizationId |
| } |
| }); |
|
|
| 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, |
| }, organizationId); |
| } |
| } |
|
|
| async function syncModuleFile(prisma: PrismaClient, filePath: string, organizationId: 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, |
| organizationId |
| } |
| }); |
|
|
| if (Array.isArray(content.modules)) { |
| |
| 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, |
| }, organizationId); |
| } |
| } else { |
| |
| 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, |
| }, organizationId); |
| } |
| } 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, organizationId: string) { |
| const existing = await prisma.trackDay.findFirst({ |
| where: { trackId, dayNumber } |
| }); |
|
|
| if (existing) { |
| await prisma.trackDay.update({ |
| where: { id: existing.id }, |
| data: { |
| ...data, |
| organizationId, |
| videoUrl: data.videoUrl || null, |
| videoCaption: data.videoCaption || null, |
| } |
| }); |
| } else { |
| await prisma.trackDay.create({ |
| data: { |
| ...data, |
| trackId, |
| dayNumber, |
| organizationId, |
| videoUrl: data.videoUrl || null, |
| videoCaption: data.videoCaption || null, |
| } |
| }); |
| } |
| } |
|
|