""" Homework & Material Repositories. CRUD-Operationen fuer Hausaufgaben (Feature f20) und Phasen-Materialien (Feature f19). """ from datetime import datetime from typing import Optional, List from sqlalchemy.orm import Session as DBSession from .db_models import ( HomeworkDB, HomeworkStatusEnum, PhaseMaterialDB, MaterialTypeEnum, ) from .models import ( Homework, HomeworkStatus, PhaseMaterial, MaterialType, ) class HomeworkRepository: """Repository fuer Hausaufgaben-Tracking (Feature f20).""" def __init__(self, db: DBSession): self.db = db # ==================== CREATE ==================== def create(self, homework: Homework) -> HomeworkDB: """Erstellt eine neue Hausaufgabe.""" db_homework = HomeworkDB( id=homework.homework_id, teacher_id=homework.teacher_id, class_id=homework.class_id, subject=homework.subject, title=homework.title, description=homework.description, session_id=homework.session_id, due_date=homework.due_date, status=HomeworkStatusEnum(homework.status.value), ) self.db.add(db_homework) self.db.commit() self.db.refresh(db_homework) return db_homework # ==================== READ ==================== def get_by_id(self, homework_id: str) -> Optional[HomeworkDB]: """Holt eine Hausaufgabe nach ID.""" return self.db.query(HomeworkDB).filter( HomeworkDB.id == homework_id ).first() def get_by_teacher( self, teacher_id: str, status: Optional[str] = None, limit: int = 50 ) -> List[HomeworkDB]: """Holt alle Hausaufgaben eines Lehrers.""" query = self.db.query(HomeworkDB).filter( HomeworkDB.teacher_id == teacher_id ) if status: query = query.filter(HomeworkDB.status == HomeworkStatusEnum(status)) return query.order_by( HomeworkDB.due_date.asc().nullslast(), HomeworkDB.created_at.desc() ).limit(limit).all() def get_by_class( self, class_id: str, teacher_id: str, include_completed: bool = False, limit: int = 20 ) -> List[HomeworkDB]: """Holt alle Hausaufgaben einer Klasse.""" query = self.db.query(HomeworkDB).filter( HomeworkDB.class_id == class_id, HomeworkDB.teacher_id == teacher_id ) if not include_completed: query = query.filter(HomeworkDB.status != HomeworkStatusEnum.COMPLETED) return query.order_by( HomeworkDB.due_date.asc().nullslast(), HomeworkDB.created_at.desc() ).limit(limit).all() def get_by_session(self, session_id: str) -> List[HomeworkDB]: """Holt alle Hausaufgaben einer Session.""" return self.db.query(HomeworkDB).filter( HomeworkDB.session_id == session_id ).order_by(HomeworkDB.created_at.desc()).all() def get_pending( self, teacher_id: str, days_ahead: int = 7 ) -> List[HomeworkDB]: """Holt anstehende Hausaufgaben der naechsten X Tage.""" from datetime import timedelta cutoff = datetime.utcnow() + timedelta(days=days_ahead) return self.db.query(HomeworkDB).filter( HomeworkDB.teacher_id == teacher_id, HomeworkDB.status.in_([HomeworkStatusEnum.ASSIGNED, HomeworkStatusEnum.IN_PROGRESS]), HomeworkDB.due_date <= cutoff ).order_by(HomeworkDB.due_date.asc()).all() # ==================== UPDATE ==================== def update_status( self, homework_id: str, status: HomeworkStatus ) -> Optional[HomeworkDB]: """Aktualisiert den Status einer Hausaufgabe.""" db_homework = self.get_by_id(homework_id) if not db_homework: return None db_homework.status = HomeworkStatusEnum(status.value) self.db.commit() self.db.refresh(db_homework) return db_homework def update(self, homework: Homework) -> Optional[HomeworkDB]: """Aktualisiert eine Hausaufgabe.""" db_homework = self.get_by_id(homework.homework_id) if not db_homework: return None db_homework.title = homework.title db_homework.description = homework.description db_homework.due_date = homework.due_date db_homework.status = HomeworkStatusEnum(homework.status.value) self.db.commit() self.db.refresh(db_homework) return db_homework # ==================== DELETE ==================== def delete(self, homework_id: str) -> bool: """Loescht eine Hausaufgabe.""" db_homework = self.get_by_id(homework_id) if not db_homework: return False self.db.delete(db_homework) self.db.commit() return True # ==================== CONVERSION ==================== def to_dataclass(self, db_homework: HomeworkDB) -> Homework: """Konvertiert DB-Model zu Dataclass.""" return Homework( homework_id=db_homework.id, teacher_id=db_homework.teacher_id, class_id=db_homework.class_id, subject=db_homework.subject, title=db_homework.title, description=db_homework.description or "", session_id=db_homework.session_id, due_date=db_homework.due_date, status=HomeworkStatus(db_homework.status.value), created_at=db_homework.created_at, updated_at=db_homework.updated_at, ) class MaterialRepository: """Repository fuer Phasen-Materialien (Feature f19).""" def __init__(self, db: DBSession): self.db = db # ==================== CREATE ==================== def create(self, material: PhaseMaterial) -> PhaseMaterialDB: """Erstellt ein neues Material.""" db_material = PhaseMaterialDB( id=material.material_id, teacher_id=material.teacher_id, title=material.title, material_type=MaterialTypeEnum(material.material_type.value), url=material.url, description=material.description, phase=material.phase, subject=material.subject, grade_level=material.grade_level, tags=material.tags, is_public=material.is_public, usage_count=material.usage_count, session_id=material.session_id, ) self.db.add(db_material) self.db.commit() self.db.refresh(db_material) return db_material # ==================== READ ==================== def get_by_id(self, material_id: str) -> Optional[PhaseMaterialDB]: """Holt ein Material nach ID.""" return self.db.query(PhaseMaterialDB).filter( PhaseMaterialDB.id == material_id ).first() def get_by_teacher( self, teacher_id: str, phase: Optional[str] = None, subject: Optional[str] = None, limit: int = 50 ) -> List[PhaseMaterialDB]: """Holt alle Materialien eines Lehrers.""" query = self.db.query(PhaseMaterialDB).filter( PhaseMaterialDB.teacher_id == teacher_id ) if phase: query = query.filter(PhaseMaterialDB.phase == phase) if subject: query = query.filter(PhaseMaterialDB.subject == subject) return query.order_by( PhaseMaterialDB.usage_count.desc(), PhaseMaterialDB.created_at.desc() ).limit(limit).all() def get_by_phase( self, phase: str, teacher_id: str, include_public: bool = True ) -> List[PhaseMaterialDB]: """Holt alle Materialien fuer eine bestimmte Phase.""" if include_public: return self.db.query(PhaseMaterialDB).filter( PhaseMaterialDB.phase == phase, (PhaseMaterialDB.teacher_id == teacher_id) | (PhaseMaterialDB.is_public == True) ).order_by( PhaseMaterialDB.usage_count.desc() ).all() else: return self.db.query(PhaseMaterialDB).filter( PhaseMaterialDB.phase == phase, PhaseMaterialDB.teacher_id == teacher_id ).order_by( PhaseMaterialDB.created_at.desc() ).all() def get_by_session(self, session_id: str) -> List[PhaseMaterialDB]: """Holt alle Materialien einer Session.""" return self.db.query(PhaseMaterialDB).filter( PhaseMaterialDB.session_id == session_id ).order_by(PhaseMaterialDB.phase, PhaseMaterialDB.created_at).all() def get_public_materials( self, phase: Optional[str] = None, subject: Optional[str] = None, limit: int = 20 ) -> List[PhaseMaterialDB]: """Holt oeffentliche Materialien.""" query = self.db.query(PhaseMaterialDB).filter( PhaseMaterialDB.is_public == True ) if phase: query = query.filter(PhaseMaterialDB.phase == phase) if subject: query = query.filter(PhaseMaterialDB.subject == subject) return query.order_by( PhaseMaterialDB.usage_count.desc() ).limit(limit).all() def search_by_tags( self, tags: List[str], teacher_id: Optional[str] = None ) -> List[PhaseMaterialDB]: """Sucht Materialien nach Tags.""" query = self.db.query(PhaseMaterialDB) if teacher_id: query = query.filter( (PhaseMaterialDB.teacher_id == teacher_id) | (PhaseMaterialDB.is_public == True) ) else: query = query.filter(PhaseMaterialDB.is_public == True) # Filter by tags - vereinfachte Implementierung results = [] for material in query.all(): if material.tags and any(tag in material.tags for tag in tags): results.append(material) return results[:50] # ==================== UPDATE ==================== def update(self, material: PhaseMaterial) -> Optional[PhaseMaterialDB]: """Aktualisiert ein Material.""" db_material = self.get_by_id(material.material_id) if not db_material: return None db_material.title = material.title db_material.material_type = MaterialTypeEnum(material.material_type.value) db_material.url = material.url db_material.description = material.description db_material.phase = material.phase db_material.subject = material.subject db_material.grade_level = material.grade_level db_material.tags = material.tags db_material.is_public = material.is_public self.db.commit() self.db.refresh(db_material) return db_material def increment_usage(self, material_id: str) -> Optional[PhaseMaterialDB]: """Erhoeht den Usage-Counter eines Materials.""" db_material = self.get_by_id(material_id) if not db_material: return None db_material.usage_count += 1 self.db.commit() self.db.refresh(db_material) return db_material def attach_to_session( self, material_id: str, session_id: str ) -> Optional[PhaseMaterialDB]: """Verknuepft ein Material mit einer Session.""" db_material = self.get_by_id(material_id) if not db_material: return None db_material.session_id = session_id db_material.usage_count += 1 self.db.commit() self.db.refresh(db_material) return db_material # ==================== DELETE ==================== def delete(self, material_id: str) -> bool: """Loescht ein Material.""" db_material = self.get_by_id(material_id) if not db_material: return False self.db.delete(db_material) self.db.commit() return True # ==================== CONVERSION ==================== def to_dataclass(self, db_material: PhaseMaterialDB) -> PhaseMaterial: """Konvertiert DB-Model zu Dataclass.""" return PhaseMaterial( material_id=db_material.id, teacher_id=db_material.teacher_id, title=db_material.title, material_type=MaterialType(db_material.material_type.value), url=db_material.url, description=db_material.description or "", phase=db_material.phase, subject=db_material.subject or "", grade_level=db_material.grade_level or "", tags=db_material.tags or [], is_public=db_material.is_public, usage_count=db_material.usage_count, session_id=db_material.session_id, created_at=db_material.created_at, updated_at=db_material.updated_at, )