From 5b2cda28a1f4f761db2656251710bc8407d6f539 Mon Sep 17 00:00:00 2001 From: Automated Action Date: Tue, 17 Jun 2025 02:34:55 +0000 Subject: [PATCH] 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 --- README.md | 8 ++++ app/api/v1/endpoints/todos.py | 13 +++++- app/crud/crud_todo.py | 22 +++++++++- app/models/todo.py | 11 ++++- app/schemas/todo.py | 5 +++ .../004_add_todo_priority_and_due_date.py | 40 +++++++++++++++++++ 6 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 migrations/versions/004_add_todo_priority_and_due_date.py diff --git a/README.md b/README.md index ebbd3e0..a232aea 100644 --- a/README.md +++ b/README.md @@ -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) ``` diff --git a/app/api/v1/endpoints/todos.py b/app/api/v1/endpoints/todos.py index d617965..59f8d68 100644 --- a/app/api/v1/endpoints/todos.py +++ b/app/api/v1/endpoints/todos.py @@ -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 diff --git a/app/crud/crud_todo.py b/app/crud/crud_todo.py index 48d1502..9c7887e 100644 --- a/app/crud/crud_todo.py +++ b/app/crud/crud_todo.py @@ -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) @@ -25,6 +29,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) diff --git a/app/models/todo.py b/app/models/todo.py index 8c71a1d..e106ae7 100644 --- a/app/models/todo.py +++ b/app/models/todo.py @@ -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") \ No newline at end of file diff --git a/app/schemas/todo.py b/app/schemas/todo.py index 40f15ef..b82f8f9 100644 --- a/app/schemas/todo.py +++ b/app/schemas/todo.py @@ -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 diff --git a/migrations/versions/004_add_todo_priority_and_due_date.py b/migrations/versions/004_add_todo_priority_and_due_date.py new file mode 100644 index 0000000..0ce95be --- /dev/null +++ b/migrations/versions/004_add_todo_priority_and_due_date.py @@ -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') \ No newline at end of file