diff --git a/api/src/rehearsalhub/main.py b/api/src/rehearsalhub/main.py index 855498f..3ac4f34 100644 --- a/api/src/rehearsalhub/main.py +++ b/api/src/rehearsalhub/main.py @@ -12,6 +12,7 @@ from rehearsalhub.routers import ( bands_router, internal_router, members_router, + sessions_router, songs_router, versions_router, ws_router, @@ -51,6 +52,7 @@ def create_app() -> FastAPI: prefix = "/api/v1" app.include_router(auth_router, prefix=prefix) app.include_router(bands_router, prefix=prefix) + app.include_router(sessions_router, prefix=prefix) app.include_router(songs_router, prefix=prefix) app.include_router(versions_router, prefix=prefix) app.include_router(annotations_router, prefix=prefix) diff --git a/api/src/rehearsalhub/routers/__init__.py b/api/src/rehearsalhub/routers/__init__.py index c6f72a2..cb2773a 100644 --- a/api/src/rehearsalhub/routers/__init__.py +++ b/api/src/rehearsalhub/routers/__init__.py @@ -3,6 +3,7 @@ from rehearsalhub.routers.auth import router as auth_router from rehearsalhub.routers.bands import router as bands_router from rehearsalhub.routers.internal import router as internal_router from rehearsalhub.routers.members import router as members_router +from rehearsalhub.routers.sessions import router as sessions_router from rehearsalhub.routers.songs import router as songs_router from rehearsalhub.routers.versions import router as versions_router from rehearsalhub.routers.ws import router as ws_router @@ -12,6 +13,7 @@ __all__ = [ "bands_router", "internal_router", "members_router", + "sessions_router", "songs_router", "versions_router", "annotations_router", diff --git a/api/src/rehearsalhub/routers/sessions.py b/api/src/rehearsalhub/routers/sessions.py new file mode 100644 index 0000000..d550d81 --- /dev/null +++ b/api/src/rehearsalhub/routers/sessions.py @@ -0,0 +1,92 @@ +import uuid + +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.ext.asyncio import AsyncSession + +from rehearsalhub.db.engine import get_session +from rehearsalhub.db.models import Member +from rehearsalhub.dependencies import get_current_member +from rehearsalhub.repositories.band import BandRepository +from rehearsalhub.repositories.rehearsal_session import RehearsalSessionRepository +from rehearsalhub.schemas.rehearsal_session import ( + RehearsalSessionDetail, + RehearsalSessionRead, + RehearsalSessionUpdate, +) +from rehearsalhub.schemas.song import SongRead +from rehearsalhub.services.band import BandService + +router = APIRouter(prefix="/bands", tags=["sessions"]) + + +@router.get("/{band_id}/sessions", response_model=list[RehearsalSessionRead]) +async def list_sessions( + band_id: uuid.UUID, + session: AsyncSession = Depends(get_session), + current_member: Member = Depends(get_current_member), +): + svc = BandService(session) + try: + await svc.assert_membership(band_id, current_member.id) + except PermissionError: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a member") + + repo = RehearsalSessionRepository(session) + rows = await repo.list_for_band(band_id) + return [ + RehearsalSessionRead.model_validate(s, update={"recording_count": count}) + for s, count in rows + ] + + +@router.get("/{band_id}/sessions/{session_id}", response_model=RehearsalSessionDetail) +async def get_session_detail( + band_id: uuid.UUID, + session_id: uuid.UUID, + session: AsyncSession = Depends(get_session), + current_member: Member = Depends(get_current_member), +): + svc = BandService(session) + try: + await svc.assert_membership(band_id, current_member.id) + except PermissionError: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a member") + + repo = RehearsalSessionRepository(session) + rehearsal = await repo.get_with_songs(session_id) + if rehearsal is None or rehearsal.band_id != band_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found") + + songs = [ + SongRead.model_validate(s, update={"version_count": len(s.versions)}) + for s in rehearsal.songs + ] + return RehearsalSessionDetail.model_validate( + rehearsal, + update={"recording_count": len(songs), "songs": songs}, + ) + + +@router.patch("/{band_id}/sessions/{session_id}", response_model=RehearsalSessionRead) +async def update_session( + band_id: uuid.UUID, + session_id: uuid.UUID, + data: RehearsalSessionUpdate, + session: AsyncSession = Depends(get_session), + current_member: Member = Depends(get_current_member), +): + band_repo = BandRepository(session) + role = await band_repo.get_member_role(band_id, current_member.id) + if role != "admin": + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin only") + + repo = RehearsalSessionRepository(session) + rehearsal = await repo.get_by_id(session_id) + if rehearsal is None or rehearsal.band_id != band_id: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found") + + updates = {k: v for k, v in data.model_dump().items() if v is not None} + if updates: + rehearsal = await repo.update(rehearsal, **updates) + + return RehearsalSessionRead.model_validate(rehearsal, update={"recording_count": 0})