Spaces:
Sleeping
Sleeping
| 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 | |
| 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 | |
| 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 | |
| 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 | |