Files
breakpilot-lehrer/backend-lehrer/classroom_engine/repository_homework.py
Benjamin Admin b2a0126f14 [split-required] Split remaining Python monoliths (Phase 1 continued)
klausur-service (7 monoliths):
- grid_editor_helpers.py (1,737 → 5 files: columns, filters, headers, zones)
- cv_cell_grid.py (1,675 → 7 files: build, legacy, streaming, merge, vocab)
- worksheet_editor_api.py (1,305 → 4 files: models, AI, reconstruct, routes)
- legal_corpus_ingestion.py (1,280 → 3 files: registry, chunking, ingestion)
- cv_review.py (1,248 → 4 files: pipeline, spell, LLM, barrel)
- cv_preprocessing.py (1,166 → 3 files: deskew, dewarp, barrel)
- rbac.py, admin_api.py, routes/eh.py remain (next batch)

backend-lehrer (1 monolith):
- classroom_engine/repository.py (1,705 → 7 files by domain)

All re-export barrels preserve backward compatibility.
Zero import errors verified.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 22:47:59 +02:00

383 lines
13 KiB
Python

"""
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,
)