
This commit introduces new models and endpoints for managing chore history and scheduling within the application. Key changes include: - Added `ChoreHistory` and `ChoreAssignmentHistory` models to track changes and events related to chores and assignments. - Implemented CRUD operations for chore history in the `history.py` module. - Created endpoints to retrieve chore and assignment history in the `chores.py` and `groups.py` files. - Introduced a scheduling feature for group chores, allowing for round-robin assignment generation. - Updated existing chore and assignment CRUD operations to log history entries for create, update, and delete actions. This enhancement improves the tracking of chore-related events and facilitates better management of group chore assignments.
139 lines
5.3 KiB
Python
139 lines
5.3 KiB
Python
from datetime import date, datetime
|
|
from typing import Optional, List, Any
|
|
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, ChoreHistoryEventTypeEnum # For UserPublic relation
|
|
from .user import UserPublic # For embedding user information
|
|
|
|
# Forward declaration for circular dependencies
|
|
class ChoreAssignmentPublic(BaseModel):
|
|
pass
|
|
|
|
# History Schemas
|
|
class ChoreHistoryPublic(BaseModel):
|
|
id: int
|
|
event_type: ChoreHistoryEventTypeEnum
|
|
event_data: Optional[dict[str, Any]] = None
|
|
changed_by_user: Optional[UserPublic] = None
|
|
timestamp: datetime
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
class ChoreAssignmentHistoryPublic(BaseModel):
|
|
id: int
|
|
event_type: ChoreHistoryEventTypeEnum
|
|
event_data: Optional[dict[str, Any]] = None
|
|
changed_by_user: Optional[UserPublic] = None
|
|
timestamp: datetime
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
# 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
|
|
assignments: List[ChoreAssignmentPublic] = []
|
|
history: List[ChoreHistoryPublic] = []
|
|
|
|
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
|
|
assigned_to_user_id: Optional[int] = None # For reassigning the chore
|
|
|
|
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
|
|
history: List[ChoreAssignmentHistoryPublic] = []
|
|
|
|
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()
|
|
ChoreAssignmentPublic.model_rebuild()
|