""" Certificates Models - Pydantic models and enums for Zeugnisverwaltung. """ from datetime import datetime from typing import Optional, List, Dict from enum import Enum from pydantic import BaseModel, Field # ============================================================================= # Enums # ============================================================================= class CertificateType(str, Enum): """Typen von Zeugnissen.""" HALBJAHR = "halbjahr" JAHRES = "jahres" ABSCHLUSS = "abschluss" ABGANG = "abgang" UEBERGANG = "uebergang" class CertificateStatus(str, Enum): """Status eines Zeugnisses.""" DRAFT = "draft" REVIEW = "review" APPROVED = "approved" ISSUED = "issued" ARCHIVED = "archived" class GradeType(str, Enum): """Notentyp.""" NUMERIC = "numeric" POINTS = "points" TEXT = "text" class BehaviorGrade(str, Enum): """Verhaltens-/Arbeitsnoten.""" A = "A" B = "B" C = "C" D = "D" # ============================================================================= # Pydantic Models # ============================================================================= class SchoolInfoModel(BaseModel): """Schulinformationen fuer Zeugnis.""" name: str address: str phone: str email: str website: Optional[str] = None principal: Optional[str] = None logo_path: Optional[str] = None class SubjectGrade(BaseModel): """Note fuer ein Fach.""" name: str = Field(..., description="Fachname") grade: str = Field(..., description="Note (1-6 oder A-D)") points: Optional[int] = Field(None, description="Punkte (Oberstufe, 0-15)") note: Optional[str] = Field(None, description="Bemerkung zum Fach") class AttendanceInfo(BaseModel): """Anwesenheitsinformationen.""" days_absent: int = Field(0, description="Fehlende Tage gesamt") days_excused: int = Field(0, description="Entschuldigte Tage") days_unexcused: int = Field(0, description="Unentschuldigte Tage") hours_absent: Optional[int] = Field(None, description="Fehlstunden gesamt") class CertificateCreateRequest(BaseModel): """Request zum Erstellen eines neuen Zeugnisses.""" student_id: str = Field(..., description="ID des Schuelers") student_name: str = Field(..., description="Name des Schuelers") student_birthdate: str = Field(..., description="Geburtsdatum") student_class: str = Field(..., description="Klasse") school_year: str = Field(..., description="Schuljahr (z.B. '2024/2025')") certificate_type: CertificateType = Field(..., description="Art des Zeugnisses") subjects: List[SubjectGrade] = Field(..., description="Fachnoten") attendance: AttendanceInfo = Field(default_factory=AttendanceInfo) remarks: Optional[str] = Field(None, description="Bemerkungen") class_teacher: str = Field(..., description="Klassenlehrer/in") principal: str = Field(..., description="Schulleiter/in") school_info: Optional[SchoolInfoModel] = Field(None) issue_date: Optional[str] = Field(None, description="Ausstellungsdatum") social_behavior: Optional[BehaviorGrade] = Field(None) work_behavior: Optional[BehaviorGrade] = Field(None) class CertificateUpdateRequest(BaseModel): """Request zum Aktualisieren eines Zeugnisses.""" subjects: Optional[List[SubjectGrade]] = None attendance: Optional[AttendanceInfo] = None remarks: Optional[str] = None class_teacher: Optional[str] = None principal: Optional[str] = None social_behavior: Optional[BehaviorGrade] = None work_behavior: Optional[BehaviorGrade] = None status: Optional[CertificateStatus] = None class CertificateResponse(BaseModel): """Response mit Zeugnisdaten.""" id: str student_id: str student_name: str student_birthdate: str student_class: str school_year: str certificate_type: CertificateType subjects: List[SubjectGrade] attendance: AttendanceInfo remarks: Optional[str] class_teacher: str principal: str school_info: Optional[SchoolInfoModel] issue_date: Optional[str] social_behavior: Optional[BehaviorGrade] work_behavior: Optional[BehaviorGrade] status: CertificateStatus average_grade: Optional[float] pdf_path: Optional[str] dsms_cid: Optional[str] created_at: datetime updated_at: datetime class CertificateListResponse(BaseModel): """Response mit Liste von Zeugnissen.""" certificates: List[CertificateResponse] total: int page: int page_size: int class GradeStatistics(BaseModel): """Notenstatistiken fuer eine Klasse.""" class_name: str school_year: str certificate_type: CertificateType student_count: int average_grade: float grade_distribution: Dict[str, int] subject_averages: Dict[str, float] # ============================================================================= # Helper Functions # ============================================================================= def get_type_label(cert_type: CertificateType) -> str: """Gibt menschenlesbare Labels fuer Zeugnistypen zurueck.""" labels = { CertificateType.HALBJAHR: "Halbjahreszeugnis", CertificateType.JAHRES: "Jahreszeugnis", CertificateType.ABSCHLUSS: "Abschlusszeugnis", CertificateType.ABGANG: "Abgangszeugnis", CertificateType.UEBERGANG: "Uebergangszeugnis", } return labels.get(cert_type, cert_type.value) def calculate_average(subjects: List[Dict]) -> Optional[float]: """Berechnet Notendurchschnitt.""" numeric_grades = [] for subject in subjects: grade = subject.get("grade", "") try: numeric = float(grade) if 1 <= numeric <= 6: numeric_grades.append(numeric) except (ValueError, TypeError): pass if numeric_grades: return round(sum(numeric_grades) / len(numeric_grades), 2) return None