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.get('type') == ChoreTypeEnum.group and v is None: raise ValueError("group_id is required for group chores") if values.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.get('type') == ChoreTypeEnum.group and v is None: raise ValueError("group_id is required for group chores") if values.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()