from pydantic import BaseModel, Field, constr from typing import Optional, List from datetime import datetime from pydantic import BaseModel, Field, validator from typing import Optional, List, Any, Dict from .utils.helperfx import PhoneValidator # Extend the existing schema # Constants for phone and MAC address patterns PHONE_PATTERN = r"^(?:\+255|0)\d{9}$" MAC_PATTERN = r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$" # Register User Request class RegisterUserRequest(BaseModel): name: str = Field(..., max_length=100) password: str = Field(..., max_length=100) phoneNumber: str = Field( ..., pattern=PHONE_PATTERN, description="Tanzanian phone number starting with +255 or 0 followed by 9 digits", ) mac_address: str = Field( ..., pattern=MAC_PATTERN, description="MAC address in standard format" ) def hash_password(self): self.password = pwd_context.hash(self.password) class Config: schema_extra = { "example": { "name": "John Doe", "password": "StrongPassword1!", "phoneNumber": "+255123456789", "mac_address": "00:1A:2B:3C:4D:5E", } } # Login User Request class LoginUserRequest(BaseModel): phoneNumber: str = Field(..., pattern=PHONE_PATTERN) password: str mac_address: str = Field(..., pattern=MAC_PATTERN) # User Response with session data and additional information class UserSessionData(BaseModel): phone: str mac_address: str ip_address: Optional[str] = None uptime: Optional[str] = None bytes_in: int = 0 bytes_out: int = 0 status: str # Active, Inactive, Disabled, etc. profile: str login_time: Optional[datetime] = None logout_time: Optional[datetime] = None # Extended response for listing active users and all users with detailed session data class ActiveUsersResponse(BaseModel): code: int message: str active_users: List[UserSessionData] class UsersListResponse(BaseModel): code: int message: str data: list # Add User Status Response to manage user enabling/disabling status class UserStatusResponse(BaseModel): code: int message: str user_status: str # Indicates whether the user is enabled or disabled # Base Response Schema for standardized responses class BaseResponse(BaseModel): code: int message: str payload: Optional[dict] = None # Login and Logout Responses with tracking for login/logout times class LoginResponse(BaseResponse): login_time: Optional[datetime] = None class LogoutResponse(BaseResponse): logout_time: Optional[datetime] = None # Forgot Password and Reset Password Requests for account management class ForgotPasswordRequest(BaseModel): phoneNumber: str = Field(..., pattern=PHONE_PATTERN) class ResetPasswordRequest(BaseModel): phoneNumber: str = Field(..., pattern=PHONE_PATTERN) new_password: str = Field(..., max_length=100) # User Statistics Response to get detailed statistics for specific or all users class UserStats(BaseModel): phone: str profile: str status: str uptime: str bytes_in: int bytes_out: int class UserListData(BaseModel): phoneNumber: str = Field(..., description="User phone number") profile: str = Field(..., description="User profile, e.g., bandwidth limit profile") status: str = Field(..., description="User status, either 'active' or 'inactive'") disabled: bool = Field(..., description="Whether the user is disabled") comment: Optional[str] = Field( None, description="Additional comments or notes for the user" ) data_limit: Optional[int] = Field( None, description="Total data limit for the user in bytes" ) class UserStatsResponse(BaseResponse): user_stats: List[UserStats] # Example of a response builder for structured success and error responses class ResponseBuilder: @staticmethod def success(response_type: BaseModel, message: str, data: dict = None): return response_type(code=200, message=message, payload=data) @staticmethod def error(response_type: BaseModel, message: str, error_details: str): return response_type( code=400, message=message, payload={"error": error_details} ) # Shared Phone Number Validator class UserBase(BaseModel): phoneNumber: str = Field(..., description="User phone number") @validator("phoneNumber") def validate_phone(cls, phoneNumber): is_valid, formatted_phone = PhoneValidator.validate_and_format(phoneNumber) if not is_valid: raise ValueError("Invalid phone number format") return formatted_phone # Request Models class UserCreate(UserBase): password: str = Field(..., min_length=4, description="User password") profile: str = Field(default="2mbps_profile", description="User profile") class UserLogin(UserBase): password: str = Field(..., description="User password") mac_address: str = Field(..., description="Device MAC address") ip_address: str = Field(..., description="User's IP address") # Base API Response Model class ApiResponse(BaseModel): success: bool = True message: Optional[str] = Field("success", description="Response message") error: Optional[str] = None data: Optional[Dict[str, Any]] = None # Detailed User Models class UserParams(BaseModel): name: str password: str profile: str disabled: bool class ActiveUserData(BaseModel): phone: str uptime: str mac_address: str ip_address: str usage: UserStats class UserStatsData(BaseModel): phoneNumber: str profile: str status: str # Enabled or Disabled uptime: str usage: UserStats class UserListData(BaseModel): phoneNumber: str profile: str status: str # Active or Inactive disabled: bool comment: Optional[str] = "" data_limit: Optional[int] = None # Specific Response Models class RegisterResponse(ApiResponse): data: Optional[Dict[str, Any]] = Field(None, description="Registration details") class LoginResponse(ApiResponse): data: Optional[Dict[str, Any]] = Field(None, description="Login details") class LogoutResponse(ApiResponse): data: Optional[Dict[str, Any]] = Field(None, description="Logout details") class UserStatusResponse(ApiResponse): data: Optional[Dict[str, Any]] = Field( None, description="User status change details" ) class ActiveUsersResponse(ApiResponse): active_users: List[UserSessionData] class UserStatsResponse(ApiResponse): data: Optional[Dict[str, List[UserStatsData]]] = Field( None, description="Statistics for users" ) class UsersListResponse(ApiResponse): data: Optional[Dict[str, List[UserListData]]] = Field( None, description="List of all users with details" )