Automated Action d4b0ceed9c Implement Task Manager API with FastAPI and SQLite
- Setup project structure and FastAPI application
- Configure SQLite database with SQLAlchemy ORM
- Setup Alembic for database migrations
- Implement user authentication with JWT
- Create task models and CRUD operations
- Implement task assignment functionality
- Add detailed API documentation
- Create comprehensive README with usage instructions
- Lint code with Ruff
2025-06-08 18:02:43 +00:00

223 lines
5.4 KiB
Python

from datetime import datetime
from typing import Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import and_, or_
from sqlalchemy.orm import Session
from app.core.deps import get_current_user, get_db
from app.models.task import Task, TaskPriority, TaskStatus
from app.models.user import User
from app.schemas.task import (
Task as TaskSchema,
)
from app.schemas.task import (
TaskCreate,
TaskUpdate,
TaskWithAssignees,
)
router = APIRouter()
@router.get("/", response_model=List[TaskSchema])
def read_tasks(
*,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
skip: int = 0,
limit: int = 100,
status: Optional[TaskStatus] = None,
priority: Optional[TaskPriority] = None,
search: Optional[str] = None,
) -> Any:
"""
Retrieve tasks owned by current user or assigned to current user.
Optionally filter by status, priority, or search term.
"""
query = db.query(Task).filter(
and_(
or_(
Task.owner_id == current_user.id,
Task.assignees.any(id=current_user.id)
),
not Task.is_deleted
)
)
# Apply filters if provided
if status:
query = query.filter(Task.status == status)
if priority:
query = query.filter(Task.priority == priority)
if search:
query = query.filter(
or_(
Task.title.ilike(f"%{search}%"),
Task.description.ilike(f"%{search}%"),
)
)
# Apply pagination
tasks = query.order_by(Task.updated_at.desc()).offset(skip).limit(limit).all()
return tasks
@router.post("/", response_model=TaskSchema)
def create_task(
*,
db: Session = Depends(get_db),
task_in: TaskCreate,
current_user: User = Depends(get_current_user),
) -> Any:
"""
Create a new task.
"""
# Create task object
task = Task(
**task_in.model_dump(),
owner_id=current_user.id,
)
# Save to database
db.add(task)
db.commit()
db.refresh(task)
return task
@router.get("/{task_id}", response_model=TaskWithAssignees)
def read_task(
*,
db: Session = Depends(get_db),
task_id: int,
current_user: User = Depends(get_current_user),
) -> Any:
"""
Get details of a specific task.
"""
task = db.query(Task).filter(
and_(
Task.id == task_id,
not Task.is_deleted
)
).first()
if not task:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Task not found"
)
# Check if user has access to task
if task.owner_id != current_user.id and current_user not in task.assignees:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You don't have access to this task"
)
# Convert to TaskWithAssignees schema
result = TaskWithAssignees.model_validate(task)
result.assignee_ids = [user.id for user in task.assignees]
return result
@router.put("/{task_id}", response_model=TaskSchema)
def update_task(
*,
db: Session = Depends(get_db),
task_id: int,
task_in: TaskUpdate,
current_user: User = Depends(get_current_user),
) -> Any:
"""
Update a task.
"""
task = db.query(Task).filter(
and_(
Task.id == task_id,
not Task.is_deleted
)
).first()
if not task:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Task not found"
)
# Only task owner can update a task
if task.owner_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only the task owner can update the task"
)
# Update with new data
update_data = task_in.model_dump(exclude_unset=True)
# If status is being changed to completed, set completed_at
if "status" in update_data and update_data["status"] == TaskStatus.COMPLETED:
update_data["completed_at"] = datetime.utcnow()
# If status is being changed from completed to another status, clear completed_at
elif "status" in update_data and task.status == TaskStatus.COMPLETED:
update_data["completed_at"] = None
# Update task object
for field, value in update_data.items():
setattr(task, field, value)
# Save to database
db.add(task)
db.commit()
db.refresh(task)
return task
@router.delete(
"/{task_id}",
status_code=status.HTTP_204_NO_CONTENT,
response_model=None
)
def delete_task(
*,
db: Session = Depends(get_db),
task_id: int,
current_user: User = Depends(get_current_user),
) -> Any:
"""
Delete a task (soft delete).
"""
task = db.query(Task).filter(
and_(
Task.id == task_id,
not Task.is_deleted
)
).first()
if not task:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Task not found"
)
# Only task owner can delete a task
if task.owner_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only the task owner can delete the task"
)
# Soft delete
task.is_deleted = True
# Save to database
db.add(task)
db.commit()
return None