Files
breakpilot-core/consent-service/internal/database/migrate_school.go
Benjamin Admin 92c86ec6ba [split-required] [guardrail-change] Enforce 500 LOC budget across all services
Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook)
and split all 44 files exceeding 500 LOC into domain-focused modules:

- consent-service (Go): models, handlers, services, database splits
- backend-core (Python): security_api, rbac_api, pdf_service, auth splits
- admin-core (TypeScript): 5 page.tsx + sidebar extractions
- pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits
- voice-service (Python): enhanced_task_orchestrator split

Result: 0 violations, 36 exempted (pipeline, tests, pure-data files).
Go build verified clean. No behavior changes — pure structural splits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 00:09:30 +02:00

183 lines
5.8 KiB
Go

package database
import (
"context"
"fmt"
)
// migrateSchool creates school management tables: schools, classes,
// students, teachers, parents, timetable, attendance, grades,
// class diary, parent meetings, Matrix integration (Phase 9).
func migrateSchool(db *DB) error {
ctx := context.Background()
migrations := []string{
// =============================================
// Phase 9: Schulverwaltung / School Management
// Matrix-basierte Kommunikation für Schulen
// =============================================
// Schools table
`CREATE TABLE IF NOT EXISTS schools (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
short_name VARCHAR(50),
type VARCHAR(50) NOT NULL,
address TEXT,
city VARCHAR(100),
postal_code VARCHAR(20),
state VARCHAR(50),
country VARCHAR(2) DEFAULT 'DE',
phone VARCHAR(50),
email VARCHAR(255),
website VARCHAR(255),
matrix_server_name VARCHAR(255),
logo_url TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)`,
// School years
`CREATE TABLE IF NOT EXISTS school_years (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
school_id UUID NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
name VARCHAR(20) NOT NULL,
start_date DATE NOT NULL,
end_date DATE NOT NULL,
is_current BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(school_id, name)
)`,
// Subjects
`CREATE TABLE IF NOT EXISTS subjects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
school_id UUID NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL,
short_name VARCHAR(10) NOT NULL,
color VARCHAR(7),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(school_id, short_name)
)`,
// Classes
`CREATE TABLE IF NOT EXISTS classes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
school_id UUID NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
school_year_id UUID NOT NULL REFERENCES school_years(id) ON DELETE CASCADE,
name VARCHAR(20) NOT NULL,
grade INT NOT NULL,
section VARCHAR(5),
room VARCHAR(50),
matrix_info_room VARCHAR(255),
matrix_rep_room VARCHAR(255),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(school_id, school_year_id, name)
)`,
// Students
`CREATE TABLE IF NOT EXISTS students (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
school_id UUID NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
class_id UUID NOT NULL REFERENCES classes(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
student_number VARCHAR(50),
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
date_of_birth DATE,
gender VARCHAR(1),
matrix_user_id VARCHAR(255),
matrix_dm_room VARCHAR(255),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
)`,
// Teachers
`CREATE TABLE IF NOT EXISTS teachers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
school_id UUID NOT NULL REFERENCES schools(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
teacher_code VARCHAR(10),
title VARCHAR(20),
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
matrix_user_id VARCHAR(255),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(school_id, user_id)
)`,
// Class teachers assignment
`CREATE TABLE IF NOT EXISTS class_teachers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
class_id UUID NOT NULL REFERENCES classes(id) ON DELETE CASCADE,
teacher_id UUID NOT NULL REFERENCES teachers(id) ON DELETE CASCADE,
is_primary BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(class_id, teacher_id)
)`,
// Teacher subjects assignment
`CREATE TABLE IF NOT EXISTS teacher_subjects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
teacher_id UUID NOT NULL REFERENCES teachers(id) ON DELETE CASCADE,
subject_id UUID NOT NULL REFERENCES subjects(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(teacher_id, subject_id)
)`,
// Parents
`CREATE TABLE IF NOT EXISTS parents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
matrix_user_id VARCHAR(255),
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
phone VARCHAR(50),
emergency_contact BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id)
)`,
// Student-parent relationships
`CREATE TABLE IF NOT EXISTS student_parents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
student_id UUID NOT NULL REFERENCES students(id) ON DELETE CASCADE,
parent_id UUID NOT NULL REFERENCES parents(id) ON DELETE CASCADE,
relationship VARCHAR(20) NOT NULL,
is_primary BOOLEAN DEFAULT FALSE,
has_custody BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(student_id, parent_id)
)`,
// Parent representatives
`CREATE TABLE IF NOT EXISTS parent_representatives (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
class_id UUID NOT NULL REFERENCES classes(id) ON DELETE CASCADE,
parent_id UUID NOT NULL REFERENCES parents(id) ON DELETE CASCADE,
role VARCHAR(20) NOT NULL,
elected_at TIMESTAMPTZ NOT NULL,
expires_at TIMESTAMPTZ,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW()
)`,
}
for _, migration := range migrations {
if _, err := db.Pool.Exec(ctx, migration); err != nil {
return fmt.Errorf("migrateSchool: %w", err)
}
}
// Run the second batch (timetable, attendance, grades, etc.)
return migrateSchoolPart2(db)
}