from datetime import timedelta from uuid import UUID from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import delete, select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession from app.api import deps from app.models import Project, Slot, Sms, User from app.schemas.requests import ( ProjectImportGsheetRequest, ProjectRequest, ProjectSMSBatchRequest, ) from app.schemas.responses import ProjectListResponse, ProjectResponse, SMSResponse router = APIRouter() @router.get("/projects", response_model=list[ProjectListResponse]) async def list_project( current_user: User = Depends(deps.get_current_user), session: AsyncSession = Depends(deps.get_session), ): """Get project_list""" results = await session.execute(select(Project)) return results.scalars().all() @router.get("/public-projects", response_model=list[ProjectListResponse]) async def list_public_project( session: AsyncSession = Depends(deps.get_session), ): """Get the list of public projects""" results = await session.execute(select(Project).where(Project.is_public == True)) return results.scalars().all() @router.post("/project", response_model=ProjectResponse) async def create_project( new_project: ProjectRequest, current_user: User = Depends(deps.get_current_user), session: AsyncSession = Depends(deps.get_session), ): """Create a new project""" project = Project(**new_project.dict()) session.add(project) try: await session.commit() except IntegrityError as e: raise HTTPException(422, "Project name already exist") await session.refresh(project) return ProjectResponse.from_orm(project) @router.get("/public-project/{id}", response_model=ProjectResponse) async def get_public_project( id: UUID, session: AsyncSession = Depends(deps.get_session), ): """Get a project that is public""" result = await session.get(Project, id) if (result is None) or not result.is_public: return HTTPException(status_code=404, detail="Project not found") return result @router.get("/project/{id}", response_model=ProjectResponse) async def get_project( id: UUID, session: AsyncSession = Depends(deps.get_session), ): """Get a project""" project = await session.get(Project, id) if project is None: return HTTPException(status_code=404, detail="Project not found") project.sms project.volunteers project.slots response = ProjectResponse.from_orm(project) return response @router.post("/project/{id}", response_model=ProjectListResponse) async def update_project( id: UUID, edit_project: ProjectRequest, current_user: User = Depends(deps.get_current_user), session: AsyncSession = Depends(deps.get_session), ): """Edit project""" p = await session.get(Project, id) if p is None: raise HTTPException(status_code=404, detail="Project not found") p.name = edit_project.name p.is_public = edit_project.is_public await session.commit() return p @router.post("/project/{id}/import-gsheet", response_model=ProjectResponse) async def update_project_from_gsheet( id: UUID, gsheet: ProjectImportGsheetRequest, current_user: User = Depends(deps.get_current_user), session: AsyncSession = Depends(deps.get_session), ): """Edit project name""" p = await session.get(Project, id) if p is None: raise HTTPException(status_code=404, detail="Project not found") url = gsheet.sheet_url # TODO implement feature to import return p @router.post("/project/{id}/create-all-sms", response_model=list[SMSResponse]) async def create_sms_batch( id: UUID, sms_batch: ProjectSMSBatchRequest, current_user: User = Depends(deps.get_current_user), session: AsyncSession = Depends(deps.get_session), ): """Create SMS based on a template and the list of slots and volunteer associated to the project The placeholder that can be used in the template are - {titre} slot.title - {description} slot.description - {debut} slot.starting_time - {fin} slot.ending_ting - {prenom} volunteer.name - {name} volunteer.surname """ p = await session.get(Project, id) if p is None: raise HTTPException(status_code=404, detail="Project not found") # Get all slots slots = await session.execute(select(Slot).where(Slot.project_id == id)) sms_list = [] for slot in slots.scalars(): # Replace the slot placeholder by their value slot_content = ( sms_batch.template.replace("{titre}", slot.title) .replace("{description}", slot.description) .replace("{debut}", str(slot.starting_time)) .replace("{fin}", str(slot.ending_time)) ) sending_time = slot.starting_time - timedelta(minutes=sms_batch.delta_t) for volunteer in slot.volunteers: # Create a new SMS customized for each user attache to the slot personalized_content = slot_content.replace( "{prenom}", volunteer.name ).replace("{nom}", volunteer.surname) sms = Sms( project_id=id, volunteer_id=volunteer.id, content=personalized_content, phone_number=volunteer.phone_number, sending_time=sending_time, ) sms_list.append(sms) session.add_all(sms_list) await session.commit() return sms_list @router.delete("/project/{id}") async def delete_project( id: UUID, current_user: User = Depends(deps.get_current_user), session: AsyncSession = Depends(deps.get_session), ): """Delete project""" await session.execute(delete(Project).where(Project.id == id)) await session.commit()