173 lines
4.9 KiB
Python
173 lines
4.9 KiB
Python
"""Activity API endpoints."""
|
|
|
|
from __future__ import annotations
|
|
|
|
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,
|
|
)
|
|
from app.services.activity_service import (
|
|
complete_activity as svc_complete_activity,
|
|
)
|
|
from app.services.activity_service import (
|
|
create_activity as svc_create_activity,
|
|
)
|
|
from app.services.activity_service import (
|
|
get_activity as svc_get_activity,
|
|
)
|
|
from app.services.activity_service import (
|
|
list_activities as svc_list_activities,
|
|
)
|
|
from app.services.activity_service import (
|
|
soft_delete_activity as svc_soft_delete_activity,
|
|
)
|
|
from app.services.activity_service import (
|
|
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: ActivityType | None = None,
|
|
owner_id: int | None = None,
|
|
overdue: bool | None = None,
|
|
completed: bool | None = 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"]
|