Files
schulos/scripts/generate-data-model.ts
T

179 lines
7.0 KiB
TypeScript

/**
* Generate DATA_MODEL.md from Prisma schema and database config
* This document lists all data entities, their purposes, legal basis, and retention categories
*/
const fs = require("fs");
const path = require("path");
interface DataEntity {
name: string;
dataCategories: string[];
legalBasis: string;
purpose: string;
retentionCategory: string;
notes?: string;
}
// Core data model definition
const dataModel: DataEntity[] = [
{
name: "User",
dataCategories: ["firstName", "lastName", "email", "role", "createdAt"],
legalBasis: "GDPR Art. 6(1)(c) - Legal obligation (Schulgesetz)",
purpose: "User account management and authentication",
retentionCategory: "TEACHER_AUDIT_LOG",
notes: "Retained per employment contract or student enrollment period",
},
{
name: "Student",
dataCategories: [
"firstName",
"lastName",
"dateOfBirth",
"email",
"phone",
"address",
],
legalBasis: "GDPR Art. 6(1)(c) - Legal obligation (Schulgesetz)",
purpose: "Student record management and communication",
retentionCategory: "LEARNING_PROGRESS",
},
{
name: "ClassGroup",
dataCategories: ["name", "grade", "year", "studentCount"],
legalBasis: "GDPR Art. 6(1)(c) - Legal obligation (Schulgesetz)",
purpose: "Class organization and grouping",
retentionCategory: "LEARNING_PROGRESS",
},
{
name: "Assessment",
dataCategories: ["studentId", "subjectId", "score", "feedback", "date"],
legalBasis:
"GDPR Art. 6(1)(a) - Consent + Art. 8 (children) - Parental consent",
purpose: "Record student learning progress and performance",
retentionCategory: "ASSESSMENT_RESULT",
},
{
name: "Report (Final Grade Report)",
dataCategories: ["studentId", "finalGrades", "comments", "semester"],
legalBasis:
"GDPR Art. 6(1)(c) - Legal obligation (Schulgesetz - long-term retention requirement)",
purpose: "Permanent record of student academic achievement",
retentionCategory: "FINAL_REPORT",
notes: "Typically retained for student lifetime + 30 years (per Bundesland law)",
},
{
name: "BehaviorIncident",
dataCategories: ["studentId", "incidentType", "description", "resolution"],
legalBasis:
"GDPR Art. 6(1)(a) - Consent + Art. 9(2)(h) - Health/safety necessity",
purpose: "Track disciplinary actions and behavioral support",
retentionCategory: "DISCIPLINARY_RECORD",
notes: "Retention varies by Bundesland; typically 3-10 years",
},
{
name: "ExamIncidentEvent",
dataCategories: ["studentId", "eventType", "timestamp", "notes"],
legalBasis:
"GDPR Art. 6(1)(a) - Consent + Art. 9(2)(h) - Exam integrity necessity",
purpose: "Log exam-time incidents (distress, technical issues)",
retentionCategory: "EXAM_INCIDENT",
notes: "Shorter retention (6-12 months) unless escalated to disciplinary record",
},
{
name: "Message",
dataCategories: ["senderId", "recipientId", "content", "timestamp"],
legalBasis: "GDPR Art. 6(1)(a) - Consent (user opt-in for messaging)",
purpose: "Communication between teachers, parents, and students",
retentionCategory: "MESSAGE",
},
{
name: "AuditLog",
dataCategories: ["userId", "action", "entityType", "timestamp"],
legalBasis: "GDPR Art. 6(1)(c) - Legal obligation (data protection compliance)",
purpose: "Audit trail for regulatory compliance and breach investigation",
retentionCategory: "TEACHER_AUDIT_LOG",
},
{
name: "Notebook (Student Learning Notebooks)",
dataCategories: ["studentId", "content", "createdAt"],
legalBasis: "GDPR Art. 6(1)(a) - Consent (student/parent consent)",
purpose: "Student learning portfolio and reflection entries",
retentionCategory: "NOTEBOOK_PAGE",
},
{
name: "AITutorLog",
dataCategories: ["studentId", "interaction", "timestamp", "response"],
legalBasis: "GDPR Art. 6(1)(a) - Explicit consent + Art. 22 - Automated decisions",
purpose: "Track AI tutor interactions for learning analytics",
retentionCategory: "AI_TUTOR_LOG",
notes: "Subject to Art. 22 GDPR - no solely automated grading decisions",
},
];
export function generateDataModel(outputPath: string = "docs/legal"): void {
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath, { recursive: true });
}
const lines: string[] = [
"# Data Model Documentation",
"",
"**Status**: Schema Reference for ExaMetra (Module L)",
"",
"This document outlines all personal data entities collected by ExaMetra, their legal basis, purpose, and retention category.",
"",
"## Core Entities",
"",
"| Entity | Data Categories | Legal Basis | Purpose | Retention Category | Notes |",
"|--------|-----------------|------------|---------|-------------------|-------|",
];
for (const entity of dataModel) {
const dataCategories = entity.dataCategories.join(", ");
const notes = entity.notes ? `${entity.notes}` : "—";
const row = `| **${entity.name}** | ${dataCategories} | ${entity.legalBasis} | ${entity.purpose} | **${entity.retentionCategory}** | ${notes} |`;
lines.push(row);
}
lines.push(
"",
"## Retention Categories",
"",
"| Category | Typical Retention | Legal Basis |",
"|----------|------------------|------------|",
"| LEARNING_PROGRESS | 1-2 years | Bundesland school law (Schulgesetz) |",
"| ASSESSMENT_RESULT | 1-2 years | Bundesland school law |",
"| FINAL_REPORT | 30+ years | Permanent record under Schulgesetz |",
"| DISCIPLINARY_RECORD | 3-10 years | Varies by Bundesland |",
"| EXAM_INCIDENT | 6-12 months | Exam integrity + school safety |",
"| MESSAGE | 1-2 years | Communication retention best practices |",
"| AI_TUTOR_LOG | 6-12 months | Pedagogical purpose + learning analytics |",
"| NOTEBOOK_PAGE | 1-2 years | Student portfolio / learning records |",
"| SIGNAGE_LOG | 3 months | Safety monitoring (emergency only) |",
"| TEACHER_AUDIT_LOG | 1-2 years | Compliance and audit trail |",
"",
"## Compliance Notes",
"",
"- **DPO Review**: Each retention period must be reviewed and approved by your school's Data Protection Officer (DPO)",
"- **Bundesland Variations**: Retention periods may vary by state (Bundesland). Consult your regional school authority.",
"- **Parental Consent**: For student data (especially minors), parental consent is required per Art. 8 GDPR",
"- **Automated Decisions**: No solely automated decisions (e.g., automatic grade lowering) without human review per Art. 22 GDPR",
"- **Data Subject Rights**: All entities are subject to GDPR rights: access, correction, erasure (where legally permitted), portability",
"",
"## Generated From",
"",
`Generated: ${new Date().toISOString()}`,
"Source: ExaMetra Prisma schema (Module L)",
];
const filePath = path.join(outputPath, "DATA_MODEL.md");
fs.writeFileSync(filePath, lines.join("\n"), "utf-8");
console.log(`[v0] Generated DATA_MODEL.md at ${filePath}`);
}
if (require.main === module) {
generateDataModel();
}