Add todo priority and due date features
- Add priority levels (low, medium, high) to Todo model - Add due date field to Todo model - Create Alembic migration for new fields - Update Todo schemas to include new fields - Enhance CRUD operations with priority and due date filtering - Update API endpoints to support new fields - Implement smart ordering by priority and due date - Update documentation to reflect changes
This commit is contained in:
parent
f67be35a71
commit
5b2cda28a1
@ -9,6 +9,9 @@ A powerful, secure RESTful API for managing tasks and todos, built with FastAPI
|
||||
- Token revocation (logout)
|
||||
- Role-based access control (User/Admin roles)
|
||||
- 📝 Todo CRUD operations
|
||||
- Priority levels (High, Medium, Low)
|
||||
- Due dates for better task management
|
||||
- Smart ordering by priority and due date
|
||||
- 👤 User management
|
||||
- 🔍 Advanced todo filtering and pagination
|
||||
- 📄 API documentation (via Swagger UI and ReDoc)
|
||||
@ -98,6 +101,9 @@ The `GET /api/v1/todos/` endpoint supports the following query parameters:
|
||||
- `limit`: Maximum number of records to return (default: 100)
|
||||
- `title`: Filter by title (contains search)
|
||||
- `is_completed`: Filter by completion status (true/false)
|
||||
- `priority`: Filter by priority level (low, medium, high)
|
||||
- `due_date_before`: Filter for todos due before this date
|
||||
- `due_date_after`: Filter for todos due after this date
|
||||
|
||||
## Database Schema
|
||||
|
||||
@ -118,6 +124,8 @@ id: Integer (Primary Key)
|
||||
title: String (Indexed)
|
||||
description: Text (Optional)
|
||||
is_completed: Boolean (Default: False)
|
||||
priority: Enum(low, medium, high) (Default: medium)
|
||||
due_date: DateTime (Optional)
|
||||
owner_id: Integer (Foreign Key to User)
|
||||
```
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
from datetime import datetime
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
@ -11,6 +12,7 @@ from app.crud.crud_todo import (
|
||||
get_todos,
|
||||
update_todo,
|
||||
)
|
||||
from app.models.todo import PriorityLevel
|
||||
from app.models.user import User
|
||||
from app.schemas.todo import Todo, TodoCreate, TodoUpdate
|
||||
|
||||
@ -24,6 +26,9 @@ def read_todos(
|
||||
limit: int = 100,
|
||||
title: Optional[str] = None,
|
||||
is_completed: Optional[bool] = None,
|
||||
priority: Optional[PriorityLevel] = None,
|
||||
due_date_before: Optional[datetime] = None,
|
||||
due_date_after: Optional[datetime] = None,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
) -> Any:
|
||||
"""
|
||||
@ -33,6 +38,9 @@ def read_todos(
|
||||
- **limit**: Maximum number of records to return
|
||||
- **title**: Filter by title (contains search)
|
||||
- **is_completed**: Filter by completion status
|
||||
- **priority**: Filter by priority level (low, medium, high)
|
||||
- **due_date_before**: Filter for todos due before this date
|
||||
- **due_date_after**: Filter for todos due after this date
|
||||
"""
|
||||
todos = get_todos(
|
||||
db=db,
|
||||
@ -40,7 +48,10 @@ def read_todos(
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
title=title,
|
||||
is_completed=is_completed
|
||||
is_completed=is_completed,
|
||||
priority=priority,
|
||||
due_date_before=due_date_before,
|
||||
due_date_after=due_date_after
|
||||
)
|
||||
return todos
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.todo import Todo
|
||||
from app.models.todo import PriorityLevel, Todo
|
||||
from app.schemas.todo import TodoCreate, TodoUpdate
|
||||
|
||||
|
||||
@ -16,7 +17,10 @@ def get_todos(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
title: Optional[str] = None,
|
||||
is_completed: Optional[bool] = None
|
||||
is_completed: Optional[bool] = None,
|
||||
priority: Optional[PriorityLevel] = None,
|
||||
due_date_before: Optional[datetime] = None,
|
||||
due_date_after: Optional[datetime] = None
|
||||
) -> List[Todo]:
|
||||
query = db.query(Todo).filter(Todo.owner_id == owner_id)
|
||||
|
||||
@ -26,6 +30,18 @@ def get_todos(
|
||||
if is_completed is not None:
|
||||
query = query.filter(Todo.is_completed == is_completed)
|
||||
|
||||
if priority is not None:
|
||||
query = query.filter(Todo.priority == priority)
|
||||
|
||||
if due_date_before is not None:
|
||||
query = query.filter(Todo.due_date <= due_date_before)
|
||||
|
||||
if due_date_after is not None:
|
||||
query = query.filter(Todo.due_date >= due_date_after)
|
||||
|
||||
# Order by priority (highest first) and due date (earliest first)
|
||||
query = query.order_by(Todo.priority.desc(), Todo.due_date.asc())
|
||||
|
||||
return query.offset(skip).limit(limit).all()
|
||||
|
||||
|
||||
@ -34,6 +50,8 @@ def create_todo(db: Session, todo_in: TodoCreate, owner_id: int) -> Todo:
|
||||
title=todo_in.title,
|
||||
description=todo_in.description,
|
||||
is_completed=todo_in.is_completed,
|
||||
priority=todo_in.priority,
|
||||
due_date=todo_in.due_date,
|
||||
owner_id=owner_id
|
||||
)
|
||||
db.add(db_todo)
|
||||
|
@ -1,9 +1,16 @@
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text
|
||||
from enum import Enum as PyEnum
|
||||
from sqlalchemy import Boolean, Column, DateTime, Enum, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class PriorityLevel(str, PyEnum):
|
||||
LOW = "low"
|
||||
MEDIUM = "medium"
|
||||
HIGH = "high"
|
||||
|
||||
|
||||
class Todo(Base):
|
||||
__tablename__ = "todos"
|
||||
|
||||
@ -11,6 +18,8 @@ class Todo(Base):
|
||||
title = Column(String, index=True)
|
||||
description = Column(Text, nullable=True)
|
||||
is_completed = Column(Boolean, default=False)
|
||||
priority = Column(Enum(PriorityLevel), default=PriorityLevel.MEDIUM)
|
||||
due_date = Column(DateTime, nullable=True)
|
||||
owner_id = Column(Integer, ForeignKey("users.id"))
|
||||
|
||||
owner = relationship("User", back_populates="todos")
|
@ -1,13 +1,18 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.models.todo import PriorityLevel
|
||||
|
||||
|
||||
# Shared properties
|
||||
class TodoBase(BaseModel):
|
||||
title: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
is_completed: Optional[bool] = False
|
||||
priority: Optional[PriorityLevel] = PriorityLevel.MEDIUM
|
||||
due_date: Optional[datetime] = None
|
||||
|
||||
|
||||
# Properties to receive on item creation
|
||||
|
40
migrations/versions/004_add_todo_priority_and_due_date.py
Normal file
40
migrations/versions/004_add_todo_priority_and_due_date.py
Normal file
@ -0,0 +1,40 @@
|
||||
"""add todo priority and due date
|
||||
|
||||
Revision ID: 004
|
||||
Revises: 003
|
||||
Create Date: 2023-11-17
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '004'
|
||||
down_revision = '003'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Create priority_level enum type
|
||||
with op.batch_alter_table('todos', schema=None) as batch_op:
|
||||
# Add priority column
|
||||
batch_op.add_column(sa.Column('priority', sa.Enum('low', 'medium', 'high', name='prioritylevel'), nullable=True))
|
||||
# Set default values for existing records
|
||||
batch_op.execute("UPDATE todos SET priority = 'medium' WHERE priority IS NULL")
|
||||
# Make priority not nullable with default
|
||||
batch_op.alter_column('priority', nullable=False, server_default='medium')
|
||||
|
||||
# Add due_date column
|
||||
batch_op.add_column(sa.Column('due_date', sa.DateTime(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Remove the new columns
|
||||
with op.batch_alter_table('todos', schema=None) as batch_op:
|
||||
batch_op.drop_column('due_date')
|
||||
batch_op.drop_column('priority')
|
||||
|
||||
# Drop the enum type
|
||||
op.execute('DROP TYPE prioritylevel')
|
Loading…
x
Reference in New Issue
Block a user