from datetime import datetime, date from abc import ABC from typing import Optional from pydantic import BaseModel, validator from .models import Gender class AbstractUserInfoValidation(BaseModel, ABC): @validator('email', check_fields=False) def assert_email_is_valid(cls, email): if email is not None: if not len(email): raise ValueError("Email must not be empty.") # TODO implement more robust check return email @validator('first_name', check_fields=False) def assert_first_name_is_valid(cls, first_name): if first_name is not None: if not len(first_name): raise ValueError("First Name must not be empty.") return first_name @validator('last_name', check_fields=False) def assert_last_name_is_valid(cls, last_name): if last_name is not None: if not len(last_name): raise ValueError("Last Name must not be empty.") return last_name @validator('date_of_birth', check_fields=False) def assert_dob_is_valid(cls, dob): if dob is not None: if dob >= date.today(): raise ValueError("Date of birth cannot be in the future.") return dob class AbstractUser(AbstractUserInfoValidation, ABC): email: str first_name: str last_name: str gender: Optional[Gender] date_of_birth: Optional[date] is_patient: Optional[bool] is_admin: Optional[bool] @validator('is_admin') def assert_tegridy(cls, is_admin, values): if values['is_patient']: if is_admin: raise ValueError('User cannot be both patient and admin.') for key in ['gender', 'date_of_birth']: if key not in values or values[key] is None: raise ValueError(f"Must specify key '{key}' for patients.") if not values['is_patient'] and not is_admin: raise ValueError(f'User must either be patient or admin.') return is_admin class UserCreate(AbstractUser): password: str password_confirmation: str @validator('password_confirmation') def assert_passwords_match(cls, password_confirmation, values): if not password_confirmation == values['password']: raise ValueError("Passwords do not match.") if len(password_confirmation) < 1: # TODO use more robust password rules raise ValueError("Password must not be empty.") return password_confirmation class UserUpdate(AbstractUserInfoValidation): email: Optional[str] first_name: Optional[str] last_name: Optional[str] gender: Optional[Gender] date_of_birth: Optional[date] password: Optional[str] = None password_confirmation: Optional[str] = None @validator('password_confirmation') def assert_passwords_match_or_are_both_none(cls, password_confirmation, values): password = values.get('password') if None not in [password, password_confirmation]: if not password == password_confirmation: raise ValueError("Passwords do not match.") if len(password_confirmation) < 1: # TODO use more robust password rules raise ValueError("Password must not be empty.") return password_confirmation class User(AbstractUser): id: int created: datetime updated: datetime class Config: orm_mode = True