""" Klausur-Service Erwartungshorizont Models Data classes for BYOEH (Bring-Your-Own-Expectation-Horizon). """ from dataclasses import dataclass from datetime import datetime from typing import Optional, Dict, Any from .enums import EHStatus @dataclass class Erwartungshorizont: """An encrypted Erwartungshorizont (expectation horizon).""" id: str tenant_id: str teacher_id: str title: str subject: str niveau: str # 'eA' or 'gA' year: int aufgaben_nummer: Optional[str] encryption_key_hash: str salt: str encrypted_file_path: str file_size_bytes: int original_filename: str rights_confirmed: bool rights_confirmed_at: Optional[datetime] status: EHStatus chunk_count: int indexed_at: Optional[datetime] error_message: Optional[str] training_allowed: bool # ALWAYS FALSE created_at: datetime deleted_at: Optional[datetime] def to_dict(self) -> Dict[str, Any]: """Convert to dictionary for JSON serialization.""" status_value = self.status.value if hasattr(self.status, 'value') else self.status return { 'id': self.id, 'tenant_id': self.tenant_id, 'teacher_id': self.teacher_id, 'title': self.title, 'subject': self.subject, 'niveau': self.niveau, 'year': self.year, 'aufgaben_nummer': self.aufgaben_nummer, 'status': status_value, 'chunk_count': self.chunk_count, 'rights_confirmed': self.rights_confirmed, 'rights_confirmed_at': self.rights_confirmed_at.isoformat() if self.rights_confirmed_at else None, 'indexed_at': self.indexed_at.isoformat() if self.indexed_at else None, 'file_size_bytes': self.file_size_bytes, 'original_filename': self.original_filename, 'training_allowed': self.training_allowed, 'created_at': self.created_at.isoformat(), 'deleted_at': self.deleted_at.isoformat() if self.deleted_at else None } @dataclass class EHRightsConfirmation: """Rights confirmation for an Erwartungshorizont upload.""" id: str eh_id: str teacher_id: str confirmation_type: str # 'upload' | 'annual' confirmation_text: str ip_address: Optional[str] user_agent: Optional[str] confirmed_at: datetime def to_dict(self) -> Dict[str, Any]: """Convert to dictionary for JSON serialization.""" return { 'id': self.id, 'eh_id': self.eh_id, 'teacher_id': self.teacher_id, 'confirmation_type': self.confirmation_type, 'confirmation_text': self.confirmation_text, 'confirmed_at': self.confirmed_at.isoformat() } @dataclass class EHAuditLogEntry: """Audit log entry for EH operations.""" id: str eh_id: Optional[str] tenant_id: str user_id: str action: str # upload, index, rag_query, download, delete details: Optional[Dict] ip_address: Optional[str] user_agent: Optional[str] created_at: datetime def to_dict(self) -> Dict[str, Any]: """Convert to dictionary for JSON serialization.""" return { 'id': self.id, 'eh_id': self.eh_id, 'tenant_id': self.tenant_id, 'user_id': self.user_id, 'action': self.action, 'details': self.details, 'created_at': self.created_at.isoformat() } @dataclass class EHKeyShare: """Encrypted passphrase share for authorized users.""" id: str eh_id: str user_id: str encrypted_passphrase: str # Passphrase encrypted with recipient's public key passphrase_hint: str # Optional hint for the passphrase granted_by: str granted_at: datetime role: str # 'second_examiner', 'third_examiner', 'supervisor' klausur_id: Optional[str] # Link to specific Klausur if applicable active: bool def to_dict(self) -> Dict[str, Any]: """Convert to dictionary for JSON serialization.""" return { 'id': self.id, 'eh_id': self.eh_id, 'user_id': self.user_id, 'passphrase_hint': self.passphrase_hint, 'granted_by': self.granted_by, 'granted_at': self.granted_at.isoformat(), 'role': self.role, 'klausur_id': self.klausur_id, 'active': self.active } @dataclass class EHKlausurLink: """Link between an EH and a Klausur.""" id: str eh_id: str klausur_id: str linked_by: str linked_at: datetime def to_dict(self) -> Dict[str, Any]: """Convert to dictionary for JSON serialization.""" return { 'id': self.id, 'eh_id': self.eh_id, 'klausur_id': self.klausur_id, 'linked_by': self.linked_by, 'linked_at': self.linked_at.isoformat() } @dataclass class EHShareInvitation: """Invitation to share an EH with another user.""" id: str eh_id: str inviter_id: str # User who sent the invitation invitee_id: str # User receiving the invitation invitee_email: str # Email for notification role: str # Target role for the invitee klausur_id: Optional[str] # Optional link to specific Klausur message: Optional[str] # Optional message from inviter status: str # 'pending', 'accepted', 'declined', 'expired', 'revoked' expires_at: datetime # Invitation expiration created_at: datetime accepted_at: Optional[datetime] declined_at: Optional[datetime] def to_dict(self) -> Dict[str, Any]: """Convert to dictionary for JSON serialization.""" return { 'id': self.id, 'eh_id': self.eh_id, 'inviter_id': self.inviter_id, 'invitee_id': self.invitee_id, 'invitee_email': self.invitee_email, 'role': self.role, 'klausur_id': self.klausur_id, 'message': self.message, 'status': self.status, 'expires_at': self.expires_at.isoformat(), 'created_at': self.created_at.isoformat(), 'accepted_at': self.accepted_at.isoformat() if self.accepted_at else None, 'declined_at': self.declined_at.isoformat() if self.declined_at else None }