[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>
This commit is contained in:
382
backend-lehrer/classroom_engine/repository_homework.py
Normal file
382
backend-lehrer/classroom_engine/repository_homework.py
Normal file
@@ -0,0 +1,382 @@
|
||||
"""
|
||||
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,
|
||||
)
|
||||
Reference in New Issue
Block a user