From 1d7eac0ac70f5783127488afd1c45a254a645bf0 Mon Sep 17 00:00:00 2001 From: Automated Action Date: Tue, 13 May 2025 05:14:03 +0000 Subject: [PATCH] Add due date feature to Todo application MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added due_date field to Todo model - Updated schemas to include due_date field - Created Alembic migration for the new field - Added API endpoint to filter todos by due date range - Updated README to document the new feature 🤖 Generated with BackendIM... (backend.im) Co-Authored-By: Claude --- README.md | 3 ++ alembic/versions/002_add_due_date_column.py | 24 ++++++++++++ app/api/endpoints/todos.py | 42 +++++++++++++++++++-- app/models/todo.py | 1 + app/schemas/todo.py | 2 + 5 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 alembic/versions/002_add_due_date_column.py diff --git a/README.md b/README.md index 8a6168e..b531781 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ A RESTful API for a Todo application built with FastAPI and SQLite. ## Features - Create, Read, Update, and Delete Todo items +- Due date assignment and tracking for todo items +- Filtering todos by due date range - Health endpoint for application monitoring - SQLite database with SQLAlchemy ORM - Database migrations with Alembic @@ -17,6 +19,7 @@ A RESTful API for a Todo application built with FastAPI and SQLite. - **GET /api/v1/todos/{id}**: Get a specific todo by ID - **PUT /api/v1/todos/{id}**: Update a todo - **DELETE /api/v1/todos/{id}**: Delete a todo +- **GET /api/v1/todos/due-date/**: Filter todos by due date range ## Getting Started diff --git a/alembic/versions/002_add_due_date_column.py b/alembic/versions/002_add_due_date_column.py new file mode 100644 index 0000000..9c0b94a --- /dev/null +++ b/alembic/versions/002_add_due_date_column.py @@ -0,0 +1,24 @@ +"""add due_date column to todo table + +Revision ID: 002 +Revises: 001 +Create Date: 2025-05-13 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '002' +down_revision = '001' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column('todo', sa.Column('due_date', sa.DateTime(timezone=True), nullable=True)) + + +def downgrade() -> None: + op.drop_column('todo', 'due_date') \ No newline at end of file diff --git a/app/api/endpoints/todos.py b/app/api/endpoints/todos.py index 576339e..04b4503 100644 --- a/app/api/endpoints/todos.py +++ b/app/api/endpoints/todos.py @@ -1,6 +1,7 @@ -from typing import Any, List -from fastapi import APIRouter, Depends, HTTPException, status +from typing import Any, List, Optional +from fastapi import APIRouter, Depends, HTTPException, status, Query from sqlalchemy.orm import Session +from datetime import datetime, date from app import schemas from app.models.todo import Todo @@ -33,6 +34,7 @@ def create_todo( title=todo_in.title, description=todo_in.description, completed=todo_in.completed, + due_date=todo_in.due_date, ) db.add(todo) db.commit() @@ -97,7 +99,39 @@ def delete_todo( status_code=status.HTTP_404_NOT_FOUND, detail=f"Todo with ID {id} not found" ) - + db.delete(todo) db.commit() - return None \ No newline at end of file + return None + + +@router.get("/due-date/", response_model=List[schemas.Todo]) +def get_todos_by_due_date( + *, + db: Session = Depends(get_db), + start_date: Optional[date] = Query(None, description="Filter todos due on or after this date"), + end_date: Optional[date] = Query(None, description="Filter todos due on or before this date"), + skip: int = 0, + limit: int = 100, +) -> Any: + """ + Filter todos by due date range. + """ + query = db.query(Todo) + + if start_date: + # Convert date to datetime with time at beginning of day + start_datetime = datetime.combine(start_date, datetime.min.time()) + query = query.filter(Todo.due_date >= start_datetime) + + if end_date: + # Convert date to datetime with time at end of day + end_datetime = datetime.combine(end_date, datetime.max.time()) + query = query.filter(Todo.due_date <= end_datetime) + + # Only return todos with due dates if filtering by date + if start_date or end_date: + query = query.filter(Todo.due_date.isnot(None)) + + todos = query.order_by(Todo.due_date.asc()).offset(skip).limit(limit).all() + return todos \ No newline at end of file diff --git a/app/models/todo.py b/app/models/todo.py index dca6ea2..5f64db9 100644 --- a/app/models/todo.py +++ b/app/models/todo.py @@ -8,5 +8,6 @@ class Todo(Base): title = Column(String, index=True) description = Column(String, nullable=True) completed = Column(Boolean, default=False) + due_date = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) \ No newline at end of file diff --git a/app/schemas/todo.py b/app/schemas/todo.py index 1c9caa1..9a9b332 100644 --- a/app/schemas/todo.py +++ b/app/schemas/todo.py @@ -7,6 +7,7 @@ class TodoBase(BaseModel): title: str description: Optional[str] = None completed: bool = False + due_date: Optional[datetime] = None # Properties to receive on creation class TodoCreate(TodoBase): @@ -16,6 +17,7 @@ class TodoCreate(TodoBase): class TodoUpdate(TodoBase): title: Optional[str] = None completed: Optional[bool] = None + due_date: Optional[datetime] = None # Properties shared by models stored in DB class TodoInDBBase(TodoBase):