Automated Action 4b391fe54b Enhance todo app with advanced features
- Add User model with email-based authentication
- Add Tag model with many-to-many relationship to todos
- Add TodoTag junction table for todo-tag relationships
- Enhance Todo model with priority levels (low, medium, high)
- Add due_date field with datetime support
- Add recurrence_pattern field for recurring todos
- Add parent-child relationship for subtasks support
- Create comprehensive alembic migration for all changes
- Add proper indexes for performance optimization
- Use Text type for todo descriptions
- Implement proper SQLAlchemy relationships and foreign keys
2025-06-19 12:49:26 +00:00

258 lines
6.2 KiB
Python

from pydantic import BaseModel, Field, EmailStr, validator
from datetime import datetime
from typing import Optional, List
from enum import Enum
# Enums for validation
class PriorityLevel(str, Enum):
"""Priority levels for todos"""
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
class RecurrencePattern(str, Enum):
"""Recurrence patterns for todos"""
NONE = "none"
DAILY = "daily"
WEEKLY = "weekly"
MONTHLY = "monthly"
YEARLY = "yearly"
# User schemas
class UserBase(BaseModel):
"""Base user schema with common fields"""
username: str = Field(..., min_length=3, max_length=50)
email: EmailStr
full_name: Optional[str] = Field(None, max_length=100)
class UserCreate(UserBase):
"""Schema for creating a new user"""
password: str = Field(..., min_length=6)
class UserUpdate(BaseModel):
"""Schema for updating user information"""
username: Optional[str] = Field(None, min_length=3, max_length=50)
email: Optional[EmailStr] = None
full_name: Optional[str] = Field(None, max_length=100)
password: Optional[str] = Field(None, min_length=6)
class User(UserBase):
"""User response schema"""
id: int
is_active: bool
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class UserInDB(User):
"""User schema with password hash for internal use"""
hashed_password: str
# Enhanced Todo schemas
class TodoBase(BaseModel):
"""Base todo schema with common fields"""
title: str = Field(..., min_length=1, max_length=200)
description: Optional[str] = Field(None, max_length=1000)
completed: bool = False
priority: PriorityLevel = PriorityLevel.MEDIUM
tags: List[str] = Field(default_factory=list, max_items=10)
due_date: Optional[datetime] = None
parent_id: Optional[int] = Field(None, description="ID of parent todo for subtasks")
recurrence_pattern: RecurrencePattern = RecurrencePattern.NONE
user_id: Optional[int] = Field(
None, description="ID of the user who owns this todo"
)
@validator("tags")
def validate_tags(cls, v):
"""Validate tags are non-empty strings and remove duplicates"""
if v:
# Remove empty strings and duplicates
clean_tags = list(set([tag.strip() for tag in v if tag.strip()]))
# Validate tag length
for tag in clean_tags:
if len(tag) > 50:
raise ValueError("Each tag must be 50 characters or less")
return clean_tags
return v
@validator("due_date")
def validate_due_date(cls, v):
"""Validate due date is not in the past"""
if v and v < datetime.now():
raise ValueError("Due date cannot be in the past")
return v
class TodoCreate(TodoBase):
"""Schema for creating a new todo"""
pass
class TodoUpdate(BaseModel):
"""Schema for updating todo information"""
title: Optional[str] = Field(None, min_length=1, max_length=200)
description: Optional[str] = Field(None, max_length=1000)
completed: Optional[bool] = None
priority: Optional[PriorityLevel] = None
tags: Optional[List[str]] = Field(None, max_items=10)
due_date: Optional[datetime] = None
parent_id: Optional[int] = Field(None, description="ID of parent todo for subtasks")
recurrence_pattern: Optional[RecurrencePattern] = None
@validator("tags")
def validate_tags(cls, v):
"""Validate tags are non-empty strings and remove duplicates"""
if v is not None:
# Remove empty strings and duplicates
clean_tags = list(set([tag.strip() for tag in v if tag.strip()]))
# Validate tag length
for tag in clean_tags:
if len(tag) > 50:
raise ValueError("Each tag must be 50 characters or less")
return clean_tags
return v
@validator("due_date")
def validate_due_date(cls, v):
"""Validate due date is not in the past"""
if v and v < datetime.now():
raise ValueError("Due date cannot be in the past")
return v
class Todo(TodoBase):
"""Todo response schema with all fields"""
id: int
created_at: datetime
updated_at: datetime
subtasks: List["Todo"] = Field(default_factory=list, description="List of subtasks")
shared_with: List[User] = Field(
default_factory=list, description="Users this todo is shared with"
)
class Config:
from_attributes = True
class TodoSummary(BaseModel):
"""Simplified todo schema for list views"""
id: int
title: str
completed: bool
priority: PriorityLevel
tags: List[str]
due_date: Optional[datetime]
created_at: datetime
subtask_count: int = 0
class Config:
from_attributes = True
# Sharing schemas
class TodoShareBase(BaseModel):
"""Base schema for todo sharing"""
todo_id: int
user_id: int
can_edit: bool = False
class TodoShareCreate(TodoShareBase):
"""Schema for creating a todo share"""
pass
class TodoShare(TodoShareBase):
"""Todo share response schema"""
id: int
shared_at: datetime
todo: TodoSummary
user: User
class Config:
from_attributes = True
# Response schemas for collections
class TodoListResponse(BaseModel):
"""Response schema for todo lists with pagination"""
todos: List[TodoSummary]
total: int
page: int
page_size: int
has_next: bool
has_previous: bool
class UserListResponse(BaseModel):
"""Response schema for user lists with pagination"""
users: List[User]
total: int
page: int
page_size: int
has_next: bool
has_previous: bool
# Authentication schemas
class Token(BaseModel):
"""JWT token response"""
access_token: str
token_type: str
class TokenData(BaseModel):
"""Token data for JWT validation"""
username: Optional[str] = None
# API Response schemas
class APIResponse(BaseModel):
"""Generic API response wrapper"""
success: bool
message: str
data: Optional[dict] = None
class HealthResponse(BaseModel):
"""Health check response schema"""
status: str
timestamp: datetime
version: str = "1.0.0"
# Update Todo model to handle forward references
Todo.model_rebuild()