Spaces:
Sleeping
Sleeping
File size: 4,805 Bytes
cccf200 fa948af cccf200 fa948af cccf200 fa948af 0644a5c fa948af cccf200 fa948af cccf200 fa948af cccf200 fa948af cccf200 fa948af 0644a5c cccf200 fa948af cccf200 fa948af cccf200 fa948af | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | """
Task model migrated to SQLModel with PostgreSQL support.
Extended for 012-advanced-todo-features with due dates, recurrence, and reminders.
"""
from typing import Optional
from datetime import datetime
from enum import Enum
from sqlmodel import SQLModel, Field, Column
from sqlalchemy import DateTime
class RecurrencePattern(str, Enum):
"""Recurrence pattern enumeration for recurring tasks."""
DAILY = "daily"
WEEKLY = "weekly"
BI_WEEKLY = "bi-weekly"
MONTHLY = "monthly"
YEARLY = "yearly"
class Task(SQLModel, table=True):
"""Task entity for todo items."""
__tablename__ = "tasks"
# Existing fields from 001-todo-ai-chatbot
id: Optional[int] = Field(default=None, primary_key=True)
user_id: int = Field(index=True) # Removed foreign_key constraint for demo users
title: str = Field(max_length=200, min_length=1)
description: str = Field(default="", max_length=1000)
completed: bool = Field(default=False)
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
# Offline sync support
client_id: Optional[str] = Field(default=None, max_length=100, index=True)
version: int = Field(default=1)
# NEW FIELDS for 012-advanced-todo-features (due dates, recurrence, reminders)
due_date: Optional[datetime] = Field(
default=None,
sa_column=Column(DateTime(timezone=True)),
description="Task deadline in UTC timezone"
)
recurrence_pattern: Optional[RecurrencePattern] = Field(
default=None,
description="Recurrence type: daily/weekly/bi-weekly/monthly/yearly"
)
is_recurring: bool = Field(
default=False,
description="Whether task auto-creates next instance on completion"
)
reminder_minutes: int = Field(
default=15,
ge=0,
le=1440, # Max 24 hours
description="Minutes before due_date to send reminder (0-1440)"
)
next_occurrence: Optional[datetime] = Field(
default=None,
sa_column=Column(DateTime(timezone=True)),
description="Auto-calculated next due date for recurring tasks"
)
def validate_due_date(self) -> None:
"""
Validate due date constraints.
Raises:
ValueError: If due_date is not timezone-aware
"""
if self.due_date and self.due_date.tzinfo is None:
raise ValueError("due_date must be timezone-aware (UTC)")
def validate_recurrence(self) -> None:
"""
Validate recurrence configuration.
Raises:
ValueError: If recurrence settings are invalid
"""
if self.is_recurring and not self.recurrence_pattern:
raise ValueError("is_recurring requires recurrence_pattern to be set")
if self.is_recurring and not self.due_date:
raise ValueError("Recurring tasks must have a due_date")
if self.recurrence_pattern and not self.is_recurring:
# Auto-set is_recurring if pattern is set
self.is_recurring = True
def calculate_next_occurrence(self) -> datetime:
"""
Calculate next occurrence for recurring tasks.
Returns:
Next occurrence datetime in UTC
Raises:
ValueError: If not a recurring task or missing due_date
"""
if not self.is_recurring or not self.due_date or not self.recurrence_pattern:
raise ValueError("Can only calculate next occurrence for recurring tasks")
from ..utils.recurrence_calculator import RecurrenceCalculator
return RecurrenceCalculator.calculate_next_occurrence(
self.due_date,
self.recurrence_pattern.value
)
class TaskCreate(SQLModel):
"""Schema for creating a new task."""
title: str = Field(max_length=200, min_length=1)
description: str = Field(default="", max_length=1000)
client_id: Optional[str] = Field(default=None, max_length=100)
class TaskUpdate(SQLModel):
"""Schema for updating a task."""
title: Optional[str] = Field(default=None, max_length=200, min_length=1)
description: Optional[str] = Field(default=None, max_length=1000)
completed: Optional[bool] = Field(default=None)
class TaskResponse(SQLModel):
"""Schema for task response."""
id: int
user_id: int
title: str
description: str
completed: bool
created_at: datetime
updated_at: datetime
client_id: Optional[str] = None
version: int
# NEW FIELDS for 012-advanced-todo-features
due_date: Optional[datetime] = None
recurrence_pattern: Optional[RecurrencePattern] = None
is_recurring: bool = False
reminder_minutes: int = 15
next_occurrence: Optional[datetime] = None
|