This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

285 lines
8.7 KiB
Python

"""
Classroom API - Homework Routes
Homework tracking endpoints (Feature f20).
"""
from uuid import uuid4
from typing import Dict, Optional
from datetime import datetime
import logging
from fastapi import APIRouter, HTTPException, Query
from classroom_engine import (
Homework,
HomeworkStatus,
)
from ..models import (
CreateHomeworkRequest,
UpdateHomeworkRequest,
HomeworkResponse,
HomeworkListResponse,
)
from ..services.persistence import (
init_db_if_needed,
DB_ENABLED,
SessionLocal,
)
logger = logging.getLogger(__name__)
router = APIRouter(tags=["Homework"])
# In-Memory Storage fuer Hausaufgaben (Fallback)
_homework: Dict[str, Homework] = {}
def _build_homework_response(hw: Homework) -> HomeworkResponse:
"""Baut eine HomeworkResponse aus einem Homework-Objekt."""
is_overdue = False
if hw.due_date and hw.status != HomeworkStatus.COMPLETED:
is_overdue = hw.due_date < datetime.utcnow()
return HomeworkResponse(
homework_id=hw.homework_id,
teacher_id=hw.teacher_id,
class_id=hw.class_id,
subject=hw.subject,
title=hw.title,
description=hw.description,
session_id=hw.session_id,
due_date=hw.due_date.isoformat() if hw.due_date else None,
status=hw.status.value,
is_overdue=is_overdue,
created_at=hw.created_at.isoformat() if hw.created_at else None,
updated_at=hw.updated_at.isoformat() if hw.updated_at else None,
)
@router.post("/homework", response_model=HomeworkResponse, status_code=201)
async def create_homework(request: CreateHomeworkRequest) -> HomeworkResponse:
"""
Erstellt eine neue Hausaufgabe (Feature f20).
Kann mit einer Session verknuepft werden.
"""
init_db_if_needed()
due_date = None
if request.due_date:
try:
due_date = datetime.fromisoformat(request.due_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Ungueltiges Datumsformat")
homework = Homework(
homework_id=str(uuid4()),
teacher_id=request.teacher_id,
class_id=request.class_id,
subject=request.subject,
title=request.title,
description=request.description,
session_id=request.session_id,
due_date=due_date,
status=HomeworkStatus.ASSIGNED,
created_at=datetime.utcnow(),
)
# Persistieren wenn DB verfuegbar
if DB_ENABLED:
try:
from classroom_engine.repository import HomeworkRepository
db = SessionLocal()
repo = HomeworkRepository(db)
repo.create(homework)
db.close()
except Exception as e:
logger.warning(f"DB persist failed for homework: {e}")
_homework[homework.homework_id] = homework
return _build_homework_response(homework)
@router.get("/homework", response_model=HomeworkListResponse)
async def list_homework(
teacher_id: str = Query(None, description="Filter nach Lehrer"),
class_id: str = Query(None, description="Filter nach Klasse"),
status: Optional[str] = Query(None, description="Filter nach Status: assigned, in_progress, completed"),
limit: int = Query(50, ge=1, le=100)
) -> HomeworkListResponse:
"""
Listet Hausaufgaben mit optionalen Filtern (Feature f20).
"""
init_db_if_needed()
homework_list = []
# Aus DB laden wenn verfuegbar
if DB_ENABLED:
try:
from classroom_engine.repository import HomeworkRepository
db = SessionLocal()
repo = HomeworkRepository(db)
db_homework = repo.get_by_filters(
teacher_id=teacher_id,
class_id=class_id,
status=status,
limit=limit
)
for db_hw in db_homework:
hw = repo.to_dataclass(db_hw)
_homework[hw.homework_id] = hw
homework_list.append(_build_homework_response(hw))
db.close()
return HomeworkListResponse(homework=homework_list, total=len(homework_list))
except Exception as e:
logger.warning(f"DB read failed for homework: {e}")
# Fallback auf Memory
for hw in _homework.values():
if teacher_id and hw.teacher_id != teacher_id:
continue
if class_id and hw.class_id != class_id:
continue
if status:
try:
filter_status = HomeworkStatus(status)
if hw.status != filter_status:
continue
except ValueError:
pass
homework_list.append(_build_homework_response(hw))
return HomeworkListResponse(homework=homework_list[:limit], total=len(homework_list))
@router.get("/homework/{homework_id}", response_model=HomeworkResponse)
async def get_homework(homework_id: str) -> HomeworkResponse:
"""
Ruft eine einzelne Hausaufgabe ab (Feature f20).
"""
init_db_if_needed()
# Aus Memory
if homework_id in _homework:
return _build_homework_response(_homework[homework_id])
# Aus DB laden
if DB_ENABLED:
try:
from classroom_engine.repository import HomeworkRepository
db = SessionLocal()
repo = HomeworkRepository(db)
db_hw = repo.get_by_id(homework_id)
db.close()
if db_hw:
hw = repo.to_dataclass(db_hw)
_homework[hw.homework_id] = hw
return _build_homework_response(hw)
except Exception as e:
logger.warning(f"DB read failed: {e}")
raise HTTPException(status_code=404, detail="Hausaufgabe nicht gefunden")
@router.put("/homework/{homework_id}", response_model=HomeworkResponse)
async def update_homework(
homework_id: str,
request: UpdateHomeworkRequest
) -> HomeworkResponse:
"""
Aktualisiert eine Hausaufgabe (Feature f20).
"""
init_db_if_needed()
# Aus Memory holen
homework = _homework.get(homework_id)
# Aus DB laden wenn nicht in Memory
if not homework and DB_ENABLED:
try:
from classroom_engine.repository import HomeworkRepository
db = SessionLocal()
repo = HomeworkRepository(db)
db_hw = repo.get_by_id(homework_id)
db.close()
if db_hw:
homework = repo.to_dataclass(db_hw)
_homework[homework.homework_id] = homework
except Exception as e:
logger.warning(f"DB read failed: {e}")
if not homework:
raise HTTPException(status_code=404, detail="Hausaufgabe nicht gefunden")
# Aktualisieren
if request.title is not None:
homework.title = request.title
if request.description is not None:
homework.description = request.description
if request.due_date is not None:
try:
homework.due_date = datetime.fromisoformat(request.due_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Ungueltiges Datumsformat")
if request.status is not None:
try:
homework.status = HomeworkStatus(request.status)
except ValueError:
raise HTTPException(status_code=400, detail="Ungueltiger Status")
homework.updated_at = datetime.utcnow()
# In DB aktualisieren
if DB_ENABLED:
try:
from classroom_engine.repository import HomeworkRepository
db = SessionLocal()
repo = HomeworkRepository(db)
repo.update(homework)
db.close()
except Exception as e:
logger.warning(f"DB update failed: {e}")
_homework[homework_id] = homework
return _build_homework_response(homework)
@router.patch("/homework/{homework_id}/status")
async def update_homework_status(
homework_id: str,
status: str = Query(..., description="Neuer Status: assigned, in_progress, completed")
) -> HomeworkResponse:
"""
Aktualisiert nur den Status einer Hausaufgabe (Feature f20).
"""
return await update_homework(homework_id, UpdateHomeworkRequest(status=status))
@router.delete("/homework/{homework_id}")
async def delete_homework(homework_id: str) -> Dict[str, str]:
"""
Loescht eine Hausaufgabe (Feature f20).
"""
init_db_if_needed()
if homework_id in _homework:
del _homework[homework_id]
if DB_ENABLED:
try:
from classroom_engine.repository import HomeworkRepository
db = SessionLocal()
repo = HomeworkRepository(db)
repo.delete(homework_id)
db.close()
except Exception as e:
logger.warning(f"DB delete failed: {e}")
return {"status": "deleted", "homework_id": homework_id}