from pydantic import BaseModel, EmailStr, Field from typing import Optional, List, Dict, Any from datetime import date, time, datetime # ============ Auth ============ class LoginRequest(BaseModel): email: str password: str class TokenResponse(BaseModel): access_token: str token_type: str = "bearer" class UserResponse(BaseModel): id: int email: str name: str role: str class Config: from_attributes = True # ============ Attendance ============ class AttendanceBase(BaseModel): employee_id: str employee_name: str department: Optional[str] = None date: date check_in: Optional[time] = None check_out: Optional[time] = None overtime_hours: float = 0 leave_type: Optional[str] = None leave_hours: float = 0 status: str remarks: Optional[str] = None custom_fields: Dict[str, Any] = {} class AttendanceCreate(AttendanceBase): pass class AttendanceUpdate(BaseModel): employee_id: Optional[str] = None employee_name: Optional[str] = None department: Optional[str] = None date: Optional[date] = None check_in: Optional[time] = None check_out: Optional[time] = None overtime_hours: Optional[float] = None leave_type: Optional[str] = None leave_hours: Optional[float] = None status: Optional[str] = None remarks: Optional[str] = None custom_fields: Optional[Dict[str, Any]] = None class AttendanceResponse(AttendanceBase): id: int created_by: Optional[int] = None created_at: datetime updated_at: datetime class Config: from_attributes = True # ============ Accident ============ class AccidentBase(BaseModel): date: date time: Optional[time] = None location: str employee_name: str employee_id: Optional[str] = None department: Optional[str] = None description: str severity: str medical_report: Optional[str] = None action_taken: Optional[str] = None responsible_person: Optional[str] = None custom_fields: Dict[str, Any] = {} class AccidentCreate(AccidentBase): pass class AccidentUpdate(BaseModel): date: Optional[date] = None time: Optional[time] = None location: Optional[str] = None employee_name: Optional[str] = None employee_id: Optional[str] = None department: Optional[str] = None description: Optional[str] = None severity: Optional[str] = None medical_report: Optional[str] = None action_taken: Optional[str] = None responsible_person: Optional[str] = None custom_fields: Optional[Dict[str, Any]] = None class AccidentResponse(AccidentBase): id: int created_by: Optional[int] = None created_at: datetime updated_at: datetime class Config: from_attributes = True # ============ Custom Fields ============ class CustomFieldBase(BaseModel): field_name: str field_type: str field_key: str required: bool = False options: List[str] = [] order: int = 0 class CustomFieldCreate(CustomFieldBase): pass class CustomFieldResponse(CustomFieldBase): id: int class Config: from_attributes = True # ============ Dashboard ============ class AttendanceStats(BaseModel): total: int today: int this_month: int by_status: Dict[str, int] by_department: Dict[str, int] trend: List[Dict[str, Any]] class AccidentStats(BaseModel): total: int this_month: int unresolved: int by_severity: Dict[str, int] recent: List[AccidentResponse] # ============ Import ============ class ImportRequest(BaseModel): section: str # "attendance" or "accident" update_existing: bool = True class ImportResult(BaseModel): created: int updated: int skipped: int errors: List[str] # ============ Pagination / Filter ============ class FilterParams(BaseModel): page: int = 1 per_page: int = 20 sort_by: Optional[str] = None sort_order: Optional[str] = "asc" # asc or desc search: Optional[str] = None date_from: Optional[date] = None date_to: Optional[date] = None department: Optional[str] = None status: Optional[str] = None class PaginatedResponse(BaseModel): items: List[Any] total: int page: int per_page: int total_pages: int