CognxSafeTrack
fix(ai): add imageUrl field to Prisma schema and correctly route language selection inside whatsapp.ts
f0f0df4
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,
}
});
}
}