209 lines
6.3 KiB
Python
209 lines
6.3 KiB
Python
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import selectinload
|
|
from typing import List
|
|
|
|
from app.database import get_db
|
|
from app.models.project import Project
|
|
from app.models.project_task import ProjectTask
|
|
from app.schemas.project import ProjectCreate, ProjectUpdate, ProjectResponse
|
|
from app.schemas.project_task import ProjectTaskCreate, ProjectTaskUpdate, ProjectTaskResponse
|
|
from app.routers.auth import get_current_session
|
|
from app.models.settings import Settings
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/", response_model=List[ProjectResponse])
|
|
async def get_projects(
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Get all projects with their tasks."""
|
|
query = select(Project).options(selectinload(Project.tasks)).order_by(Project.created_at.desc())
|
|
result = await db.execute(query)
|
|
projects = result.scalars().all()
|
|
|
|
return projects
|
|
|
|
|
|
@router.post("/", response_model=ProjectResponse, status_code=201)
|
|
async def create_project(
|
|
project: ProjectCreate,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Create a new project."""
|
|
new_project = Project(**project.model_dump())
|
|
db.add(new_project)
|
|
await db.commit()
|
|
|
|
# Re-fetch with eagerly loaded tasks for response serialization
|
|
query = select(Project).options(selectinload(Project.tasks)).where(Project.id == new_project.id)
|
|
result = await db.execute(query)
|
|
return result.scalar_one()
|
|
|
|
|
|
@router.get("/{project_id}", response_model=ProjectResponse)
|
|
async def get_project(
|
|
project_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Get a specific project by ID with its tasks."""
|
|
query = select(Project).options(selectinload(Project.tasks)).where(Project.id == project_id)
|
|
result = await db.execute(query)
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="Project not found")
|
|
|
|
return project
|
|
|
|
|
|
@router.put("/{project_id}", response_model=ProjectResponse)
|
|
async def update_project(
|
|
project_id: int,
|
|
project_update: ProjectUpdate,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Update a project."""
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="Project not found")
|
|
|
|
update_data = project_update.model_dump(exclude_unset=True)
|
|
|
|
for key, value in update_data.items():
|
|
setattr(project, key, value)
|
|
|
|
await db.commit()
|
|
|
|
# Re-fetch with eagerly loaded tasks for response serialization
|
|
query = select(Project).options(selectinload(Project.tasks)).where(Project.id == project_id)
|
|
result = await db.execute(query)
|
|
return result.scalar_one()
|
|
|
|
|
|
@router.delete("/{project_id}", status_code=204)
|
|
async def delete_project(
|
|
project_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Delete a project and all its tasks."""
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="Project not found")
|
|
|
|
await db.delete(project)
|
|
await db.commit()
|
|
|
|
return None
|
|
|
|
|
|
@router.get("/{project_id}/tasks", response_model=List[ProjectTaskResponse])
|
|
async def get_project_tasks(
|
|
project_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Get all tasks for a specific project."""
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="Project not found")
|
|
|
|
query = select(ProjectTask).where(ProjectTask.project_id == project_id).order_by(ProjectTask.sort_order.asc())
|
|
result = await db.execute(query)
|
|
tasks = result.scalars().all()
|
|
|
|
return tasks
|
|
|
|
|
|
@router.post("/{project_id}/tasks", response_model=ProjectTaskResponse, status_code=201)
|
|
async def create_project_task(
|
|
project_id: int,
|
|
task: ProjectTaskCreate,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Create a new task for a project."""
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
|
project = result.scalar_one_or_none()
|
|
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="Project not found")
|
|
|
|
task_data = task.model_dump()
|
|
task_data["project_id"] = project_id
|
|
new_task = ProjectTask(**task_data)
|
|
db.add(new_task)
|
|
await db.commit()
|
|
await db.refresh(new_task)
|
|
|
|
return new_task
|
|
|
|
|
|
@router.put("/{project_id}/tasks/{task_id}", response_model=ProjectTaskResponse)
|
|
async def update_project_task(
|
|
project_id: int,
|
|
task_id: int,
|
|
task_update: ProjectTaskUpdate,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Update a project task."""
|
|
result = await db.execute(
|
|
select(ProjectTask).where(
|
|
ProjectTask.id == task_id,
|
|
ProjectTask.project_id == project_id
|
|
)
|
|
)
|
|
task = result.scalar_one_or_none()
|
|
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
update_data = task_update.model_dump(exclude_unset=True)
|
|
|
|
for key, value in update_data.items():
|
|
setattr(task, key, value)
|
|
|
|
await db.commit()
|
|
await db.refresh(task)
|
|
|
|
return task
|
|
|
|
|
|
@router.delete("/{project_id}/tasks/{task_id}", status_code=204)
|
|
async def delete_project_task(
|
|
project_id: int,
|
|
task_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: Settings = Depends(get_current_session)
|
|
):
|
|
"""Delete a project task."""
|
|
result = await db.execute(
|
|
select(ProjectTask).where(
|
|
ProjectTask.id == task_id,
|
|
ProjectTask.project_id == project_id
|
|
)
|
|
)
|
|
task = result.scalar_one_or_none()
|
|
|
|
if not task:
|
|
raise HTTPException(status_code=404, detail="Task not found")
|
|
|
|
await db.delete(task)
|
|
await db.commit()
|
|
|
|
return None
|