From 7251fea2ba35e217a5e79f6a040a984a1e361f00 Mon Sep 17 00:00:00 2001 From: Automated Action Date: Thu, 19 Jun 2025 13:18:24 +0000 Subject: [PATCH] Add due date functionality to Todo model - Add due_date field as nullable DateTime with timezone support - Add is_overdue property to check if task is overdue - Add days_until_due property for time calculations - Create migration 007 to add due_date column with index - Maintain backward compatibility with nullable due_date --- alembic/versions/007_add_due_date.py | 32 ++++++++++++++++++++++++++++ app/models/todo.py | 25 ++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 alembic/versions/007_add_due_date.py diff --git a/alembic/versions/007_add_due_date.py b/alembic/versions/007_add_due_date.py new file mode 100644 index 0000000..b7382e4 --- /dev/null +++ b/alembic/versions/007_add_due_date.py @@ -0,0 +1,32 @@ +"""Add due_date field to todos table + +Revision ID: 007 +Revises: 006 +Create Date: 2025-06-19 12:00:00.000000 + +""" + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = "007" +down_revision = "006" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # Add due_date column to todos table + op.add_column( + "todos", + sa.Column("due_date", sa.DateTime(timezone=True), nullable=True), + ) + + # Add index for better performance on due date queries + op.create_index(op.f("ix_todos_due_date"), "todos", ["due_date"], unique=False) + + +def downgrade() -> None: + op.drop_index(op.f("ix_todos_due_date"), table_name="todos") + op.drop_column("todos", "due_date") \ No newline at end of file diff --git a/app/models/todo.py b/app/models/todo.py index db1349f..fa52c72 100644 --- a/app/models/todo.py +++ b/app/models/todo.py @@ -2,6 +2,8 @@ from sqlalchemy import Column, Integer, String, Boolean, DateTime, Enum, Foreign from sqlalchemy.orm import relationship from sqlalchemy.sql import func import enum +from datetime import datetime, timezone +from typing import Optional from app.db.base import Base @@ -23,6 +25,7 @@ class Todo(Base): project_id = Column(Integer, ForeignKey("projects.id"), nullable=True) category_id = Column(Integer, ForeignKey("categories.id"), nullable=True) parent_id = Column(Integer, ForeignKey("todos.id"), nullable=True) + due_date = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) @@ -42,3 +45,25 @@ class Todo(Base): def is_subtask(self) -> bool: """Check if this todo is a subtask of another todo.""" return self.parent_id is not None + + @property + def is_overdue(self) -> bool: + """Check if this todo is overdue.""" + if self.due_date is None or self.completed: + return False + return datetime.now(timezone.utc) > self.due_date + + @property + def days_until_due(self) -> Optional[int]: + """Calculate the number of days until the due date. + + Returns: + int: Number of days until due (negative if overdue) + None: If no due date is set + """ + if self.due_date is None: + return None + + now = datetime.now(timezone.utc) + delta = (self.due_date - now).days + return delta \ No newline at end of file