Files
schulos/prisma/seed.ts
T
Mustafa Cetin 0c1f739989 Initial commit: ExaMetra v1.0.0 — Free & open-source competence & grading platform
Features:
- 15+ views: Dashboard, Classes, Progress, Flower, Matrix, Assessments, Grading, Reports, Attendance, Calendar, Lesson Plans, Analytics, Parent Communication, Behavior Tracking, Settings
- 34+ API endpoints
- 30+ Prisma models
- 700+ i18n keys (German + English)
- Keyboard shortcuts (Cmd+K, Cmd+1-9, Cmd+/)
- Batch deletion, drag-and-drop seating, custom color palette
- Demo accounts (admin, teacher, student, parent)
- Dark mode, responsive design, print styles
- CSV/PDF export, notification system, onboarding tour
- Zero emojis — all replaced with Lucide icons
- MIT License
2026-07-29 05:46:10 +00:00

1546 lines
59 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* CompetenceTrack — Prisma Seed Script
* Populates the database with realistic demo data for a Berlin elementary school.
*
* Usage: bun prisma/seed.ts
*/
import { PrismaClient } from '@prisma/client'
import bcrypt from 'bcryptjs'
const prisma = new PrismaClient()
// ─── Helper: random date within last N days ──────────────────────────────
function randomDateWithinLast(days: number): Date {
const now = new Date()
const past = new Date(now.getTime() - days * 24 * 60 * 60 * 1000)
const diff = now.getTime() - past.getTime()
return new Date(past.getTime() + Math.random() * diff)
}
// ─── Helper: random integer in range [min, max] ──────────────────────────
function randInt(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min
}
// ─── Helper: pick random element from array ──────────────────────────────
function pick<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)]
}
async function main() {
console.log('🌱 Seeding CompetenceTrack database...\n')
// Clean up existing data (order matters due to foreign keys)
console.log(' 🧹 Cleaning existing data...')
await prisma.auditLog.deleteMany()
await prisma.behaviorIncident.deleteMany()
await prisma.behaviorCategory.deleteMany()
await prisma.commentBankEntry.deleteMany()
await prisma.commentCategory.deleteMany()
await prisma.rubricLevel.deleteMany()
await prisma.rubricCriterion.deleteMany()
await prisma.rubric.deleteMany()
await prisma.parentMessage.deleteMany()
await prisma.parentContact.deleteMany()
await prisma.lessonPlan.deleteMany()
await prisma.teacherNote.deleteMany()
await prisma.attendanceRecord.deleteMany()
await prisma.attendanceSession.deleteMany()
await prisma.gradingWeightRule.deleteMany()
await prisma.gradingScheme.deleteMany()
await prisma.computedGrade.deleteMany()
await prisma.reportSection.deleteMany()
await prisma.report.deleteMany()
await prisma.dataExportRequest.deleteMany()
await prisma.assessmentResult.deleteMany()
await prisma.assessmentCompetencyLink.deleteMany()
await prisma.assessment.deleteMany()
await prisma.learningProgressEntry.deleteMany()
await prisma.masteryLevelDefinition.deleteMany()
await prisma.competency.deleteMany()
await prisma.competencyCategory.deleteMany()
await prisma.classCompetencyAssignment.deleteMany()
await prisma.competencyTemplate.deleteMany()
await prisma.enrollment.deleteMany()
await prisma.classGroupTeacher.deleteMany()
await prisma.classGroup.deleteMany()
await prisma.student.deleteMany()
await prisma.subject.deleteMany()
await prisma.schoolYear.deleteMany()
await prisma.user.deleteMany()
await prisma.school.deleteMany()
console.log(' ✅ Cleaned up.\n')
// ─── 1. Demo School ───────────────────────────────────────────────────
console.log(' 🏫 Creating school...')
const school = await prisma.school.create({
data: {
name: 'Grundschule Am Park',
schoolType: 'ELEMENTARY',
country: 'DE',
timezone: 'Europe/Berlin',
},
})
// ─── 2. Demo School Year ──────────────────────────────────────────────
console.log(' 📅 Creating school year...')
const schoolYear = await prisma.schoolYear.create({
data: {
schoolId: school.id,
label: '2025/2026',
startDate: new Date('2025-08-01'),
endDate: new Date('2026-07-31'),
},
})
// ─── 3. Demo Users ──────────────────────────────────────────────────
console.log(' 👩‍🏫 Creating demo users...')
const passwordHash = await bcrypt.hash('Demo2025!', 10)
const teacher = await prisma.user.create({
data: {
schoolId: school.id,
email: 'demo@competencetrack.org',
passwordHash,
firstName: 'Anna',
lastName: 'Müller',
role: 'SCHOOL_ADMIN',
locale: 'de',
isDemo: true,
},
})
// Demo Teacher (regular teacher role)
const demoTeacher = await prisma.user.create({
data: {
schoolId: school.id,
email: 'demo.teacher@competencetrack.org',
passwordHash,
firstName: 'Max',
lastName: 'Lehrer',
role: 'TEACHER',
locale: 'de',
isDemo: true,
},
})
// Demo Student (student user account)
const demoStudent = await prisma.user.create({
data: {
schoolId: school.id,
email: 'demo.student@competencetrack.org',
passwordHash,
firstName: 'Lena',
lastName: 'Schüler',
role: 'STUDENT',
locale: 'de',
isDemo: true,
},
})
// Demo Parent (parent user account)
const demoParent = await prisma.user.create({
data: {
schoolId: school.id,
email: 'demo.parent@competencetrack.org',
passwordHash,
firstName: 'Thomas',
lastName: 'Elter',
role: 'PARENT',
locale: 'de',
isDemo: true,
},
})
// ─── 4. Demo Classes ──────────────────────────────────────────────────
console.log(' 🏫 Creating classes...')
const class3a = await prisma.classGroup.create({
data: {
schoolId: school.id,
schoolYearId: schoolYear.id,
name: '3a',
gradeLevel: 3,
schoolType: 'ELEMENTARY',
},
})
const class3b = await prisma.classGroup.create({
data: {
schoolId: school.id,
schoolYearId: schoolYear.id,
name: '3b',
gradeLevel: 3,
schoolType: 'ELEMENTARY',
},
})
// Assign teacher roles
await prisma.classGroupTeacher.create({
data: {
classGroupId: class3a.id,
userId: teacher.id,
role: 'HOMEROOM_TEACHER',
},
})
await prisma.classGroupTeacher.create({
data: {
classGroupId: class3b.id,
userId: teacher.id,
role: 'SUBJECT_TEACHER',
},
})
// Assign demo teacher to class 3b as subject teacher
await prisma.classGroupTeacher.create({
data: {
classGroupId: class3b.id,
userId: demoTeacher.id,
role: 'HOMEROOM_TEACHER',
},
})
// ─── 5. Demo Students ─────────────────────────────────────────────────
console.log(' 👧🧒 Creating students...')
// 3a students (8 students)
const students3aNames = [
{ firstName: 'Lena', lastName: 'Schmidt', dob: '2016-03-12' },
{ firstName: 'Maximilian', lastName: 'Weber', dob: '2016-07-22' },
{ firstName: 'Sophie', lastName: 'Schneider', dob: '2016-01-08' },
{ firstName: 'Felix', lastName: 'Fischer', dob: '2016-09-15' },
{ firstName: 'Emma', lastName: 'Meyer', dob: '2016-05-30' },
{ firstName: 'Lukas', lastName: 'Wagner', dob: '2016-11-04' },
{ firstName: 'Mia', lastName: 'Becker', dob: '2016-02-18' },
{ firstName: 'Jonas', lastName: 'Hoffmann', dob: '2016-08-27' },
]
// 3b students (7 students)
const students3bNames = [
{ firstName: 'Hannah', lastName: 'Koch', dob: '2016-04-09' },
{ firstName: 'Leon', lastName: 'Richter', dob: '2016-12-01' },
{ firstName: 'Clara', lastName: 'Klein', dob: '2016-06-14' },
{ firstName: 'Tim', lastName: 'Schröder', dob: '2016-10-20' },
{ firstName: 'Anna-Lena', lastName: 'Braun', dob: '2016-03-25' },
{ firstName: 'David', lastName: 'Zimmermann', dob: '2016-07-03' },
{ firstName: 'Laura', lastName: 'Krüger', dob: '2016-01-30' },
]
const students3a: { id: string; firstName: string; lastName: string }[] = []
const students3b: { id: string; firstName: string; lastName: string }[] = []
for (const s of students3aNames) {
const student = await prisma.student.create({
data: {
schoolId: school.id,
firstName: s.firstName,
lastName: s.lastName,
dateOfBirth: new Date(s.dob),
},
})
students3a.push(student)
// Enroll in class 3a
await prisma.enrollment.create({
data: {
studentId: student.id,
classGroupId: class3a.id,
schoolYearId: schoolYear.id,
startDate: new Date('2025-08-01'),
},
})
}
for (const s of students3bNames) {
const student = await prisma.student.create({
data: {
schoolId: school.id,
firstName: s.firstName,
lastName: s.lastName,
dateOfBirth: new Date(s.dob),
},
})
students3b.push(student)
// Enroll in class 3b
await prisma.enrollment.create({
data: {
studentId: student.id,
classGroupId: class3b.id,
schoolYearId: schoolYear.id,
startDate: new Date('2025-08-01'),
},
})
}
console.log(` Created ${students3a.length} students in 3a, ${students3b.length} students in 3b`)
// ─── 6. Subjects ──────────────────────────────────────────────────────
console.log(' 📚 Creating subjects...')
const mathSubject = await prisma.subject.create({
data: {
schoolId: school.id,
name: 'Mathematik',
gradeLevelMin: 1,
gradeLevelMax: 4,
},
})
const germanSubject = await prisma.subject.create({
data: {
schoolId: school.id,
name: 'Deutsch',
gradeLevelMin: 1,
gradeLevelMax: 4,
},
})
// ─── 7. Competency Templates ──────────────────────────────────────────
console.log(' 🎯 Creating competency templates...')
// ─── Math Template ─────────────────────────────────────────────────────
const mathTemplate = await prisma.competencyTemplate.create({
data: {
name: 'Mathematik Klasse 3',
description: 'Kompetenzraster für Mathematik in Klasse 3 gemäß Rahmenlehrplan Berlin-Brandenburg',
subjectId: mathSubject.id,
schoolType: 'ELEMENTARY',
gradeLevelMin: 3,
gradeLevelMax: 3,
isGlobalTemplate: false,
createdByUserId: teacher.id,
version: 1,
schoolId: school.id,
},
})
// Math categories
const mathCatZahlen = await prisma.competencyCategory.create({
data: {
competencyTemplateId: mathTemplate.id,
name: 'Zahlen und Operationen',
order: 0,
color: '#10b981',
},
})
const mathCatGeometrie = await prisma.competencyCategory.create({
data: {
competencyTemplateId: mathTemplate.id,
name: 'Geometrie',
order: 1,
color: '#f59e0b',
},
})
const mathCatGroessen = await prisma.competencyCategory.create({
data: {
competencyTemplateId: mathTemplate.id,
name: 'Größen und Messen',
order: 2,
color: '#8b5cf6',
},
})
// Math competencies — Zahlen und Operationen
const mathCompZAH01 = await prisma.competency.create({
data: {
categoryId: mathCatZahlen.id,
code: 'M.3.ZAH.01',
title: 'Zahlenraum bis 1000 erfassen',
description: 'Die Schülerinnen und Schüler können den Zahlenraum bis 1000 darstellen, ordnen und vergleichen.',
order: 0,
},
})
const mathCompZAH02 = await prisma.competency.create({
data: {
categoryId: mathCatZahlen.id,
code: 'M.3.ZAH.02',
title: 'Schriftliche Addition und Subtraktion',
description: 'Die Schülerinnen und Schüler können schriftliche Addition und Subtraktion im Zahlenraum bis 1000 durchführen.',
order: 1,
},
})
const mathCompZAH03 = await prisma.competency.create({
data: {
categoryId: mathCatZahlen.id,
code: 'M.3.ZAH.03',
title: 'Multiplikation und Division',
description: 'Die Schülerinnen und Schüler beherrschen das kleine Einmaleins und können einfache Multiplikations- und Divisionsaufgaben lösen.',
order: 2,
},
})
// Math competencies — Geometrie
const mathCompGEO01 = await prisma.competency.create({
data: {
categoryId: mathCatGeometrie.id,
code: 'M.3.GEO.01',
title: 'Ebenen Figuren erkennen und benennen',
description: 'Die Schülerinnen und Schüler können ebene Figuren wie Dreieck, Viereck, Kreis erkennen und benennen.',
order: 0,
},
})
const mathCompGEO02 = await prisma.competency.create({
data: {
categoryId: mathCatGeometrie.id,
code: 'M.3.GEO.02',
title: 'Umfang und Flächeninhalt vergleichen',
description: 'Die Schülerinnen und Schüler können den Umfang und den Flächeninhalt einfacher Figuren vergleichen und messen.',
order: 1,
},
})
// Math competencies — Größen und Messen
const mathCompGRO01 = await prisma.competency.create({
data: {
categoryId: mathCatGroessen.id,
code: 'M.3.GRO.01',
title: 'Längen messen und umrechnen',
description: 'Die Schülerinnen und Schüler können Längen mit appropriateen Maßeinheiten messen und zwischen m, dm, cm umrechnen.',
order: 0,
},
})
const mathCompGRO02 = await prisma.competency.create({
data: {
categoryId: mathCatGroessen.id,
code: 'M.3.GRO.02',
title: 'Geldbeträge berechnen',
description: 'Die Schülerinnen und Schüler können mit Geldbeträgen in Euro und Cent rechnen und diese vergleichen.',
order: 1,
},
})
// Mastery level definitions for all Math competencies
const masteryLevels = [
{ levelValue: 1, label: 'noch nicht erreicht' },
{ levelValue: 2, label: 'entwickelt sich' },
{ levelValue: 3, label: 'kompetent' },
{ levelValue: 4, label: 'weit entwickelt' },
]
const mathCompetencies = [mathCompZAH01, mathCompZAH02, mathCompZAH03, mathCompGEO01, mathCompGEO02, mathCompGRO01, mathCompGRO02]
for (const comp of mathCompetencies) {
for (const ml of masteryLevels) {
await prisma.masteryLevelDefinition.create({
data: {
competencyId: comp.id,
levelValue: ml.levelValue,
label: ml.label,
description: ml.levelValue === 1
? 'Die Kompetenz ist noch nicht erreicht.'
: ml.levelValue === 2
? 'Die Kompetenz entwickelt sich, erste Ansätze sind erkennbar.'
: ml.levelValue === 3
? 'Die Kompetenz wird sicher und selbstständig gezeigt.'
: 'Die Kompetenz wird weit über das erwartete Niveau hinaus gezeigt.',
},
})
}
}
// ─── Deutsch Template ──────────────────────────────────────────────────
const germanTemplate = await prisma.competencyTemplate.create({
data: {
name: 'Deutsch Klasse 3',
description: 'Kompetenzraster für Deutsch in Klasse 3 gemäß Rahmenlehrplan Berlin-Brandenburg',
subjectId: germanSubject.id,
schoolType: 'ELEMENTARY',
gradeLevelMin: 3,
gradeLevelMax: 3,
isGlobalTemplate: false,
createdByUserId: teacher.id,
version: 1,
schoolId: school.id,
},
})
// German categories
const germanCatLesen = await prisma.competencyCategory.create({
data: {
competencyTemplateId: germanTemplate.id,
name: 'Lesen',
order: 0,
color: '#3b82f6',
},
})
const germanCatSchreiben = await prisma.competencyCategory.create({
data: {
competencyTemplateId: germanTemplate.id,
name: 'Schreiben',
order: 1,
color: '#ef4444',
},
})
const germanCatSprechen = await prisma.competencyCategory.create({
data: {
competencyTemplateId: germanTemplate.id,
name: 'Sprechen und Zuhören',
order: 2,
color: '#06b6d4',
},
})
// German competencies — Lesen
const germanCompLES01 = await prisma.competency.create({
data: {
categoryId: germanCatLesen.id,
code: 'D.3.LES.01',
title: 'Altergerechte Texte sinnentnehmend lesen',
description: 'Die Schülerinnen und Schüler können altersgerechte Texte flüssig lesen und den Inhalt verstehen.',
order: 0,
},
})
const germanCompLES02 = await prisma.competency.create({
data: {
categoryId: germanCatLesen.id,
code: 'D.3.LES.02',
title: 'Texte zusammenfassen und interpretieren',
description: 'Die Schülerinnen und Schüler können gelesene Texte in eigenen Worten zusammenfassen und einfache Schlussfolgerungen ziehen.',
order: 1,
},
})
// German competencies — Schreiben
const germanCompSCH01 = await prisma.competency.create({
data: {
categoryId: germanCatSchreiben.id,
code: 'D.3.SCH.01',
title: 'Texte verfassen (Erzählung, Beschreibung)',
description: 'Die Schülerinnen und Schüler können kurze Erzählungen und Beschreibungen verfassen.',
order: 0,
},
})
const germanCompSCH02 = await prisma.competency.create({
data: {
categoryId: germanCatSchreiben.id,
code: 'D.3.SCH.02',
title: 'Rechtschreibung anwenden',
description: 'Die Schülerinnen und Schüler können grundlegende Rechtschreibregeln anwenden.',
order: 1,
},
})
const germanCompSCH03 = await prisma.competency.create({
data: {
categoryId: germanCatSchreiben.id,
code: 'D.3.SCH.03',
title: 'Sätze und Texte gliedern',
description: 'Die Schülerinnen und Schüler können Sätze sinnvoll gliedern und Texte strukturieren.',
order: 2,
},
})
// German competencies — Sprechen und Zuhören
const germanCompSPR01 = await prisma.competency.create({
data: {
categoryId: germanCatSprechen.id,
code: 'D.3.SPR.01',
title: 'Zuhören und verstehen',
description: 'Die Schülerinnen und Schüler können aufmerksam zuhören und Gehörtes wiedergeben.',
order: 0,
},
})
const germanCompSPR02 = await prisma.competency.create({
data: {
categoryId: germanCatSprechen.id,
code: 'D.3.SPR.02',
title: 'Sachgerecht sprechen und erzählen',
description: 'Die Schülerinnen und Schüler können sich sachgerecht ausdrücken und Erlebnisse zusammenhängend erzählen.',
order: 1,
},
})
// Mastery level definitions for all German competencies
const germanCompetencies = [germanCompLES01, germanCompLES02, germanCompSCH01, germanCompSCH02, germanCompSCH03, germanCompSPR01, germanCompSPR02]
for (const comp of germanCompetencies) {
for (const ml of masteryLevels) {
await prisma.masteryLevelDefinition.create({
data: {
competencyId: comp.id,
levelValue: ml.levelValue,
label: ml.label,
description: ml.levelValue === 1
? 'Die Kompetenz ist noch nicht erreicht.'
: ml.levelValue === 2
? 'Die Kompetenz entwickelt sich, erste Ansätze sind erkennbar.'
: ml.levelValue === 3
? 'Die Kompetenz wird sicher und selbstständig gezeigt.'
: 'Die Kompetenz wird weit über das erwartete Niveau hinaus gezeigt.',
},
})
}
}
// ─── 8. ClassCompetencyAssignment ──────────────────────────────────────
console.log(' 🔗 Assigning competency templates to class 3a...')
const mathAssignment = await prisma.classCompetencyAssignment.create({
data: {
classGroupId: class3a.id,
subjectId: mathSubject.id,
competencyTemplateId: mathTemplate.id,
schoolYearId: schoolYear.id,
},
})
const germanAssignment = await prisma.classCompetencyAssignment.create({
data: {
classGroupId: class3a.id,
subjectId: germanSubject.id,
competencyTemplateId: germanTemplate.id,
schoolYearId: schoolYear.id,
},
})
// ─── 9. Learning Progress Entries ──────────────────────────────────────
console.log(' 📊 Creating learning progress entries...')
const allCompetencies = [...mathCompetencies, ...germanCompetencies]
const progressNotes = [
'Zeigt gute Fortschritte im Unterricht.',
'Braucht noch zusätzliche Übung.',
'Hat das Thema sicher verstanden.',
'Kann das Gelernte selbstständig anwenden.',
'Hat in der Gruppenarbeit gut mitgemacht.',
'Benötigt noch Unterstützung bei komplexeren Aufgaben.',
'Hervorragende Mitarbeit heute.',
'Hat Schwierigkeiten mit dem Thema, braucht Förderung.',
null,
null,
]
let progressCount = 0
for (let i = 0; i < 30; i++) {
const student = pick(students3a)
const competency = pick(allCompetencies)
const date = randomDateWithinLast(60)
// Weighted mastery: most students are 2-3, fewer at 1 or 4
const mastery = pick([1, 2, 2, 2, 3, 3, 3, 3, 4])
await prisma.learningProgressEntry.create({
data: {
studentId: student.id,
competencyId: competency.id,
teacherId: teacher.id,
classGroupId: class3a.id,
date,
masteryLevelValue: mastery,
note: pick(progressNotes),
},
})
progressCount++
}
console.log(` Created ${progressCount} learning progress entries`)
// ─── 10. Assessment ────────────────────────────────────────────────────
console.log(' 📝 Creating assessment...')
const assessment = await prisma.assessment.create({
data: {
classGroupId: class3a.id,
subjectId: mathSubject.id,
teacherId: teacher.id,
title: 'Mathematik Test 1',
date: new Date('2025-10-15'),
type: 'TEST',
maxScore: 20,
weight: 2.0,
},
})
// Link assessment to Math competencies (ZAH ones primarily)
const mathAssessmentCompetencies = [mathCompZAH01, mathCompZAH02, mathCompZAH03]
for (const comp of mathAssessmentCompetencies) {
await prisma.assessmentCompetencyLink.create({
data: {
assessmentId: assessment.id,
competencyId: comp.id,
weight: 1.0,
},
})
}
// Create assessment results for all students in 3a
for (const student of students3a) {
// Realistic score distribution: mean around 14/20, some variation
const score = Math.min(20, Math.max(4, Math.round(randInt(8, 19) + randInt(-3, 3))))
const clampedScore = Math.min(20, Math.max(0, score))
// Map score to mastery level (rough mapping)
const masteryFromScore = clampedScore >= 17 ? 4 : clampedScore >= 13 ? 3 : clampedScore >= 8 ? 2 : 1
await prisma.assessmentResult.create({
data: {
assessmentId: assessment.id,
studentId: student.id,
score: clampedScore,
masteryLevelValue: masteryFromScore,
},
})
}
console.log(` Created assessment with ${students3a.length} results`)
// ─── 11. Grading Scheme ────────────────────────────────────────────────
console.log(' 📋 Creating grading scheme...')
const gradingScheme = await prisma.gradingScheme.create({
data: {
classGroupId: class3a.id,
subjectId: mathSubject.id,
schoolId: school.id,
name: 'Noten 1-6 Mathematik 3a',
type: 'NUMERIC_GRADE',
scaleDefinition: JSON.stringify({
min: 1,
max: 6,
labels: {
'1': 'sehr gut',
'2': 'gut',
'3': 'befriedigend',
'4': 'ausreichend',
'5': 'mangelhaft',
'6': 'ungenügend',
},
}),
},
})
// Grading weight rules
await prisma.gradingWeightRule.create({
data: {
gradingSchemeId: gradingScheme.id,
sourceType: 'LEARNING_PROGRESS',
weightPercent: 50.0,
},
})
await prisma.gradingWeightRule.create({
data: {
gradingSchemeId: gradingScheme.id,
sourceType: 'ASSESSMENT',
weightPercent: 50.0,
},
})
// ─── 12. Audit Log Entries ─────────────────────────────────────────────
console.log(' 📝 Creating audit log entries...')
const auditEntries = [
{
action: 'CREATE',
entityType: 'CompetencyTemplate',
entityId: mathTemplate.id,
metadata: JSON.stringify({ templateName: 'Mathematik Klasse 3' }),
},
{
action: 'CREATE',
entityType: 'CompetencyTemplate',
entityId: germanTemplate.id,
metadata: JSON.stringify({ templateName: 'Deutsch Klasse 3' }),
},
{
action: 'CREATE',
entityType: 'Assessment',
entityId: assessment.id,
metadata: JSON.stringify({ title: 'Mathematik Test 1', class: '3a' }),
},
{
action: 'ASSIGN',
entityType: 'ClassCompetencyAssignment',
entityId: mathAssignment.id,
metadata: JSON.stringify({ class: '3a', subject: 'Mathematik' }),
},
{
action: 'ASSIGN',
entityType: 'ClassCompetencyAssignment',
entityId: germanAssignment.id,
metadata: JSON.stringify({ class: '3a', subject: 'Deutsch' }),
},
]
for (const entry of auditEntries) {
await prisma.auditLog.create({
data: {
userId: teacher.id,
schoolId: school.id,
action: entry.action,
entityType: entry.entityType,
entityId: entry.entityId,
timestamp: randomDateWithinLast(30),
metadata: entry.metadata,
},
})
}
// ─── 13. Lesson Plans ──────────────────────────────────────────────────
console.log(' 📅 Creating lesson plans...')
// Helper: get a date in the current week (Mon-Fri) at a given hour
function dateThisWeek(dayOffset: number, hour: number, minute = 0): Date {
const now = new Date()
const day = now.getDay() // 0 = Sunday, 1 = Monday
const diffToMonday = day === 0 ? -6 : 1 - day
const monday = new Date(now)
monday.setDate(now.getDate() + diffToMonday)
monday.setHours(hour, minute, 0, 0)
const result = new Date(monday)
result.setDate(monday.getDate() + dayOffset)
return result
}
function dateLastWeek(dayOffset: number, hour: number, minute = 0): Date {
const d = dateThisWeek(dayOffset, hour, minute)
d.setDate(d.getDate() - 7)
return d
}
// Lesson 1: draft, scheduled for later this week
await prisma.lessonPlan.create({
data: {
teacherId: teacher.id,
classGroupId: class3a.id,
subjectId: mathSubject.id,
title: 'Schriftliche Addition bis 1000',
description: 'Einführung in die schriftliche Addition im Zahlenraum bis 1000 mit Übertrag.',
date: dateThisWeek(2, 9, 0), // Wednesday this week, 09:00
durationMin: 45,
status: 'draft',
objectives: JSON.stringify([
'SuS können die schriftliche Addition sachgerecht durchführen.',
'SuS beherrschen den Übertrag korrekt.',
'SuS können eigene Aufgaben lösen und vergleichen.',
]),
materials: JSON.stringify([
'Schulbuch S. 42–43',
'Arbeitsblatt Addition',
'Plättchen als Anschauungsmaterial',
]),
homework: 'Arbeitsblatt Aufgabe 1–3',
reflection: null,
linkedCompetencyIds: JSON.stringify([mathCompZAH02.id, mathCompZAH01.id]),
},
})
// Lesson 2: scheduled, this week Tuesday
await prisma.lessonPlan.create({
data: {
teacherId: teacher.id,
classGroupId: class3a.id,
subjectId: germanSubject.id,
title: 'Leseverstehen: Märchen untersuchen',
description: 'Gemeinsames Lesen eines Märchens, Erarbeitung von Merkmalen.',
date: dateThisWeek(1, 10, 30), // Tuesday this week, 10:30
durationMin: 45,
status: 'scheduled',
objectives: JSON.stringify([
'SuS können Merkmale eines Märchens nennen.',
'SuS können die Handlung in eigenen Worten wiedergeben.',
]),
materials: JSON.stringify([
'Märchen "Rotkäppchen" (Kopiervorlage)',
'Lesezeichen mit Märchenmerkmalen',
'Interaktives Tafelbild',
]),
homework: 'Märchen zu Ende lesen und eine Frage notieren.',
reflection: null,
linkedCompetencyIds: JSON.stringify([germanCompLES01.id, germanCompLES02.id]),
},
})
// Lesson 3: scheduled, this week Thursday
await prisma.lessonPlan.create({
data: {
teacherId: teacher.id,
classGroupId: class3a.id,
subjectId: mathSubject.id,
title: 'Geometrische Figuren — Vierecke',
description: 'Erkennen und Benennen von Vierecken (Quadrat, Rechteck, Raute, Parallelogramm).',
date: dateThisWeek(3, 9, 0), // Thursday this week, 09:00
durationMin: 45,
status: 'scheduled',
objectives: JSON.stringify([
'SuS können verschiedene Vierecke benennen.',
'SuS können Eigenschaften der Vierecke beschreiben.',
]),
materials: JSON.stringify([
'Geometrische Formen aus Pappe',
'Arbeitsblatt "Vierecke sortieren"',
'Geodreieck',
]),
homework: 'Arbeitsblatt "Finde Vierecke in der Klasse"',
reflection: null,
linkedCompetencyIds: JSON.stringify([mathCompGEO01.id]),
},
})
// Lesson 4: completed last week
await prisma.lessonPlan.create({
data: {
teacherId: teacher.id,
classGroupId: class3a.id,
subjectId: mathSubject.id,
title: 'Multiplikation — Einmaleins wiederholen',
description: 'Wiederholung des kleinen Einmaleins mit Spielen und Übungen.',
date: dateLastWeek(1, 9, 0), // Last week Tuesday, 09:00
durationMin: 45,
status: 'completed',
objectives: JSON.stringify([
'SuS können das kleine Einmaleins sicher anwenden.',
'SuS können Aufgaben in Partnerarbeit lösen.',
]),
materials: JSON.stringify([
'Einmaleins-Plakat',
'Karten für "Male-Mal-Mir-Nach"',
]),
homework: 'Einmaleins-Trainingsheft S. 8',
reflection: 'Die SuS waren sehr motiviert. Besonders die Partnerarbeit hat gut funktioniert. Einige SuS benötigen noch Übung mit dem 7er und 8er Einmaleins.',
linkedCompetencyIds: JSON.stringify([mathCompZAH03.id]),
},
})
console.log(' Created 4 lesson plans (1 draft, 2 scheduled, 1 completed)')
// ─── 14. Parent Communication ──────────────────────────────────────
console.log(' 👨‍👩‍👧 Creating parent contacts & messages...')
// Clean previous parent data (so re-seeding works)
await prisma.parentMessage.deleteMany()
await prisma.parentContact.deleteMany()
// Pick a few students to create parent contacts for
const parentSeedStudents = [
students3a[0], // Lena Schmidt
students3a[1], // Maximilian Weber
students3a[3], // Felix Fischer
students3a[5], // Lukas Wagner
students3b[0], // Hannah Koch
]
const parentContacts: { id: string; firstName: string; lastName: string; studentId: string; studentFirstName: string; studentLastName: string }[] = []
const contactDefs = [
{ firstName: 'Maria', lastName: 'Schmidt', email: 'maria.schmidt@example.org', phone: '+49 30 12345601', relationship: 'parent', preferredContact: 'email', preferredLanguage: 'de', notes: 'Erreichbar ab 16 Uhr.' },
{ firstName: 'Thomas', lastName: 'Weber', email: 't.weber@example.org', phone: '+49 30 12345602', relationship: 'parent', preferredContact: 'phone', preferredLanguage: 'de', notes: null },
{ firstName: 'Petra', lastName: 'Fischer', email: 'petra.fischer@example.org', phone: '+49 30 12345603', relationship: 'guardian', preferredContact: 'both', preferredLanguage: 'de', notes: 'Großmutter, Haupterziehungsberechtigte.' },
{ firstName: 'Andreas', lastName: 'Wagner', email: 'a.wagner@example.org', phone: '+49 30 12345604', relationship: 'parent', preferredContact: 'email', preferredLanguage: 'en', notes: 'Speaks English at home.' },
{ firstName: 'Julia', lastName: 'Koch', email: 'julia.koch@example.org', phone: '+49 30 12345605', relationship: 'parent', preferredContact: 'email', preferredLanguage: 'de', notes: null },
]
for (let i = 0; i < parentSeedStudents.length; i++) {
const stu = parentSeedStudents[i]
const def = contactDefs[i]
const created = await prisma.parentContact.create({
data: {
studentId: stu.id,
firstName: def.firstName,
lastName: def.lastName,
email: def.email,
phone: def.phone,
relationship: def.relationship,
preferredContact: def.preferredContact,
preferredLanguage: def.preferredLanguage,
notes: def.notes,
},
})
parentContacts.push({
id: created.id,
firstName: created.firstName,
lastName: created.lastName,
studentId: stu.id,
studentFirstName: stu.firstName,
studentLastName: stu.lastName,
})
}
// Create 8 messages across categories, priorities, statuses
const now = new Date()
function daysAgo(days: number, hour = 10, minute = 0): Date {
const d = new Date(now)
d.setDate(d.getDate() - days)
d.setHours(hour, minute, 0, 0)
return d
}
type MsgDef = {
parentIdx: number
subject: string
body: string
category: string
priority: string
status: string
createdAt: Date
readAt?: Date
reply?: string
replyAt?: Date
}
const msgDefs: MsgDef[] = [
{
parentIdx: 0,
subject: 'Positive Rückmeldung zu Lenas Lernfortschritt',
body: 'Sehr geehrte Frau Schmidt,\n\nich möchte Ihnen eine positive Rückmeldung zu Lenas Lernfortschritt im Fach Mathematik geben. In den letzten Wochen hat sie sich besonders im Bereich der schriftlichen Addition deutlich verbessert. Sie arbeitet konzentriert mit und hilft gerne anderen Kindern.\n\nMit freundlichen Grüßen\nAnna Müller',
category: 'progress',
priority: 'normal',
status: 'read',
createdAt: daysAgo(12, 9, 30),
readAt: daysAgo(11, 18, 0),
},
{
parentIdx: 0,
subject: 'Elternsprechtag — Terminvereinbarung',
body: 'Sehr geehrte Frau Schmidt,\n\nam kommenden Freitag findet der Elternsprechtag statt. Ich würde Sie gerne zu einem Gespräch über Lenas Entwicklung einladen. Bitte melden Sie sich mit einem Wunschtermin.\n\nMit freundlichen Grüßen\nAnna Müller',
category: 'event',
priority: 'high',
status: 'replied',
createdAt: daysAgo(8, 14, 0),
readAt: daysAgo(8, 19, 30),
reply: 'Sehr geehrte Frau Müller,\n\nvielen Dank für die Nachricht. Mir würde Freitag um 15:30 Uhr passen.\n\nHerzliche Grüße\nMaria Schmidt',
replyAt: daysAgo(7, 8, 15),
},
{
parentIdx: 1,
subject: 'Überprüfung im Fach Deutsch — Erinnerung',
body: 'Sehr geehrter Herr Weber,\n\nnächste Woche schreiben wir eine kurze Leseverständnis-Überprüfung. Bitte unterstützen Sie Maximilian bei der Vorbereitung, indem Sie mit ihm die Märchen-Lesezeichen durchgehen.\n\nMit freundlichen Grüßen\nAnna Müller',
category: 'assessment',
priority: 'high',
status: 'delivered',
createdAt: daysAgo(4, 8, 0),
},
{
parentIdx: 2,
subject: 'Verhalten im Unterricht — Gesprächsbedarf',
body: 'Sehr geehrte Frau Fischer,\n\nin den letzten Tagen ist mir aufgefallen, dass Felix im Unterricht häufiger unaufmerksam war und Mitschüler ablenkte. Ich würde gerne zeitnah ein Gespräch mit Ihnen führen, um gemeinsam zu überlegen, wie wir Felix unterstützen können.\n\nMit freundlichen Grüßen\nAnna Müller',
category: 'behavior',
priority: 'urgent',
status: 'sent',
createdAt: daysAgo(2, 11, 45),
},
{
parentIdx: 2,
subject: 'Positives Feedback zum Arbeitsverhalten',
body: 'Sehr geehrte Frau Fischer,\n\nich möchte Ihnen mitteilen, dass Felix in dieser Woche sehr konzentriert gearbeitet hat und die Aufgaben eigenständig gelöst hat. Das ist ein großer Fortschritt!\n\nMit freundlichen Grüßen\nAnna Müller',
category: 'progress',
priority: 'normal',
status: 'sent',
createdAt: daysAgo(1, 9, 15),
},
{
parentIdx: 3,
subject: 'Anwesenheit — Entschuldigung',
body: 'Dear Mr. Wagner,\n\nI would like to inform you that Lukas was marked absent yesterday without prior notice. Please send a written excuse at your earliest convenience.\n\nBest regards\nAnna Müller',
category: 'attendance',
priority: 'normal',
status: 'delivered',
createdAt: daysAgo(3, 13, 0),
},
{
parentIdx: 4,
subject: 'Schulfest — Mitwirkung der Klasse 3b',
body: 'Sehr geehrte Frau Koch,\n\nam Ende des Schuljahres findet unser Schulfest statt. Die Klasse 3b wird einen kleinen Beitrag leisten. Ich würde mich freuen, wenn Hannah daran teilnimmt. Nähere Informationen folgen.\n\nMit freundlichen Grüßen\nAnna Müller',
category: 'event',
priority: 'low',
status: 'draft',
createdAt: daysAgo(0, 8, 30),
},
{
parentIdx: 0,
subject: 'Allgemeine Informationen zur Klassenfahrt',
body: 'Sehr geehrte Frau Schmidt,\n\nin Vorbereitung auf die Klassenfahrt im nächsten Monat sende ich Ihnen heute die Informationsmappe. Bitte geben Sie das ausgefüllte Anmeldeformular bis nächste Woche ab.\n\nMit freundlichen Grüßen\nAnna Müller',
category: 'general',
priority: 'normal',
status: 'replied',
createdAt: daysAgo(15, 16, 0),
readAt: daysAgo(14, 9, 0),
reply: 'Sehr geehrte Frau Müller,\n\ndas Formular liegt morgen früh in der Mappe bei. Vielen Dank für die Organisation!\n\nHerzliche Grüße\nMaria Schmidt',
replyAt: daysAgo(14, 17, 30),
},
]
let msgCount = 0
for (const def of msgDefs) {
const pc = parentContacts[def.parentIdx]
if (!pc) continue
await prisma.parentMessage.create({
data: {
parentId: pc.id,
teacherId: teacher.id,
studentId: pc.studentId,
subject: def.subject,
body: def.body,
category: def.category,
priority: def.priority,
status: def.status,
readAt: def.readAt ?? null,
reply: def.reply ?? null,
replyAt: def.replyAt ?? null,
createdAt: def.createdAt,
updatedAt: def.createdAt,
},
})
msgCount++
}
console.log(` Created ${parentContacts.length} parent contacts and ${msgCount} messages`)
// ─── 15. Behavior Tracking ─────────────────────────────────────────
console.log(' 🛡️ Creating behavior categories & incidents...')
// Clean previous behavior data (so re-seeding works)
await prisma.behaviorIncident.deleteMany()
await prisma.behaviorCategory.deleteMany()
const behaviorCategoryDefs = [
{ key: 'helpfulness', name: 'Hilfsbereitschaft', color: '#10b981', valence: 'positive', icon: '🤝' },
{ key: 'participation', name: 'Mitarbeit', color: '#14b8a6', valence: 'positive', icon: '✋' },
{ key: 'disruption', name: 'Störung', color: '#f43f5e', valence: 'negative', icon: '⚠️' },
{ key: 'conflict', name: 'Konflikt', color: '#f59e0b', valence: 'negative', icon: '😡' },
{ key: 'tardiness', name: 'Verspätung', color: '#64748b', valence: 'neutral', icon: '⏰' },
{ key: 'praise', name: 'Lob', color: '#8b5cf6', valence: 'positive', icon: '⭐' },
]
const behaviorCategoryIds: Record<string, string> = {}
for (const def of behaviorCategoryDefs) {
const created = await prisma.behaviorCategory.create({
data: {
schoolId: school.id,
name: def.name,
color: def.color,
valence: def.valence,
icon: def.icon,
},
})
behaviorCategoryIds[def.key] = created.id
}
console.log(` Created ${behaviorCategoryDefs.length} behavior categories`)
// Pick 4 target students across the two classes
const behaviorTargetStudents = [
students3a[0], // Lena Schmidt
students3a[1], // Maximilian Weber
students3a[3], // Felix Fischer
students3b[0], // Hannah Koch
]
// Behavior incident definitions — mix of categories, severities, locations, follow-ups, resolved status
type IncidentDef = {
studentIdx: number
categoryId: string
classGroupId: string
daysAgo: number
hour: number
severity: 'minor' | 'moderate' | 'major'
description: string
location: string
followUpAction: string | null
resolved: boolean
resolvedDaysAgo?: number
}
const incidentDefs: IncidentDef[] = [
{
studentIdx: 0,
categoryId: behaviorCategoryIds.praise,
classGroupId: class3a.id,
daysAgo: 2,
hour: 9,
severity: 'minor',
description: 'Lena hat heute einer Mitschülerin beim Aufräumen geholfen, ohne darum gebeten zu werden.',
location: 'classroom',
followUpAction: 'praise_circle',
resolved: true,
resolvedDaysAgo: 1,
},
{
studentIdx: 1,
categoryId: behaviorCategoryIds.disruption,
classGroupId: class3a.id,
daysAgo: 5,
hour: 10,
severity: 'moderate',
description: 'Maximilian hat den Unterricht mehrfach durch laute Bemerkungen unterbrochen und Mitschüler abgelenkt.',
location: 'classroom',
followUpAction: 'warning',
resolved: false,
},
{
studentIdx: 1,
categoryId: behaviorCategoryIds.tardiness,
classGroupId: class3a.id,
daysAgo: 7,
hour: 8,
severity: 'minor',
description: 'Maximilian kam 10 Minuten zu spät zum Unterricht.',
location: 'classroom',
followUpAction: null,
resolved: true,
resolvedDaysAgo: 6,
},
{
studentIdx: 2,
categoryId: behaviorCategoryIds.conflict,
classGroupId: class3a.id,
daysAgo: 10,
hour: 11,
severity: 'major',
description: 'Felix hat sich in der Pause mit einem Mitschüler gestritten und ihn geschubst. Ein Elterngespräch ist erforderlich.',
location: 'playground',
followUpAction: 'parent_talk',
resolved: false,
},
{
studentIdx: 2,
categoryId: behaviorCategoryIds.disruption,
classGroupId: class3a.id,
daysAgo: 12,
hour: 9,
severity: 'moderate',
description: 'Felix hat während der Stillarbeit mehrfach Bücher geklatscht und andere Kinder gestört.',
location: 'classroom',
followUpAction: 'warning',
resolved: true,
resolvedDaysAgo: 11,
},
{
studentIdx: 2,
categoryId: behaviorCategoryIds.helpfulness,
classGroupId: class3a.id,
daysAgo: 3,
hour: 10,
severity: 'minor',
description: 'Felix hat einer neuen Schülerin die Schulwege gezeigt und sie in die Klassengemeinschaft integriert.',
location: 'hallway',
followUpAction: 'praise_circle',
resolved: true,
resolvedDaysAgo: 2,
},
{
studentIdx: 0,
categoryId: behaviorCategoryIds.participation,
classGroupId: class3a.id,
daysAgo: 1,
hour: 9,
severity: 'minor',
description: 'Lena hat sich im Sachunterricht besonders aktiv gemeldet und ein Referat vorbereitet.',
location: 'classroom',
followUpAction: 'prize',
resolved: false,
},
{
studentIdx: 3,
categoryId: behaviorCategoryIds.praise,
classGroupId: class3b.id,
daysAgo: 4,
hour: 11,
severity: 'minor',
description: 'Hannah hat beim Lesen einer Mitschülerin geduldig geholfen und ihr Mut zugesprochen.',
location: 'library',
followUpAction: 'praise_circle',
resolved: true,
resolvedDaysAgo: 3,
},
{
studentIdx: 3,
categoryId: behaviorCategoryIds.tardiness,
classGroupId: class3b.id,
daysAgo: 8,
hour: 8,
severity: 'minor',
description: 'Hannah kam nach der großen Pause 5 Minuten zu spät zurück in den Unterricht.',
location: 'playground',
followUpAction: null,
resolved: true,
resolvedDaysAgo: 7,
},
{
studentIdx: 1,
categoryId: behaviorCategoryIds.participation,
classGroupId: class3a.id,
daysAgo: 14,
hour: 10,
severity: 'moderate',
description: 'Maximilian hat im Mathematikunterricht eine schwierige Aufgabe an der Tafel eigenständig gelöst und sein Vorgehen der Klasse erklärt.',
location: 'classroom',
followUpAction: 'prize',
resolved: true,
resolvedDaysAgo: 13,
},
]
let incidentCount = 0
for (const def of incidentDefs) {
const stu = behaviorTargetStudents[def.studentIdx]
if (!stu) continue
const date = new Date(now)
date.setDate(date.getDate() - def.daysAgo)
date.setHours(def.hour, 0, 0, 0)
const resolvedAt = def.resolved && def.resolvedDaysAgo !== undefined ? new Date(now.getTime() - def.resolvedDaysAgo * 24 * 60 * 60 * 1000) : null
await prisma.behaviorIncident.create({
data: {
studentId: stu.id,
teacherId: teacher.id,
classGroupId: def.classGroupId,
schoolId: school.id,
categoryId: def.categoryId,
date,
severity: def.severity,
description: def.description,
location: def.location,
followUpAction: def.followUpAction,
resolved: def.resolved,
resolvedAt,
resolvedById: def.resolved ? teacher.id : null,
},
})
incidentCount++
}
console.log(` Created ${incidentCount} behavior incidents`)
// ─── 16. Rubric Library ────────────────────────────────────────────────
console.log(' 📋 Creating rubrics...')
// Rubric 1: Aufsatzbewertung Deutsch (ANALYTIC)
const rubric1 = await prisma.rubric.create({
data: {
schoolId: school.id,
teacherId: teacher.id,
subjectId: germanSubject.id,
title: 'Aufsatzbewertung Deutsch',
description: 'Bewertungsraster für schriftliche Aufsätze in der Grundschule',
type: 'ANALYTIC',
maxPoints: 60,
isPublic: true,
version: 1,
criteria: {
create: [
{
name: 'Inhalt',
description: 'Inhaltliche Qualität und Gedankenführung',
weight: 2.0,
maxPoints: 20,
order: 0,
levels: {
create: [
{ label: 'Sehr gut', description: 'Der Text ist inhaltlich sehr reichhaltig, gut strukturiert und zeigt eigene Gedanken.', points: 20, order: 0 },
{ label: 'Gut', description: 'Der Text ist inhaltlich angemessen, gut nachvollziehbar und zeigt Ansätze eigener Gedanken.', points: 16, order: 1 },
{ label: 'Befriedigend', description: 'Der Text ist inhaltlich verständlich, aber eher oberflächlich und wenig eigenständig.', points: 11, order: 2 },
{ label: 'Ausreichend', description: 'Der Text ist inhaltlich dürftig, schwer nachvollziehbar und ohne eigene Gedanken.', points: 6, order: 3 },
],
},
},
{
name: 'Sprache',
description: 'Sprachliche Richtigkeit und Ausdrucksfähigkeit',
weight: 2.0,
maxPoints: 20,
order: 1,
levels: {
create: [
{ label: 'Sehr gut', description: 'Sehr fehlerfreie Sprache, treffender Ausdruck, abwechslungsreiche Sätze.', points: 20, order: 0 },
{ label: 'Gut', description: 'Wenige Fehler, guter Ausdruck, überwiegend abwechslungsreiche Sätze.', points: 16, order: 1 },
{ label: 'Befriedigend', description: 'Einige Fehler, einfacher Ausdruck, ähnliche Satzstrukturen.', points: 11, order: 2 },
{ label: 'Ausreichend', description: 'Viele Fehler, eingeschränkter Ausdruck, sehr einfache Sätze.', points: 6, order: 3 },
],
},
},
{
name: 'Darstellung',
description: 'Äußere Form, Übersichtlichkeit und Lesbarkeit',
weight: 1.0,
maxPoints: 20,
order: 2,
levels: {
create: [
{ label: 'Sehr gut', description: 'Sehr übersichtlich, saubere Schrift, klare Absatzstruktur.', points: 20, order: 0 },
{ label: 'Gut', description: 'Übersichtlich, gut lesbar, angemessene Absatzstruktur.', points: 16, order: 1 },
{ label: 'Befriedigend', description: 'Eher unübersichtlich, noch lesbar, unklare Absatzstruktur.', points: 11, order: 2 },
{ label: 'Ausreichend', description: 'Unübersichtlich, schwer lesbar, keine Absatzstruktur.', points: 6, order: 3 },
],
},
},
],
},
},
})
// Rubric 2: Mündliche Prüfung Mathe (ANALYTIC)
const rubric2 = await prisma.rubric.create({
data: {
schoolId: school.id,
teacherId: teacher.id,
subjectId: mathSubject.id,
title: 'Mündliche Prüfung Mathe',
description: 'Bewertungsraster für mündliche Mathematikprüfungen',
type: 'ANALYTIC',
maxPoints: 40,
isPublic: true,
version: 1,
criteria: {
create: [
{
name: 'Fachwissen',
description: 'Beherrschung mathematischer Begriffe und Verfahren',
weight: 2.0,
maxPoints: 20,
order: 0,
levels: {
create: [
{ label: 'Sehr gut', description: 'Sichere Beherrschung aller Fachbegriffe, kann Verfahren selbstständig erklären.', points: 20, order: 0 },
{ label: 'Gut', description: 'Gute Beherrschung der Fachbegriffe, kann Verfahren mit Hilfestellung erklären.', points: 16, order: 1 },
{ label: 'Befriedigend', description: 'Grundlegende Fachbegriffe bekannt, Erklärungen sind lückenhaft.', points: 11, order: 2 },
{ label: 'Ausreichend', description: 'Wenige Fachbegriffe bekannt, keine zusammenhängenden Erklärungen möglich.', points: 6, order: 3 },
],
},
},
{
name: 'Problemlösung',
description: 'Fähigkeit, mathematische Probleme selbstständig zu lösen',
weight: 2.0,
maxPoints: 20,
order: 1,
levels: {
create: [
{ label: 'Sehr gut', description: 'Löst Probleme selbstständig und findet eigene Lösungswege.', points: 20, order: 0 },
{ label: 'Gut', description: 'Löst Probleme mit wenig Hilfestellung, findet angemessene Lösungswege.', points: 16, order: 1 },
{ label: 'Befriedigend', description: 'Löst Probleme nur mit Hilfestellung, Lösungswege sind nachvollziehbar.', points: 11, order: 2 },
{ label: 'Ausreichend', description: 'Kann Probleme auch mit Hilfestellung kaum lösen.', points: 6, order: 3 },
],
},
},
],
},
},
})
// Rubric 3: Projektpräsentation (HOLISTIC)
const rubric3 = await prisma.rubric.create({
data: {
schoolId: school.id,
teacherId: teacher.id,
subjectId: null,
title: 'Projektpräsentation',
description: 'Ganzheitliche Bewertung von Projektpräsentationen',
type: 'HOLISTIC',
maxPoints: 15,
isPublic: false,
version: 1,
criteria: {
create: [
{
name: 'Gesamteindruck',
description: 'Gesamteindruck der Präsentation inklusive Inhalt, Vortrag und Visualisierung',
weight: 1.0,
maxPoints: 15,
order: 0,
levels: {
create: [
{ label: 'Sehr gut', description: 'Hervorragende Präsentation mit klarer Struktur, überzeugendem Vortrag und ansprechender Visualisierung.', points: 15, order: 0 },
{ label: 'Gut', description: 'Gute Präsentation mit erkennbarer Struktur, solidem Vortrag und angemessener Visualisierung.', points: 12, order: 1 },
{ label: 'Befriedigend', description: 'Ausreichende Präsentation mit Schwächen in Struktur, Vortrag oder Visualisierung.', points: 8, order: 2 },
{ label: 'Ausreichend', description: 'Schwache Präsentation mit deutlichen Mängeln in Struktur, Vortrag und Visualisierung.', points: 5, order: 3 },
],
},
},
],
},
},
})
console.log(` Created 3 rubrics: Aufsatzbewertung Deutsch, Mündliche Prüfung Mathe, Projektpräsentation`)
// ─── 16. Comment Categories & Bank Entries ────────────────────────────
console.log(' 💬 Creating comment categories and bank entries...')
const commentCatSozial = await prisma.commentCategory.create({
data: { schoolId: school.id, name: 'Sozialverhalten', color: '#10b981', icon: '🤝' },
})
const commentCatArbeit = await prisma.commentCategory.create({
data: { schoolId: school.id, name: 'Arbeitsverhalten', color: '#14b8a6', icon: '✋' },
})
const commentCatFach = await prisma.commentCategory.create({
data: { schoolId: school.id, name: 'Fachliche Kompetenz', color: '#f59e0b', icon: '📚' },
})
const commentCatMotiv = await prisma.commentCategory.create({
data: { schoolId: school.id, name: 'Motivation', color: '#8b5cf6', icon: '💪' },
})
const commentCatGesamt = await prisma.commentCategory.create({
data: { schoolId: school.id, name: 'Gesamteindruck', color: '#f43f5e', icon: '⭐' },
})
const commentEntries = [
{ cat: commentCatSozial, title: 'Aktive Mitarbeit', text: 'X beteiligt sich aktiv und konstruktiv am Unterrichtsgeschehen.', subjectId: null, gradeLevel: 'all', isPublic: true, usageCount: 12, tags: 'sozial,mitarbeit,aktiv' },
{ cat: commentCatSozial, title: 'Hilfsbereitschaft', text: 'X zeigt große Hilfsbereitschaft gegenüber Mitschülern und unterstützt andere gerne.', subjectId: null, gradeLevel: 'all', isPublic: true, usageCount: 8, tags: 'sozial,hilfe,team' },
{ cat: commentCatSozial, title: 'Respektvoller Umgang', text: 'X geht respektvoll und rücksichtsvoll mit anderen um.', subjectId: null, gradeLevel: 'all', isPublic: false, usageCount: 5, tags: 'sozial,respekt' },
{ cat: commentCatArbeit, title: 'Zuverlässige Hausaufgaben', text: 'X erledigt die Hausaufgaben regelmäßig und sorgfältig.', subjectId: null, gradeLevel: '3-4', isPublic: true, usageCount: 15, tags: 'arbeit,hausaufgaben,zuverlässig' },
{ cat: commentCatArbeit, title: 'Sorgfältiges Arbeiten', text: 'X arbeitet konzentriert und sorgfältig an den gestellten Aufgaben.', subjectId: null, gradeLevel: 'all', isPublic: true, usageCount: 10, tags: 'arbeit,konzentration,sorgfalt' },
{ cat: commentCatArbeit, title: 'Selbstständige Organisation', text: 'X kann Arbeitsmaterialien selbstständig organisieren und den Arbeitsplatz ordentlich halten.', subjectId: null, gradeLevel: '3', isPublic: false, usageCount: 3, tags: 'arbeit,organisation' },
{ cat: commentCatFach, title: 'Gute Lesekompetenz', text: 'X liest flüssig und versteht auch komplexe Texte.', subjectId: germanSubject.id, gradeLevel: '3-4', isPublic: true, usageCount: 9, tags: 'fach,lesen,deutsch' },
{ cat: commentCatFach, title: 'Sicheres Rechnen', text: 'X beherrscht die Grundrechenarten sicher und kann sie in Sachaufgaben anwenden.', subjectId: mathSubject.id, gradeLevel: '3', isPublic: true, usageCount: 7, tags: 'fach,rechnen,mathe' },
{ cat: commentCatFach, title: 'Gute Schreibkompetenz', text: 'X verfasst strukturierte und verständliche Texte mit angemessenem Wortschatz.', subjectId: germanSubject.id, gradeLevel: '3-4', isPublic: false, usageCount: 6, tags: 'fach,schreiben,deutsch' },
{ cat: commentCatMotiv, title: 'Hohe Motivation', text: 'X zeigt große Begeisterung und Engagement im Unterricht.', subjectId: null, gradeLevel: 'all', isPublic: true, usageCount: 11, tags: 'motivation,engagement' },
{ cat: commentCatMotiv, title: 'Neugier und Lernfreude', text: 'X ist neugierig und zeigt Freude am Lernen neuer Inhalte.', subjectId: null, gradeLevel: 'all', isPublic: false, usageCount: 4, tags: 'motivation,neugier,lernfreude' },
{ cat: commentCatGesamt, title: 'Positiver Gesamteindruck', text: 'X hat sich in diesem Schuljahr insgesamt sehr positiv entwickelt und ist eine Bereicherung für die Klasse.', subjectId: null, gradeLevel: 'all', isPublic: true, usageCount: 14, tags: 'gesamt,positiv,entwicklung' },
]
for (const ce of commentEntries) {
await prisma.commentBankEntry.create({
data: {
schoolId: school.id,
teacherId: teacher.id,
categoryId: ce.cat.id,
subjectId: ce.subjectId,
title: ce.title,
text: ce.text,
gradeLevel: ce.gradeLevel,
schoolType: 'ELEMENTARY',
isPublic: ce.isPublic,
usageCount: ce.usageCount,
tags: ce.tags,
},
})
}
console.log(` Created 5 comment categories, 12 comment bank entries`)
// ─── Summary ───────────────────────────────────────────────────────────
console.log('\n ✅ Seed completed successfully!\n')
console.log(' 📊 Summary:')
console.log(' ─────────────────────────────────────────')
console.log(` School: ${school.name}`)
console.log(` School Year: ${schoolYear.label}`)
console.log(` Teacher: ${teacher.firstName} ${teacher.lastName} (${teacher.email})`)
console.log(` Classes: 3a (8 students), 3b (7 students)`)
console.log(` Students: ${students3a.length + students3b.length} total`)
console.log(` Subjects: Mathematik, Deutsch`)
console.log(` Templates: Mathematik Klasse 3, Deutsch Klasse 3`)
console.log(` Categories: 6 (3 Math + 3 German)`)
console.log(` Competencies: ${mathCompetencies.length + germanCompetencies.length} (7 Math + 7 German)`)
console.log(` Progress: ${progressCount} entries`)
console.log(` Assessment: 1 with ${students3a.length} results`)
console.log(` Grading: 1 scheme (Noten 1-6)`)
console.log(` Audit Logs: ${auditEntries.length}`)
console.log(` Parent Contacts: ${parentContacts.length}, Parent Messages: ${msgCount}`)
console.log(` Behavior: ${behaviorCategoryDefs.length} categories, ${incidentCount} incidents`)
console.log(` Rubrics: 3 (Aufsatzbewertung, Mündliche Prüfung, Projektpräsentation)`)
console.log(` Comments: 5 categories, 12 bank entries`)
console.log(' ─────────────────────────────────────────')
console.log('\n 🔑 Login: demo@competencetrack.org / Demo2025!')
}
main()
.catch((e) => {
console.error('❌ Seed failed:', e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})