From a5add4977bcf198e57d0cd303936e997b70fcb58 Mon Sep 17 00:00:00 2001 From: Leopoldadmin Date: Wed, 3 Jun 2026 23:52:11 +0000 Subject: [PATCH] Upload app/api/v1/activities.py --- app/api/v1/activities.py | 162 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 app/api/v1/activities.py diff --git a/app/api/v1/activities.py b/app/api/v1/activities.py new file mode 100644 index 0000000..b456618 --- /dev/null +++ b/app/api/v1/activities.py @@ -0,0 +1,162 @@ +"""Activity API endpoints.""" + +from __future__ import annotations + +from typing import Optional + +from fastapi import APIRouter, Depends, HTTPException, Query, Response, status +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.db import get_db +from app.core.deps import get_current_user +from app.models.activity import ActivityType +from app.models.user import User +from app.schemas.activity import ( + ActivityCompleteRequest, + ActivityCreate, + ActivityOut, + ActivityUpdate, +) +from app.services.activity_service import ( + NoParentException, + complete_activity as svc_complete_activity, + create_activity as svc_create_activity, + get_activity as svc_get_activity, + list_activities as svc_list_activities, + soft_delete_activity as svc_soft_delete_activity, + update_activity as svc_update_activity, +) + +router = APIRouter(prefix="/activities", tags=["activities"]) + + +@router.post( + "/", + response_model=ActivityOut, + status_code=status.HTTP_201_CREATED, + summary="Create a new activity (polymorphic parent: account/contact/deal)", +) +async def create_activity( + payload: ActivityCreate, + current_user: User = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +) -> ActivityOut: + try: + activity = await svc_create_activity( + db, payload, org_id=current_user.org_id, owner_id=current_user.id + ) + except NoParentException as e: + raise HTTPException(status_code=422, detail=str(e)) from e + return ActivityOut.model_validate(activity) + + +@router.get( + "/", + response_model=list[ActivityOut], + summary="List activities (paginated, filterable)", +) +async def list_activities( + skip: int = Query(0, ge=0), + limit: int = Query(20, ge=1, le=100), + type: Optional[ActivityType] = None, + owner_id: Optional[int] = None, + overdue: Optional[bool] = None, + completed: Optional[bool] = None, + current_user: User = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +) -> list[ActivityOut]: + activities = await svc_list_activities( + db, + org_id=current_user.org_id, + skip=skip, + limit=limit, + type=type, + owner_id=owner_id, + overdue=overdue, + completed=completed, + ) + return [ActivityOut.model_validate(a) for a in activities] + + +@router.get( + "/{activity_id}", + response_model=ActivityOut, + summary="Get one activity", +) +async def get_activity( + activity_id: int, + current_user: User = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +) -> ActivityOut: + activity = await svc_get_activity( + db, activity_id, org_id=current_user.org_id + ) + if activity is None: + raise HTTPException(status_code=404, detail="Activity not found") + return ActivityOut.model_validate(activity) + + +@router.patch( + "/{activity_id}", + response_model=ActivityOut, + summary="Update an activity", +) +async def update_activity( + activity_id: int, + payload: ActivityUpdate, + current_user: User = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +) -> ActivityOut: + activity = await svc_get_activity( + db, activity_id, org_id=current_user.org_id + ) + if activity is None: + raise HTTPException(status_code=404, detail="Activity not found") + try: + updated = await svc_update_activity(db, activity, payload) + except NoParentException as e: + raise HTTPException(status_code=422, detail=str(e)) from e + return ActivityOut.model_validate(updated) + + +@router.patch( + "/{activity_id}/complete", + response_model=ActivityOut, + summary="Mark an activity as completed", +) +async def complete_activity( + activity_id: int, + payload: ActivityCompleteRequest, + current_user: User = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +) -> ActivityOut: + activity = await svc_get_activity( + db, activity_id, org_id=current_user.org_id + ) + if activity is None: + raise HTTPException(status_code=404, detail="Activity not found") + updated = await svc_complete_activity(db, activity, payload) + return ActivityOut.model_validate(updated) + + +@router.delete( + "/{activity_id}", + status_code=status.HTTP_204_NO_CONTENT, + response_class=Response, + summary="Soft-delete an activity", +) +async def delete_activity( + activity_id: int, + current_user: User = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +) -> Response: + activity = await svc_get_activity( + db, activity_id, org_id=current_user.org_id + ) + if activity is None: + raise HTTPException(status_code=404, detail="Activity not found") + await svc_soft_delete_activity(db, activity) + return Response(status_code=status.HTTP_204_NO_CONTENT) + + +__all__ = ["router"]