Files
breakpilot-lehrer/backend-lehrer/letters_models.py
Benjamin Admin 451365a312 [split-required] Split remaining 500-680 LOC files (final batch)
website (17 pages + 3 components):
- multiplayer/wizard, middleware/wizard+test-wizard, communication
- builds/wizard, staff-search, voice, sbom/wizard
- foerderantrag, mail/tasks, tools/communication, sbom
- compliance/evidence, uni-crawler, brandbook (already done)
- CollectionsTab, IngestionTab, RiskHeatmap

backend-lehrer (5 files):
- letters_api (641 → 2), certificates_api (636 → 2)
- alerts_agent/db/models (636 → 3)
- llm_gateway/communication_service (614 → 2)
- game/database already done in prior batch

klausur-service (2 files):
- hybrid_vocab_extractor (664 → 2)
- klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2)

voice-service (3 files):
- bqas/rag_judge (618 → 3), runner (529 → 2)
- enhanced_task_orchestrator (519 → 2)

studio-v2 (6 files):
- korrektur/[klausurId] (578 → 4), fairness (569 → 2)
- AlertsWizard (552 → 2), OnboardingWizard (513 → 2)
- korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 08:56:45 +02:00

196 lines
6.2 KiB
Python

"""
Letters Models - Pydantic models and enums for Elternbrief-Verwaltung.
"""
from datetime import datetime
from typing import Optional, List
from enum import Enum
from pydantic import BaseModel, Field
# =============================================================================
# Enums
# =============================================================================
class LetterType(str, Enum):
"""Typen von Elternbriefen."""
GENERAL = "general"
HALBJAHR = "halbjahr"
FEHLZEITEN = "fehlzeiten"
ELTERNABEND = "elternabend"
LOB = "lob"
CUSTOM = "custom"
class LetterTone(str, Enum):
"""Tonalitaet der Briefe."""
FORMAL = "formal"
PROFESSIONAL = "professional"
WARM = "warm"
CONCERNED = "concerned"
APPRECIATIVE = "appreciative"
class LetterStatus(str, Enum):
"""Status eines Briefes."""
DRAFT = "draft"
SENT = "sent"
ARCHIVED = "archived"
# =============================================================================
# Pydantic Models
# =============================================================================
class SchoolInfoModel(BaseModel):
"""Schulinformationen fuer Briefkopf."""
name: str
address: str
phone: str
email: str
website: Optional[str] = None
principal: Optional[str] = None
logo_path: Optional[str] = None
class LegalReferenceModel(BaseModel):
"""Rechtliche Referenz."""
law: str
paragraph: str
title: str
summary: Optional[str] = None
relevance: Optional[str] = None
class LetterCreateRequest(BaseModel):
"""Request zum Erstellen eines neuen Briefes."""
recipient_name: str = Field(..., description="Name des Empfaengers")
recipient_address: str = Field(..., description="Adresse des Empfaengers")
student_name: str = Field(..., description="Name des Schuelers")
student_class: str = Field(..., description="Klasse des Schuelers")
subject: str = Field(..., description="Betreff des Briefes")
content: str = Field(..., description="Inhalt des Briefes")
letter_type: LetterType = Field(LetterType.GENERAL, description="Art des Briefes")
tone: LetterTone = Field(LetterTone.PROFESSIONAL, description="Tonalitaet des Briefes")
teacher_name: str = Field(..., description="Name des Lehrers")
teacher_title: Optional[str] = Field(None, description="Titel des Lehrers")
school_info: Optional[SchoolInfoModel] = Field(None, description="Schulinformationen")
legal_references: Optional[List[LegalReferenceModel]] = Field(None, description="Rechtliche Referenzen")
gfk_principles_applied: Optional[List[str]] = Field(None, description="Angewandte GFK-Prinzipien")
class LetterUpdateRequest(BaseModel):
"""Request zum Aktualisieren eines Briefes."""
recipient_name: Optional[str] = None
recipient_address: Optional[str] = None
student_name: Optional[str] = None
student_class: Optional[str] = None
subject: Optional[str] = None
content: Optional[str] = None
letter_type: Optional[LetterType] = None
tone: Optional[LetterTone] = None
teacher_name: Optional[str] = None
teacher_title: Optional[str] = None
school_info: Optional[SchoolInfoModel] = None
legal_references: Optional[List[LegalReferenceModel]] = None
gfk_principles_applied: Optional[List[str]] = None
status: Optional[LetterStatus] = None
class LetterResponse(BaseModel):
"""Response mit Briefdaten."""
id: str
recipient_name: str
recipient_address: str
student_name: str
student_class: str
subject: str
content: str
letter_type: LetterType
tone: LetterTone
teacher_name: str
teacher_title: Optional[str]
school_info: Optional[SchoolInfoModel]
legal_references: Optional[List[LegalReferenceModel]]
gfk_principles_applied: Optional[List[str]]
gfk_score: Optional[float]
status: LetterStatus
pdf_path: Optional[str]
dsms_cid: Optional[str]
sent_at: Optional[datetime]
created_at: datetime
updated_at: datetime
class LetterListResponse(BaseModel):
"""Response mit Liste von Briefen."""
letters: List[LetterResponse]
total: int
page: int
page_size: int
class ExportPDFRequest(BaseModel):
"""Request zum PDF-Export."""
letter_id: Optional[str] = Field(None, description="ID eines gespeicherten Briefes")
letter_data: Optional[LetterCreateRequest] = Field(None, description="Oder direkte Briefdaten")
class ImproveRequest(BaseModel):
"""Request zur GFK-Verbesserung."""
content: str = Field(..., description="Text zur Verbesserung")
communication_type: Optional[str] = Field("general_info", description="Art der Kommunikation")
tone: Optional[str] = Field("professional", description="Gewuenschte Tonalitaet")
class ImproveResponse(BaseModel):
"""Response mit verbessertem Text."""
improved_content: str
changes: List[str]
gfk_score: float
gfk_principles_applied: List[str]
class SendEmailRequest(BaseModel):
"""Request zum Email-Versand."""
letter_id: str
recipient_email: str
cc_emails: Optional[List[str]] = None
include_pdf: bool = True
class SendEmailResponse(BaseModel):
"""Response nach Email-Versand."""
success: bool
message: str
sent_at: Optional[datetime]
# =============================================================================
# Helper Functions
# =============================================================================
def get_type_label(letter_type: LetterType) -> str:
"""Gibt menschenlesbare Labels fuer Brieftypen zurueck."""
labels = {
LetterType.GENERAL: "Allgemeine Information",
LetterType.HALBJAHR: "Halbjahresinformation",
LetterType.FEHLZEITEN: "Fehlzeiten-Mitteilung",
LetterType.ELTERNABEND: "Einladung Elternabend",
LetterType.LOB: "Positives Feedback",
LetterType.CUSTOM: "Benutzerdefiniert",
}
return labels.get(letter_type, letter_type.value)
def get_tone_label(tone: LetterTone) -> str:
"""Gibt menschenlesbare Labels fuer Tonalitaeten zurueck."""
labels = {
LetterTone.FORMAL: "Sehr foermlich",
LetterTone.PROFESSIONAL: "Professionell-freundlich",
LetterTone.WARM: "Warmherzig",
LetterTone.CONCERNED: "Besorgt",
LetterTone.APPRECIATIVE: "Wertschaetzend",
}
return labels.get(tone, tone.value)