Files
Momento/memento-note/lib/ai/services/study-planner.service.ts
Antigravity 104af3149f
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m28s
CI / Deploy production (on server) (push) Has been skipped
feat: générateur d'exercices + planning de révision IA
- Générateur d'exercices : bouton dans menu note → IA crée 5 exercices
  - Niveaux variés (facile/moyen/difficile) avec emojis 🟢🟡🔴
  - Corrigés détaillés dans des toggles (cliquer pour révéler)
  - Callout warning pour le niveau
  - Notes créées dans le même carnet
- Planning de révision : bouton dans barre carnet → IA crée planning
  - Choix date d'examen
  - Répétition espacée (première lecture → revoir → révision globale)
  - Rappels automatiques ajoutés aux notes (9h le jour J)
  - Vue chronologique avec activités et notes par jour
- Services : exercise-generator.service.ts + study-planner.service.ts
- Endpoints : /api/ai/generate-exercises + /api/ai/study-plan
- i18n FR/EN complet
2026-06-14 19:57:21 +00:00

126 lines
4.0 KiB
TypeScript

import { getChatProvider } from '../factory'
import { getSystemConfig } from '@/lib/config'
export interface StudyDay {
date: string
noteIds: string[]
noteTitles: string[]
activity: string
}
export interface StudyPlan {
days: StudyDay[]
totalDays: number
notesPerDay: number
}
export class StudyPlannerService {
async generate(
notes: Array<{ id: string; title: string }>,
examDate: string,
language?: string
): Promise<StudyPlan> {
const lang = language || 'fr'
const langName = lang === 'fr' ? 'français' : lang === 'fa' ? 'فارسی' : 'English'
const exam = new Date(examDate)
const today = new Date()
today.setHours(0, 0, 0, 0)
const daysUntilExam = Math.max(1, Math.ceil((exam.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)))
const notesList = notes.map((n, i) => `${i + 1}. ${n.title} (id: ${n.id})`).join('\n')
const prompt = `Tu es un expert en pédagogie et apprentissage. Crée un planning de révision optimisé.
DATE DE L'EXAMEN : ${examDate}
JOURS RESTANTS : ${daysUntilExam}
LANGUE : ${langName}
NOTES À RÉVISER :
${notesList}
Crée un planning qui répartit ces notes sur ${daysUntilExam} jours en utilisant la répétition espacée :
- Les premiers jours : couvrir tout le programme une fois
- Les jours suivants : revoir les notes difficiles plus fréquemment
- Les derniers jours : révision globale et fiches synthèses
- Inclus des jours de repos légers (relecture rapide)
FORMAT JSON UNIQUEMENT :
\`\`\`json
{
"days": [
{
"date": "YYYY-MM-DD",
"noteIds": ["id1", "id2"],
"activity": "Description courte de l'activité du jour"
}
]
}
\`\`\`
Génère exactement ${Math.min(daysUntilExam, 30)} entrées de jours (max 30).
Les dates vont de aujourd'hui (${today.toISOString().slice(0, 10)}) jusqu'à la veille de l'examen.
Chaque jour doit avoir 1-4 notes à réviser avec une activité descriptive.`
const config = await getSystemConfig()
const provider = getChatProvider(config)
const raw = await provider.generateText(prompt)
return this.parseResponse(raw, notes, daysUntilExam)
}
private parseResponse(
raw: string,
notes: Array<{ id: string; title: string }>,
totalDays: number
): StudyPlan {
const jsonMatch = raw.match(/```json\s*([\s\S]+?)\s*```/)
let jsonStr = jsonMatch ? jsonMatch[1] : raw
const start = jsonStr.indexOf('{')
const end = jsonStr.lastIndexOf('}')
if (start >= 0 && end > start) {
jsonStr = jsonStr.slice(start, end + 1)
}
try {
const parsed = JSON.parse(jsonStr)
const titleMap = new Map(notes.map(n => [n.id, n.title]))
const days: StudyDay[] = (parsed.days || []).map((d: any) => ({
date: String(d.date || ''),
noteIds: Array.isArray(d.noteIds) ? d.noteIds.map(String) : [],
noteTitles: (Array.isArray(d.noteIds) ? d.noteIds : []).map((id: string) => titleMap.get(id) || 'Note'),
activity: String(d.activity || 'Révision'),
}))
return {
days,
totalDays: days.length,
notesPerDay: days.length > 0 ? Math.round(notes.length / days.length) : 0,
}
} catch {
// Fallback: simple distribution
const days: StudyDay[] = []
const today = new Date()
const notesPerDay = Math.max(1, Math.ceil(notes.length / Math.min(totalDays, 14)))
for (let i = 0; i < Math.min(totalDays, 14); i++) {
const date = new Date(today)
date.setDate(date.getDate() + i)
const dayNotes = notes.slice(i * notesPerDay, (i + 1) * notesPerDay)
if (dayNotes.length === 0 && i > 0) break
days.push({
date: date.toISOString().slice(0, 10),
noteIds: dayNotes.map(n => n.id),
noteTitles: dayNotes.map(n => n.title),
activity: i === 0 ? 'Première lecture' : `Revoir ${dayNotes.length} notes`,
})
}
return { days, totalDays: days.length, notesPerDay }
}
}
}
export const studyPlannerService = new StudyPlannerService()