/** * Jeu de données de démonstration : la classe Terminale S2 avec deux semaines * d'évaluations. Idempotent — relancer `npm run seed` ne duplique rien. * Usage : npm run seed [-- --reset] */ import { existsSync, rmSync } from 'node:fs' import { resolve } from 'node:path' import { db, nowIso } from './db.js' import { newToken } from './auth.js' import type { WeatherKind } from '@meteo-exam/shared' const DB_PATH = process.env['METEO_DB'] ?? resolve(process.cwd(), 'data/meteo-exam.db') if (process.argv.includes('--reset')) { for (const suffix of ['', '-wal', '-shm']) { const file = `${DB_PATH}${suffix}` if (existsSync(file)) rmSync(file) } console.log('Base effacée. Relancez `npm run seed`.') process.exit(0) } const existing = db.prepare<[], { n: number }>('SELECT COUNT(*) AS n FROM classes').get()! if (existing.n > 0) { console.log('La base contient déjà des classes — seed ignoré. Utilisez --reset pour repartir de zéro.') process.exit(0) } const CLASS_NAME = 'Terminale S2' const LEVEL = 'Lycée' const ADMIN_NICK = 'Thomas S.' const CLASS_CODE = 'TERMS2' const DAY_MS = 86_400_000 const isWeekend = (d: Date): boolean => d.getDay() === 0 || d.getDay() === 6 const midnight = new Date() midnight.setHours(0, 0, 0, 0) /** * `dayOffset` compte les jours **scolaires** : 0 = aujourd'hui (même si c'est * samedi, le seed reste lisible), puis on saute les weekends. */ function schoolDay(offset: number): Date { const date = new Date(midnight) let counted = 0 for (let step = 0; step < 90; step++) { if (counted === offset) break date.setTime(date.getTime() + DAY_MS) if (!isWeekend(date)) counted++ } return date } interface SeedExam { dayOffset: number hour: number subject: string teacher: string | null room: string | null chapter: string | null durationMin: number weight: number kind: WeatherKind note: string | null } const exams: SeedExam[] = [ { dayOffset: 0, hour: 8, subject: 'Mathématiques', teacher: 'M. Roussel', room: 'B204', chapter: 'Intégrales, primitives et dérivation', durationMin: 120, weight: 4, kind: 'tempete', note: 'Calculatrice autorisée, calculatrice graphique non.' }, { dayOffset: 0, hour: 14, subject: 'Histoire-Géographie', teacher: 'Mme Ferrand', room: 'A112', chapter: null, durationMin: 55, weight: 1, kind: 'vent', note: 'Interro surprise probable en début de cours.' }, { dayOffset: 1, hour: 9, subject: 'Physique-Chimie', teacher: 'Mme Nguyen', room: 'Labo 3', chapter: 'Mécanique : second principe', durationMin: 60, weight: 2, kind: 'orage', note: 'Paquet de calculatrices fourni.' }, { dayOffset: 1, hour: 15, subject: 'Anglais', teacher: 'M. Whitfield', room: 'C007', chapter: 'Compréhension orale — Unit 4', durationMin: 30, weight: 1, kind: 'vent', note: 'Rumeur d’une dictée surprise.' }, { dayOffset: 2, hour: 8, subject: 'SVT', teacher: 'Mme Perrin', room: 'S215', chapter: 'Génétique des populations', durationMin: 90, weight: 3, kind: 'orage', note: 'TP suivi d’une synthèse écrite.' }, { dayOffset: 3, hour: 10, subject: 'Philosophie', teacher: 'M. Aubert', room: 'A008', chapter: 'La raison et le doute', durationMin: 120, weight: 3, kind: 'orage', note: 'Dissertation, sujet au choix.' }, { dayOffset: 3, hour: 16, subject: 'EPS', teacher: 'Mme Costa', room: 'Gymnase', chapter: null, durationMin: 60, weight: 1, kind: 'soleil', note: 'Basket obligatoire.' }, { dayOffset: 4, hour: 9, subject: 'Mathématiques', teacher: 'M. Roussel', room: 'B204', chapter: 'Probabilités conditionnelles', durationMin: 60, weight: 2, kind: 'orage', note: null }, { dayOffset: 5, hour: 10, subject: 'Français', teacher: 'Mme Dumas', room: 'A101', chapter: 'Guerre froide — contexte', durationMin: 60, weight: 1, kind: 'vent', note: 'Lecture suivie d’une étude de texte.' }, { dayOffset: 6, hour: 8, subject: 'Examen blanc', teacher: null, room: 'Amphi A', chapter: 'Toutes matières', durationMin: 180, weight: 5, kind: 'tempete', note: 'Blanc général, convocation à 8h précises.' }, { dayOffset: 7, hour: 9, subject: 'SVT', teacher: 'Mme Perrin', room: 'S215', chapter: 'Écosystèmes et bilan de matière', durationMin: 45, weight: 1, kind: 'brouillard', note: 'Brouillard : TP évalué, date encore floue.' }, { dayOffset: 8, hour: 14, subject: 'Anglais', teacher: 'M. Whitfield', room: 'C007', chapter: 'Expression écrite — sujet d’invention', durationMin: 60, weight: 2, kind: 'orage', note: null }, { dayOffset: 9, hour: 8, subject: 'Histoire-Géographie', teacher: 'Mme Ferrand', room: 'A112', chapter: 'La décolonisation', durationMin: 60, weight: 2, kind: 'orage', note: null }, { dayOffset: 10, hour: 10, subject: 'Philosophie', teacher: 'M. Aubert', room: 'A008', chapter: 'Le sujet et l’autre', durationMin: 60, weight: 2, kind: 'orage', note: 'Plan imposé.' }, ] const seed = db.transaction(() => { db.prepare('INSERT INTO classes (name, level, code, flash_message, created_at) VALUES (?, ?, ?, ?, ?)') .run(CLASS_NAME, LEVEL, CLASS_CODE, 'Bon courage pour les révisions cette semaine ! 🌤️', nowIso()) const classId = db.prepare<[string], { id: number }>('SELECT id FROM classes WHERE code = ?') .get(CLASS_CODE)!.id const adminToken = newToken() const adminId = db .prepare('INSERT INTO members (class_id, nickname, role, token, created_at) VALUES (?, ?, ?, ?, ?)') .run(classId, ADMIN_NICK, 'admin', adminToken, nowIso()).lastInsertRowid as number // Quelques élèves : ils donneront du volume aux votes du radar plus tard. for (const nick of ['Léa M.', 'Karim B.', 'Chloé D.', 'Inès R.', 'Lucas P.', 'Mehdi A.']) { db.prepare('INSERT INTO members (class_id, nickname, role, token, created_at) VALUES (?, ?, ?, ?, ?)') .run(classId, nick, 'student', newToken(), nowIso()) } const insert = db.prepare( `INSERT INTO exams (class_id, subject, teacher, room, chapter, starts_at, duration_min, weight, kind, note, created_by, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, ) for (const e of exams) { const date = schoolDay(e.dayOffset) date.setHours(e.hour, 0, 0, 0) const now = nowIso() insert.run( classId, e.subject, e.teacher, e.room, e.chapter, date.toISOString(), e.durationMin, e.weight, e.kind, e.note, adminId, now, now, ) } return { classId, adminToken } }) const { adminToken } = seed() console.log('Seed terminé.') console.log(` Classe : ${CLASS_NAME} (${LEVEL})`) console.log(` Code invite : ${CLASS_CODE}`) console.log(` Admin : ${ADMIN_NICK}`) console.log(` Évaluations : ${exams.length}`) console.log(` Jeton admin : ${adminToken}`)