study-buddy / app /routers /visual_lessons.py
GitHub Actions
deploy d092bea3608b7a29952f16357fda39b7a29e399b
2e818da
Raw
History Blame Contribute Delete
3.5 kB
from fastapi import APIRouter, HTTPException
from fastapi.responses import FileResponse
from app.schemas.composition import CheckpointUpdate, CompositionSpec, MolecularPanelSpec, RuntimeCheckpoint
from app.schemas.visual_lesson import LessonPayload, NucleicAcidSpec, ProteinLessonSpec
from app.services.visual_lesson_service import VisualLessonService
from app.services.visual_lesson_store import CheckpointConflictError, LessonNotFoundError, LessonVersionError
router = APIRouter(prefix="/api/visual-lessons", tags=["visual-lessons"])
_service: VisualLessonService | None = None
def get_visual_lesson_service() -> VisualLessonService:
global _service
if _service is None:
_service = VisualLessonService()
return _service
@router.get("/{project_id}/{lesson_id}", response_model=LessonPayload)
def get_visual_lesson(project_id: str, lesson_id: str) -> LessonPayload:
try:
return get_visual_lesson_service().load_payload(project_id, lesson_id)
except LessonNotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
except LessonVersionError as exc:
raise HTTPException(status_code=409, detail=str(exc)) from exc
@router.put("/{project_id}/{lesson_id}/checkpoint", response_model=RuntimeCheckpoint)
def put_visual_lesson_checkpoint(project_id: str, lesson_id: str, update: CheckpointUpdate) -> RuntimeCheckpoint:
service = get_visual_lesson_service()
try:
return service.save_checkpoint(project_id, lesson_id, update)
except LessonNotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc
except CheckpointConflictError as exc:
raise HTTPException(status_code=409, detail=str(exc)) from exc
except (LessonVersionError, ValueError) as exc:
raise HTTPException(status_code=422, detail=str(exc)) from exc
@router.get("/{project_id}/{lesson_id}/assets/{asset_id}")
def get_visual_lesson_asset(project_id: str, lesson_id: str, asset_id: str) -> FileResponse:
service = get_visual_lesson_service()
try:
spec = service.store.load(project_id, lesson_id)
structure = spec.structure if isinstance(spec, (ProteinLessonSpec, NucleicAcidSpec)) else None
if isinstance(spec, CompositionSpec):
molecular = next((panel for panel in spec.panels if isinstance(panel, MolecularPanelSpec) and panel.asset_id == asset_id), None)
if molecular is None:
raise LessonNotFoundError("Molecular structure asset not found")
coordinate_format = molecular.coordinate_format
path = service.store.asset_path(project_id, asset_id, coordinate_format)
media_type = "application/octet-stream" if coordinate_format == "bcif" else "chemical/x-mmcif"
return FileResponse(path, media_type=media_type, filename=f"{asset_id}.{coordinate_format}")
if structure is None or structure.coordinate_asset_id != asset_id:
raise LessonNotFoundError("Molecular structure asset not found")
coordinate_format = structure.coordinate_format
path = service.store.asset_path(project_id, asset_id, coordinate_format)
media_type = "application/octet-stream" if coordinate_format == "bcif" else "chemical/x-mmcif"
return FileResponse(path, media_type=media_type, filename=f"{asset_id}.{coordinate_format}")
except LessonNotFoundError as exc:
raise HTTPException(status_code=404, detail=str(exc)) from exc