Files
breakpilot-core/consent-service/internal/models/school_operations.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

383 lines
18 KiB
Go

package models
import (
"time"
"github.com/google/uuid"
)
// ========================================
// Stundenplan / Timetable
// ========================================
// TimetableSlot represents a time slot in the timetable
type TimetableSlot struct {
ID uuid.UUID `json:"id" db:"id"`
SchoolID uuid.UUID `json:"school_id" db:"school_id"`
SlotNumber int `json:"slot_number" db:"slot_number"` // 1, 2, 3... (Stunde)
StartTime string `json:"start_time" db:"start_time"` // "08:00"
EndTime string `json:"end_time" db:"end_time"` // "08:45"
IsBreak bool `json:"is_break" db:"is_break"` // Pause
Name *string `json:"name,omitempty" db:"name"` // e.g., "1. Stunde", "Große Pause"
}
// TimetableEntry represents a single lesson in the timetable
type TimetableEntry struct {
ID uuid.UUID `json:"id" db:"id"`
SchoolYearID uuid.UUID `json:"school_year_id" db:"school_year_id"`
ClassID uuid.UUID `json:"class_id" db:"class_id"`
SubjectID uuid.UUID `json:"subject_id" db:"subject_id"`
TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"`
SlotID uuid.UUID `json:"slot_id" db:"slot_id"`
DayOfWeek int `json:"day_of_week" db:"day_of_week"` // 1=Monday, 5=Friday
Room *string `json:"room,omitempty" db:"room"`
ValidFrom time.Time `json:"valid_from" db:"valid_from"`
ValidUntil *time.Time `json:"valid_until,omitempty" db:"valid_until"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// TimetableSubstitution represents a substitution/replacement lesson
type TimetableSubstitution struct {
ID uuid.UUID `json:"id" db:"id"`
OriginalEntryID uuid.UUID `json:"original_entry_id" db:"original_entry_id"`
Date time.Time `json:"date" db:"date"`
SubstituteTeacherID *uuid.UUID `json:"substitute_teacher_id,omitempty" db:"substitute_teacher_id"`
SubstituteSubjectID *uuid.UUID `json:"substitute_subject_id,omitempty" db:"substitute_subject_id"`
Room *string `json:"room,omitempty" db:"room"`
Type string `json:"type" db:"type"` // 'substitution', 'cancelled', 'room_change', 'supervision'
Note *string `json:"note,omitempty" db:"note"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
CreatedBy uuid.UUID `json:"created_by" db:"created_by"`
}
// ========================================
// Abwesenheit / Attendance
// ========================================
// AttendanceRecord represents a student's attendance for a specific lesson
type AttendanceRecord struct {
ID uuid.UUID `json:"id" db:"id"`
StudentID uuid.UUID `json:"student_id" db:"student_id"`
TimetableEntryID *uuid.UUID `json:"timetable_entry_id,omitempty" db:"timetable_entry_id"`
Date time.Time `json:"date" db:"date"`
SlotID uuid.UUID `json:"slot_id" db:"slot_id"`
Status string `json:"status" db:"status"` // AttendanceStatus constants
RecordedBy uuid.UUID `json:"recorded_by" db:"recorded_by"`
Note *string `json:"note,omitempty" db:"note"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// AbsenceReport represents a full absence report (one or more days)
type AbsenceReport struct {
ID uuid.UUID `json:"id" db:"id"`
StudentID uuid.UUID `json:"student_id" db:"student_id"`
StartDate time.Time `json:"start_date" db:"start_date"`
EndDate time.Time `json:"end_date" db:"end_date"`
Reason *string `json:"reason,omitempty" db:"reason"`
ReasonCategory string `json:"reason_category" db:"reason_category"`
Status string `json:"status" db:"status"`
ReportedBy uuid.UUID `json:"reported_by" db:"reported_by"`
ReportedAt time.Time `json:"reported_at" db:"reported_at"`
ConfirmedBy *uuid.UUID `json:"confirmed_by,omitempty" db:"confirmed_by"`
ConfirmedAt *time.Time `json:"confirmed_at,omitempty" db:"confirmed_at"`
MedicalCertificate bool `json:"medical_certificate" db:"medical_certificate"`
CertificateUploaded bool `json:"certificate_uploaded" db:"certificate_uploaded"`
MatrixNotificationSent bool `json:"matrix_notification_sent" db:"matrix_notification_sent"`
EmailNotificationSent bool `json:"email_notification_sent" db:"email_notification_sent"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// AbsenceNotification tracks notifications sent to parents about absences
type AbsenceNotification struct {
ID uuid.UUID `json:"id" db:"id"`
AttendanceRecordID uuid.UUID `json:"attendance_record_id" db:"attendance_record_id"`
ParentID uuid.UUID `json:"parent_id" db:"parent_id"`
Channel string `json:"channel" db:"channel"`
MessageContent string `json:"message_content" db:"message_content"`
SentAt *time.Time `json:"sent_at,omitempty" db:"sent_at"`
ReadAt *time.Time `json:"read_at,omitempty" db:"read_at"`
ResponseReceived bool `json:"response_received" db:"response_received"`
ResponseContent *string `json:"response_content,omitempty" db:"response_content"`
ResponseAt *time.Time `json:"response_at,omitempty" db:"response_at"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// ========================================
// Notenspiegel / Grades
// ========================================
// GradeScale represents the grading scale used
type GradeScale struct {
ID uuid.UUID `json:"id" db:"id"`
SchoolID uuid.UUID `json:"school_id" db:"school_id"`
Name string `json:"name" db:"name"`
MinValue float64 `json:"min_value" db:"min_value"`
MaxValue float64 `json:"max_value" db:"max_value"`
PassingValue float64 `json:"passing_value" db:"passing_value"`
IsAscending bool `json:"is_ascending" db:"is_ascending"`
IsDefault bool `json:"is_default" db:"is_default"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// Grade represents a single grade for a student
type Grade struct {
ID uuid.UUID `json:"id" db:"id"`
StudentID uuid.UUID `json:"student_id" db:"student_id"`
SubjectID uuid.UUID `json:"subject_id" db:"subject_id"`
TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"`
SchoolYearID uuid.UUID `json:"school_year_id" db:"school_year_id"`
GradeScaleID uuid.UUID `json:"grade_scale_id" db:"grade_scale_id"`
Type string `json:"type" db:"type"`
Value float64 `json:"value" db:"value"`
Weight float64 `json:"weight" db:"weight"`
Date time.Time `json:"date" db:"date"`
Title *string `json:"title,omitempty" db:"title"`
Description *string `json:"description,omitempty" db:"description"`
IsVisible bool `json:"is_visible" db:"is_visible"`
Semester int `json:"semester" db:"semester"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// GradeComment represents a teacher comment on a student's grade
type GradeComment struct {
ID uuid.UUID `json:"id" db:"id"`
GradeID uuid.UUID `json:"grade_id" db:"grade_id"`
TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"`
Comment string `json:"comment" db:"comment"`
IsPrivate bool `json:"is_private" db:"is_private"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// ========================================
// Klassenbuch, Meetings, Communication
// ========================================
// ClassDiaryEntry represents an entry in the digital class diary
type ClassDiaryEntry struct {
ID uuid.UUID `json:"id" db:"id"`
ClassID uuid.UUID `json:"class_id" db:"class_id"`
Date time.Time `json:"date" db:"date"`
SlotID uuid.UUID `json:"slot_id" db:"slot_id"`
SubjectID uuid.UUID `json:"subject_id" db:"subject_id"`
TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"`
Topic *string `json:"topic,omitempty" db:"topic"`
Homework *string `json:"homework,omitempty" db:"homework"`
HomeworkDueDate *time.Time `json:"homework_due_date,omitempty" db:"homework_due_date"`
Materials *string `json:"materials,omitempty" db:"materials"`
Notes *string `json:"notes,omitempty" db:"notes"`
IsCancelled bool `json:"is_cancelled" db:"is_cancelled"`
CancellationReason *string `json:"cancellation_reason,omitempty" db:"cancellation_reason"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// ParentMeetingSlot represents available time slots for parent meetings
type ParentMeetingSlot struct {
ID uuid.UUID `json:"id" db:"id"`
TeacherID uuid.UUID `json:"teacher_id" db:"teacher_id"`
Date time.Time `json:"date" db:"date"`
StartTime string `json:"start_time" db:"start_time"`
EndTime string `json:"end_time" db:"end_time"`
Location *string `json:"location,omitempty" db:"location"`
IsOnline bool `json:"is_online" db:"is_online"`
MeetingLink *string `json:"meeting_link,omitempty" db:"meeting_link"`
IsBooked bool `json:"is_booked" db:"is_booked"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// ParentMeeting represents a booked parent-teacher meeting
type ParentMeeting struct {
ID uuid.UUID `json:"id" db:"id"`
SlotID uuid.UUID `json:"slot_id" db:"slot_id"`
ParentID uuid.UUID `json:"parent_id" db:"parent_id"`
StudentID uuid.UUID `json:"student_id" db:"student_id"`
Topic *string `json:"topic,omitempty" db:"topic"`
Notes *string `json:"notes,omitempty" db:"notes"`
Status string `json:"status" db:"status"`
CancelledAt *time.Time `json:"cancelled_at,omitempty" db:"cancelled_at"`
CancelledBy *uuid.UUID `json:"cancelled_by,omitempty" db:"cancelled_by"`
CancelReason *string `json:"cancel_reason,omitempty" db:"cancel_reason"`
CompletedAt *time.Time `json:"completed_at,omitempty" db:"completed_at"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// MatrixRoom tracks Matrix rooms created for school communication
type MatrixRoom struct {
ID uuid.UUID `json:"id" db:"id"`
SchoolID uuid.UUID `json:"school_id" db:"school_id"`
MatrixRoomID string `json:"matrix_room_id" db:"matrix_room_id"`
Type string `json:"type" db:"type"`
ClassID *uuid.UUID `json:"class_id,omitempty" db:"class_id"`
StudentID *uuid.UUID `json:"student_id,omitempty" db:"student_id"`
Name string `json:"name" db:"name"`
IsEncrypted bool `json:"is_encrypted" db:"is_encrypted"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// MatrixRoomMember tracks membership in Matrix rooms
type MatrixRoomMember struct {
ID uuid.UUID `json:"id" db:"id"`
MatrixRoomID uuid.UUID `json:"matrix_room_id" db:"matrix_room_id"`
MatrixUserID string `json:"matrix_user_id" db:"matrix_user_id"`
UserID *uuid.UUID `json:"user_id,omitempty" db:"user_id"`
PowerLevel int `json:"power_level" db:"power_level"`
CanWrite bool `json:"can_write" db:"can_write"`
JoinedAt time.Time `json:"joined_at" db:"joined_at"`
LeftAt *time.Time `json:"left_at,omitempty" db:"left_at"`
}
// ParentOnboardingToken for QR-code based parent onboarding
type ParentOnboardingToken struct {
ID uuid.UUID `json:"id" db:"id"`
SchoolID uuid.UUID `json:"school_id" db:"school_id"`
ClassID uuid.UUID `json:"class_id" db:"class_id"`
StudentID uuid.UUID `json:"student_id" db:"student_id"`
Token string `json:"token" db:"token"`
Role string `json:"role" db:"role"`
ExpiresAt time.Time `json:"expires_at" db:"expires_at"`
UsedAt *time.Time `json:"used_at,omitempty" db:"used_at"`
UsedByUserID *uuid.UUID `json:"used_by_user_id,omitempty" db:"used_by_user_id"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
CreatedBy uuid.UUID `json:"created_by" db:"created_by"`
}
// ========================================
// Schulverwaltung DTOs
// ========================================
// CreateSchoolRequest for creating a new school
type CreateSchoolRequest struct {
Name string `json:"name" binding:"required"`
ShortName *string `json:"short_name"`
Type string `json:"type" binding:"required"`
Address *string `json:"address"`
City *string `json:"city"`
PostalCode *string `json:"postal_code"`
State *string `json:"state"`
Phone *string `json:"phone"`
Email *string `json:"email"`
Website *string `json:"website"`
}
// CreateClassRequest for creating a new class
type CreateClassRequest struct {
SchoolYearID string `json:"school_year_id" binding:"required"`
Name string `json:"name" binding:"required"`
Grade int `json:"grade" binding:"required"`
Section *string `json:"section"`
Room *string `json:"room"`
}
// CreateStudentRequest for creating a new student
type CreateStudentRequest struct {
ClassID string `json:"class_id" binding:"required"`
StudentNumber *string `json:"student_number"`
FirstName string `json:"first_name" binding:"required"`
LastName string `json:"last_name" binding:"required"`
DateOfBirth *string `json:"date_of_birth"` // ISO 8601
Gender *string `json:"gender"`
}
// RecordAttendanceRequest for recording attendance
type RecordAttendanceRequest struct {
StudentID string `json:"student_id" binding:"required"`
Date string `json:"date" binding:"required"` // ISO 8601
SlotID string `json:"slot_id" binding:"required"`
Status string `json:"status" binding:"required"` // AttendanceStatus
Note *string `json:"note"`
}
// ReportAbsenceRequest for parents reporting absence
type ReportAbsenceRequest struct {
StudentID string `json:"student_id" binding:"required"`
StartDate string `json:"start_date" binding:"required"`
EndDate string `json:"end_date" binding:"required"`
Reason *string `json:"reason"`
ReasonCategory string `json:"reason_category" binding:"required"`
}
// CreateGradeRequest for creating a grade
type CreateGradeRequest struct {
StudentID string `json:"student_id" binding:"required"`
SubjectID string `json:"subject_id" binding:"required"`
SchoolYearID string `json:"school_year_id" binding:"required"`
Type string `json:"type" binding:"required"`
Value float64 `json:"value" binding:"required"`
Weight float64 `json:"weight"`
Date string `json:"date" binding:"required"`
Title *string `json:"title"`
Description *string `json:"description"`
Semester int `json:"semester" binding:"required"`
}
// StudentGradeOverview provides a summary of all grades for a student in a subject
type StudentGradeOverview struct {
Student Student `json:"student"`
Subject Subject `json:"subject"`
Grades []Grade `json:"grades"`
Average float64 `json:"average"`
OralAverage float64 `json:"oral_average"`
ExamAverage float64 `json:"exam_average"`
Semester int `json:"semester"`
}
// ClassAttendanceOverview provides attendance summary for a class
type ClassAttendanceOverview struct {
Class Class `json:"class"`
Date time.Time `json:"date"`
TotalStudents int `json:"total_students"`
PresentCount int `json:"present_count"`
AbsentCount int `json:"absent_count"`
LateCount int `json:"late_count"`
Records []AttendanceRecord `json:"records"`
}
// ParentDashboard provides a parent's view of their children's data
type ParentDashboard struct {
Children []StudentOverview `json:"children"`
UnreadMessages int `json:"unread_messages"`
UpcomingMeetings []ParentMeeting `json:"upcoming_meetings"`
RecentGrades []Grade `json:"recent_grades"`
PendingActions []string `json:"pending_actions"`
}
// StudentOverview provides summary info about a student
type StudentOverview struct {
Student Student `json:"student"`
Class Class `json:"class"`
ClassTeacher *Teacher `json:"class_teacher,omitempty"`
AttendanceRate float64 `json:"attendance_rate"`
UnexcusedAbsences int `json:"unexcused_absences"`
GradeAverage float64 `json:"grade_average"`
}
// TimetableView provides a formatted timetable for display
type TimetableView struct {
Class Class `json:"class"`
Week string `json:"week"` // ISO week: "2025-W01"
Days []TimetableDayView `json:"days"`
}
// TimetableDayView represents a single day in the timetable
type TimetableDayView struct {
Date time.Time `json:"date"`
DayName string `json:"day_name"`
Lessons []TimetableLessonView `json:"lessons"`
}
// TimetableLessonView represents a single lesson in the timetable view
type TimetableLessonView struct {
Slot TimetableSlot `json:"slot"`
Subject *Subject `json:"subject,omitempty"`
Teacher *Teacher `json:"teacher,omitempty"`
Room *string `json:"room,omitempty"`
IsSubstitution bool `json:"is_substitution"`
IsCancelled bool `json:"is_cancelled"`
Note *string `json:"note,omitempty"`
}