[split-required] Split final batch of monoliths >1000 LOC
Python (6 files in klausur-service): - rbac.py (1,132 → 4), admin_api.py (1,012 → 4) - routes/eh.py (1,111 → 4), ocr_pipeline_geometry.py (1,105 → 5) Python (2 files in backend-lehrer): - unit_api.py (1,226 → 6), game_api.py (1,129 → 5) Website (6 page files): - 4x klausur-korrektur pages (1,249-1,328 LOC each) → shared components in website/components/klausur-korrektur/ (17 shared files) - companion (1,057 → 10), magic-help (1,017 → 8) 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:
160
backend-lehrer/unit_content_routes.py
Normal file
160
backend-lehrer/unit_content_routes.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# ==============================================
|
||||
# Breakpilot Drive - Unit Content Generation Routes
|
||||
# ==============================================
|
||||
# API endpoints for H5P content, worksheets, and PDF generation.
|
||||
# Extracted from unit_api.py for file-size compliance.
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query, Depends
|
||||
from typing import Optional, Dict, Any
|
||||
import logging
|
||||
|
||||
from unit_models import UnitDefinitionResponse
|
||||
from unit_helpers import get_optional_current_user, get_unit_database
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/units", tags=["Breakpilot Units"])
|
||||
|
||||
|
||||
@router.get("/content/{unit_id}/h5p")
|
||||
async def generate_h5p_content(
|
||||
unit_id: str,
|
||||
locale: str = Query("de-DE", description="Target locale"),
|
||||
user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Generate H5P content items for a unit.
|
||||
|
||||
Returns H5P-compatible content structures for:
|
||||
- Drag and Drop (vocabulary matching)
|
||||
- Fill in the Blanks (concept texts)
|
||||
- Multiple Choice (misconception targeting)
|
||||
"""
|
||||
from content_generators import generate_h5p_for_unit, H5PGenerator, generate_h5p_manifest
|
||||
|
||||
# Get unit definition
|
||||
db = await get_unit_database()
|
||||
unit_def = None
|
||||
|
||||
if db:
|
||||
try:
|
||||
unit = await db.get_unit_definition(unit_id)
|
||||
if unit:
|
||||
unit_def = unit.get("definition", {})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get unit for H5P generation: {e}")
|
||||
|
||||
if not unit_def:
|
||||
raise HTTPException(status_code=404, detail=f"Unit not found: {unit_id}")
|
||||
|
||||
try:
|
||||
generator = H5PGenerator(locale=locale)
|
||||
contents = generator.generate_from_unit(unit_def)
|
||||
manifest = generate_h5p_manifest(contents, unit_id)
|
||||
|
||||
return {
|
||||
"unit_id": unit_id,
|
||||
"locale": locale,
|
||||
"generated_count": len(contents),
|
||||
"manifest": manifest,
|
||||
"contents": [c.to_h5p_structure() for c in contents]
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"H5P generation failed: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"H5P generation failed: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/content/{unit_id}/worksheet")
|
||||
async def generate_worksheet_html(
|
||||
unit_id: str,
|
||||
locale: str = Query("de-DE", description="Target locale"),
|
||||
user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Generate worksheet HTML for a unit.
|
||||
|
||||
Returns HTML that can be:
|
||||
- Displayed in browser
|
||||
- Converted to PDF using weasyprint
|
||||
- Printed directly
|
||||
"""
|
||||
from content_generators import PDFGenerator
|
||||
|
||||
# Get unit definition
|
||||
db = await get_unit_database()
|
||||
unit_def = None
|
||||
|
||||
if db:
|
||||
try:
|
||||
unit = await db.get_unit_definition(unit_id)
|
||||
if unit:
|
||||
unit_def = unit.get("definition", {})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get unit for worksheet generation: {e}")
|
||||
|
||||
if not unit_def:
|
||||
raise HTTPException(status_code=404, detail=f"Unit not found: {unit_id}")
|
||||
|
||||
try:
|
||||
generator = PDFGenerator(locale=locale)
|
||||
worksheet = generator.generate_from_unit(unit_def)
|
||||
|
||||
return {
|
||||
"unit_id": unit_id,
|
||||
"locale": locale,
|
||||
"title": worksheet.title,
|
||||
"sections": len(worksheet.sections),
|
||||
"html": worksheet.to_html()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Worksheet generation failed: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Worksheet generation failed: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/content/{unit_id}/worksheet.pdf")
|
||||
async def download_worksheet_pdf(
|
||||
unit_id: str,
|
||||
locale: str = Query("de-DE", description="Target locale"),
|
||||
user: Optional[Dict[str, Any]] = Depends(get_optional_current_user)
|
||||
):
|
||||
"""
|
||||
Generate and download worksheet as PDF.
|
||||
|
||||
Requires weasyprint to be installed on the server.
|
||||
"""
|
||||
from fastapi.responses import Response
|
||||
|
||||
# Get unit definition
|
||||
db = await get_unit_database()
|
||||
unit_def = None
|
||||
|
||||
if db:
|
||||
try:
|
||||
unit = await db.get_unit_definition(unit_id)
|
||||
if unit:
|
||||
unit_def = unit.get("definition", {})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get unit for PDF generation: {e}")
|
||||
|
||||
if not unit_def:
|
||||
raise HTTPException(status_code=404, detail=f"Unit not found: {unit_id}")
|
||||
|
||||
try:
|
||||
from content_generators import generate_worksheet_pdf
|
||||
pdf_bytes = generate_worksheet_pdf(unit_def, locale)
|
||||
|
||||
return Response(
|
||||
content=pdf_bytes,
|
||||
media_type="application/pdf",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{unit_id}_worksheet.pdf"'
|
||||
}
|
||||
)
|
||||
except ImportError:
|
||||
raise HTTPException(
|
||||
status_code=501,
|
||||
detail="PDF generation not available. Install weasyprint: pip install weasyprint"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"PDF generation failed: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"PDF generation failed: {str(e)}")
|
||||
Reference in New Issue
Block a user