
- Introduced a new `RecurrencePattern` model to manage recurrence details for expenses, allowing for daily, weekly, monthly, and yearly patterns. - Updated the `Expense` model to include fields for recurrence management, such as `is_recurring`, `recurrence_pattern_id`, and `next_occurrence`. - Modified the database schema to reflect these changes, including alterations to existing columns and the removal of obsolete fields. - Enhanced the expense creation logic to accommodate recurring expenses and updated related CRUD operations accordingly. - Implemented necessary migrations to ensure database integrity and support for the new features.
112 lines
4.6 KiB
Python
112 lines
4.6 KiB
Python
from datetime import date, datetime
|
|
from typing import Optional, List
|
|
from pydantic import BaseModel, ConfigDict, field_validator
|
|
|
|
# Assuming ChoreFrequencyEnum is imported from models
|
|
# Adjust the import path if necessary based on your project structure.
|
|
# e.g., from app.models import ChoreFrequencyEnum
|
|
from ..models import ChoreFrequencyEnum, ChoreTypeEnum, User as UserModel # For UserPublic relation
|
|
from .user import UserPublic # For embedding user information
|
|
|
|
# Chore Schemas
|
|
class ChoreBase(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
frequency: ChoreFrequencyEnum
|
|
custom_interval_days: Optional[int] = None
|
|
next_due_date: date # For creation, this will be the initial due date
|
|
type: ChoreTypeEnum
|
|
|
|
@field_validator('custom_interval_days', mode='before')
|
|
@classmethod
|
|
def check_custom_interval_days(cls, value, values):
|
|
# Pydantic v2 uses `values.data` to get all fields
|
|
# For older Pydantic, it might just be `values`
|
|
# This is a simplified check; actual access might differ slightly
|
|
# based on Pydantic version context within the validator.
|
|
# The goal is to ensure custom_interval_days is present if frequency is 'custom'.
|
|
# This validator might be more complex in a real Pydantic v2 setup.
|
|
|
|
# A more direct way if 'frequency' is already parsed into values.data:
|
|
# freq = values.data.get('frequency')
|
|
# For this example, we'll assume 'frequency' might not be in 'values.data' yet
|
|
# if 'custom_interval_days' is validated 'before' 'frequency'.
|
|
# A truly robust validator might need to be on the whole model or run 'after'.
|
|
# For now, this is a placeholder for the logic.
|
|
# Consider if this validation is better handled at the service/CRUD layer for complex cases.
|
|
return value
|
|
|
|
class ChoreCreate(ChoreBase):
|
|
group_id: Optional[int] = None
|
|
|
|
@field_validator('group_id')
|
|
@classmethod
|
|
def validate_group_id(cls, v, values):
|
|
if values.data.get('type') == ChoreTypeEnum.group and v is None:
|
|
raise ValueError("group_id is required for group chores")
|
|
if values.data.get('type') == ChoreTypeEnum.personal and v is not None:
|
|
raise ValueError("group_id must be None for personal chores")
|
|
return v
|
|
|
|
class ChoreUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
frequency: Optional[ChoreFrequencyEnum] = None
|
|
custom_interval_days: Optional[int] = None
|
|
next_due_date: Optional[date] = None # Allow updating next_due_date directly if needed
|
|
type: Optional[ChoreTypeEnum] = None
|
|
group_id: Optional[int] = None
|
|
# last_completed_at should generally not be updated directly by user
|
|
|
|
@field_validator('group_id')
|
|
@classmethod
|
|
def validate_group_id(cls, v, values):
|
|
if values.data.get('type') == ChoreTypeEnum.group and v is None:
|
|
raise ValueError("group_id is required for group chores")
|
|
if values.data.get('type') == ChoreTypeEnum.personal and v is not None:
|
|
raise ValueError("group_id must be None for personal chores")
|
|
return v
|
|
|
|
class ChorePublic(ChoreBase):
|
|
id: int
|
|
group_id: Optional[int] = None
|
|
created_by_id: int
|
|
last_completed_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
creator: Optional[UserPublic] = None # Embed creator UserPublic schema
|
|
# group: Optional[GroupPublic] = None # Embed GroupPublic schema if needed
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
# Chore Assignment Schemas
|
|
class ChoreAssignmentBase(BaseModel):
|
|
chore_id: int
|
|
assigned_to_user_id: int
|
|
due_date: date
|
|
|
|
class ChoreAssignmentCreate(ChoreAssignmentBase):
|
|
pass
|
|
|
|
class ChoreAssignmentUpdate(BaseModel):
|
|
# Only completion status and perhaps due_date can be updated for an assignment
|
|
is_complete: Optional[bool] = None
|
|
due_date: Optional[date] = None # If rescheduling an existing assignment is allowed
|
|
|
|
class ChoreAssignmentPublic(ChoreAssignmentBase):
|
|
id: int
|
|
is_complete: bool
|
|
completed_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
# Embed ChorePublic and UserPublic for richer responses
|
|
chore: Optional[ChorePublic] = None
|
|
assigned_user: Optional[UserPublic] = None
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
# To handle potential circular imports if ChorePublic needs GroupPublic and GroupPublic needs ChorePublic
|
|
# We can update forward refs after all models are defined.
|
|
# ChorePublic.model_rebuild() # If using Pydantic v2 and forward refs were used with strings
|
|
# ChoreAssignmentPublic.model_rebuild()
|