-
-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/refactor dto #16
Changes from 9 commits
e1c46b3
45622e0
c966d10
1422abb
190c8c2
d94a918
64ce0c5
7ebc04f
3de991a
78e21fc
47b8225
7a950ae
c0e1845
d9e3477
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,19 @@ | ||
"""Data transfer object for domain registration.""" | ||
"""Data transfer object for hospital domain registration.""" | ||
from __future__ import annotations | ||
|
||
from typing import Optional | ||
|
||
import pydantic | ||
|
||
from registrations.domain.hospital import registration | ||
from registrations.domain.location.location import Address | ||
from registrations.utils import enum_utils | ||
from registrations.utils.errors import InvalidRegistrationEntryError | ||
|
||
|
||
class RegisterKeyContact( | ||
pydantic.BaseModel, | ||
extra=pydantic.Extra.forbid, | ||
allow_mutation=False, | ||
validate_assignment=True, | ||
arbitrary_types_allowed=False, | ||
|
@@ -21,7 +25,7 @@ class RegisterKeyContact( | |
email: Optional[pydantic.EmailStr] | ||
|
||
|
||
class HospitalRegistrationEntry( | ||
class ToHospitalRegistrationEntry( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uninitialized attribute: Attribute Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sonatype-lift ignore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've recorded this as ignored for this pull request. If you change your mind, just comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uninitialized attribute: Attribute Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sonatype-lift ignore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've recorded this as ignored for this pull request. If you change your mind, just comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uninitialized attribute: Attribute Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sonatype-lift ignore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've recorded this as ignored for this pull request. If you change your mind, just comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uninitialized attribute: Attribute Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sonatype-lift ignore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've recorded this as ignored for this pull request. If you change your mind, just comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uninitialized attribute: Attribute Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uninitialized attribute: Attribute Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||
pydantic.BaseModel, | ||
allow_mutation=False, | ||
validate_assignment=True, | ||
|
@@ -35,3 +39,88 @@ class HospitalRegistrationEntry( | |
key_contact: Optional[RegisterKeyContact] | ||
verified_status: Optional[str] | ||
address: Address | ||
|
||
@pydantic.validator("ownership_type", pre=True) | ||
@classmethod | ||
def validate_ownership_type(cls, ownership_type: str) -> str: | ||
"""Validate ownership is limited to OwnershipType enum.""" | ||
if ownership_type not in registration.OwnershipType.values(): | ||
raise ValueError(f"Invalid ownership type: {ownership_type}") | ||
return ownership_type | ||
|
||
@pydantic.validator("name", pre=True) | ||
@classmethod | ||
def validate_name(cls, name: str) -> str: | ||
"""Validate name has atleast one word with 3 characters.""" | ||
split_names = name.strip().split() | ||
valid_conditions = split_names and ( | ||
any(len(word) >= 2 for word in split_names) | ||
or (len(split_names) == 1 and len(name.strip().split()[0]) >= 3) | ||
) | ||
if not valid_conditions: | ||
raise ValueError(f"Invalid name: {name}. Not enough characters.") | ||
return name | ||
|
||
@pydantic.root_validator | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invalid decoration: While applying decorator Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sonatype-lift ignore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've recorded this as ignored for this pull request. If you change your mind, just comment |
||
@classmethod | ||
def validate_hospital_entity(cls, values: dict) -> dict: | ||
"""Validate hospital entity. | ||
|
||
We either get a verification status or an unverified | ||
manual registration entry. We cannot get both. | ||
Either attribute should be accessible via registration_entry | ||
as verified_status or key_contact. | ||
""" | ||
verified_status = values.get("verified_status") | ||
key_contact: Optional[RegisterKeyContact] = values.get("key_contact") | ||
wrong_invariant = not key_contact and not verified_status | ||
wrong_invariant = wrong_invariant or bool( | ||
verified_status | ||
and ( | ||
( | ||
verified_status | ||
== enum_utils.enum_value_of( | ||
registration.VerificationStatus.Unverified | ||
) | ||
and not key_contact | ||
) | ||
or ( | ||
verified_status | ||
!= enum_utils.enum_value_of( | ||
registration.VerificationStatus.Unverified | ||
) | ||
and key_contact | ||
) | ||
) | ||
) | ||
if wrong_invariant: | ||
error_msg = ( | ||
f"Cannot have {verified_status} verification status with " | ||
f"{(key_contact and key_contact.name) or key_contact} key contact." | ||
) | ||
raise InvalidRegistrationEntryError(error_msg) | ||
return values | ||
|
||
def build_hospital_entity_dict(self) -> dict: | ||
"""Build hospital entity. | ||
|
||
:return: dict, the hospital entity dict to register. | ||
""" | ||
builder_dict = self.dict() | ||
if verified_status := builder_dict.get("verified_status"): | ||
builder_dict["verified_status"] = registration.VerificationStatus( | ||
verified_status | ||
) | ||
if key_contact := builder_dict.pop("key_contact", None): | ||
builder_dict["key_contact_registrar"] = registration.ContactPerson( | ||
name=key_contact.get("name"), | ||
mobile_number=registration.PhoneNumber( | ||
number=key_contact.get("mobile") | ||
), | ||
email=key_contact.get("email"), | ||
) | ||
builder_dict["phone_number"] = registration.PhoneNumber( | ||
number=builder_dict.pop("hospital_contact_number") | ||
) | ||
builder_dict["hospital_name"] = builder_dict.pop("name") | ||
return builder_dict |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,20 @@ | ||
from __future__ import annotations | ||
|
||
import uuid | ||
from typing import Any | ||
from typing import Dict | ||
from typing import Optional | ||
from typing import Type | ||
from typing import TypeVar | ||
from typing import Union | ||
|
||
import email_validator | ||
import phonenumbers | ||
import pydantic | ||
from email_validator import EmailNotValidError | ||
from phonenumbers import parse as parse_number | ||
|
||
from registrations.domain.location.location import Address | ||
from registrations.utils import enum_utils | ||
|
||
|
||
class MissingRegistrationFieldError(pydantic.ValidationError): | ||
def __init__( | ||
self, | ||
error_msg: str, | ||
model: Type[pydantic.BaseModel], | ||
exc_tb: Optional[str] = None, | ||
): | ||
error_wrapper = pydantic.error_wrappers.ErrorWrapper( | ||
Exception(error_msg), exc_tb or error_msg | ||
) | ||
super().__init__(errors=[error_wrapper], model=model) | ||
|
||
from registrations.utils.errors import MissingRegistrationFieldError | ||
|
||
# ************************************************* # | ||
# These are the domain entities of registration.. | ||
|
@@ -39,6 +27,7 @@ class OwnershipType(enum_utils.EnumWithItems): | |
Public = "public" | ||
Private = "private" | ||
Pub_Pvt = "public_private" | ||
Charitable = "charitable" | ||
|
||
|
||
# Value Object | ||
|
@@ -55,7 +44,7 @@ class PhoneNumber(pydantic.BaseModel, allow_mutation=False, validate_assignment= | |
@pydantic.validator("number", pre=True) | ||
@classmethod | ||
def _validate_number(cls, phone_number: str) -> str: | ||
phonenum_obj: phonenumbers.PhoneNumber = phonenumbers.parse(phone_number) | ||
phonenum_obj: phonenumbers.PhoneNumber = parse_number(phone_number) # type: ignore[no-any-unimported] | ||
if not ( | ||
phonenumbers.is_possible_number(phonenum_obj) | ||
and phonenumbers.is_valid_number(phonenum_obj) | ||
|
@@ -65,7 +54,12 @@ def _validate_number(cls, phone_number: str) -> str: | |
|
||
|
||
# Value Object | ||
class ContactPerson(pydantic.BaseModel, allow_mutation=False, validate_assignment=True): | ||
class ContactPerson( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unexpected keyword: Unexpected keyword argument Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||
pydantic.BaseModel, | ||
extra=pydantic.Extra.forbid, | ||
allow_mutation=False, | ||
validate_assignment=True, | ||
): | ||
"""Key contact person registering the hospital.""" | ||
|
||
name: str | ||
|
@@ -82,6 +76,24 @@ def _validate_email( | |
return email | ||
|
||
|
||
# ================================================== # | ||
# Fix for incompatible type. | ||
# See: https://github.com/python/mypy/issues/5382 | ||
# Fixes error of type: | ||
# Argument 1 to <Some Caller> has incompatible type "**Dict[str, <Data types>]"; | ||
# expected... | ||
# TypeDict and Final does not fix this. | ||
# ================================================== # | ||
HospitalEntryDictType = Union[ | ||
pydantic.UUID1, | ||
str, | ||
Optional[OwnershipType], | ||
Address, | ||
PhoneNumber, | ||
Optional[VerificationStatus], | ||
] | ||
|
||
|
||
class HospitalEntryAggregate(pydantic.BaseModel, validate_assignment=True): | ||
"""Aggregate hospital entity. Not to be directly used. | ||
|
||
|
@@ -98,47 +110,45 @@ class HospitalEntryAggregate(pydantic.BaseModel, validate_assignment=True): | |
phone_number: PhoneNumber | ||
|
||
@classmethod | ||
def build_factory(cls, **kwargs) -> HospitalEntityType: | ||
cls._validate_attributes(**kwargs) | ||
def build_factory(cls, **kwargs: HospitalEntryDictType) -> HospitalEntityType: | ||
return ( | ||
cls._build_unclaimed_hospital_factory(**kwargs) | ||
if cls._can_be_verified(**kwargs) | ||
else cls._build_unverified_hospital_factory(**kwargs) | ||
) | ||
|
||
@classmethod | ||
def register_unverified_hospital_factory( | ||
cls, **kwargs | ||
) -> UnverifiedRegisteredHospital: | ||
return UnverifiedRegisteredHospital(**kwargs) | ||
|
||
@classmethod | ||
def register_unclaimed_hospital_factory(cls, **kwargs) -> UnclaimedHospital: | ||
return UnclaimedHospital(**kwargs) | ||
|
||
@classmethod | ||
def _build_unclaimed_hospital_factory(cls, **kwargs) -> UnclaimedHospital: | ||
return cls.register_unclaimed_hospital_factory(**kwargs) | ||
def _build_unclaimed_hospital_factory( | ||
cls, **kwargs: HospitalEntryDictType | ||
) -> UnclaimedHospital: | ||
# Required to typecase the expectation | ||
# of kwargs to have any type. | ||
values_dict: Dict[str, Any] = kwargs | ||
return UnclaimedHospital(**values_dict) | ||
|
||
@classmethod | ||
def _build_unverified_hospital_factory( | ||
cls, **kwargs | ||
cls, **kwargs: HospitalEntryDictType | ||
) -> UnverifiedRegisteredHospital: | ||
if not kwargs.get("key_contact_registrar"): | ||
raise MissingRegistrationFieldError( | ||
"Field missing. key_contact_registrar is required.", | ||
UnverifiedRegisteredHospital, | ||
) | ||
return cls.register_unverified_hospital_factory(**kwargs) | ||
# Required to typecase the expectation | ||
# of kwargs to have any type. | ||
values_dict: Dict[str, Any] = kwargs | ||
return UnverifiedRegisteredHospital(**values_dict) | ||
|
||
@pydantic.root_validator | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invalid decoration: While applying decorator Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sonatype-lift ignore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've recorded this as ignored for this pull request. If you change your mind, just comment |
||
@classmethod | ||
def _validate_attributes(cls, **kwargs) -> bool: | ||
if missing_attrs := cls._check_missing_attributes(**kwargs): | ||
def _validate_attributes(cls, values: dict) -> dict: | ||
if missing_attrs := cls._check_missing_attributes(**values): | ||
raise AttributeError(f"Missing Attributes: {','.join(missing_attrs)}") | ||
return True | ||
return values | ||
|
||
@classmethod | ||
def _check_missing_attributes(cls, **kwargs) -> Optional[list]: | ||
def _check_missing_attributes(cls, **kwargs: str) -> Optional[list]: | ||
"""Check entity attributes if absent in input.""" | ||
field_attrs = cls.__fields__.keys() | ||
kwarg_keys = kwargs.keys() | ||
|
@@ -147,7 +157,7 @@ def _check_missing_attributes(cls, **kwargs) -> Optional[list]: | |
@classmethod | ||
def _can_be_verified( | ||
cls, | ||
**kwargs, | ||
**kwargs: HospitalEntryDictType, | ||
) -> bool: | ||
"""Checks if the hospital entry can be verified. | ||
|
||
|
@@ -171,14 +181,14 @@ class UnclaimedHospital(HospitalEntryAggregate): | |
verified_status: VerificationStatus | ||
|
||
@classmethod | ||
def hospital_is_verified(cls, **kwargs) -> bool: | ||
def hospital_is_verified(cls, **kwargs: HospitalEntryDictType) -> bool: | ||
return ( | ||
bool(kwargs.get("verified_status")) | ||
and kwargs["verified_status"] == VerificationStatus.Verified | ||
) | ||
|
||
@classmethod | ||
def hospital_verification_pending(cls, **kwargs) -> bool: | ||
def hospital_verification_pending(cls, **kwargs: HospitalEntryDictType) -> bool: | ||
return ( | ||
bool(kwargs.get("verified_status")) | ||
and kwargs["verified_status"] == VerificationStatus.Pending | ||
|
@@ -190,7 +200,4 @@ def hospital_verification_pending(cls, **kwargs) -> bool: | |
# Incompatible return type: | ||
# Expected UnverifiedRegisteredHospital but got typing.Union[UnclaimedHospital, UnverifiedRegisteredHospital]. | ||
# ************************************************* # | ||
UnionRegisteredTypes = TypeVar( | ||
"UnionRegisteredTypes", UnverifiedRegisteredHospital, UnclaimedHospital | ||
) | ||
HospitalEntityType = Union[UnclaimedHospital, UnverifiedRegisteredHospital] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ class Address(pydantic.BaseModel, extra=pydantic.Extra.forbid): | |
|
||
@pydantic.root_validator(pre=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invalid decoration: While applying decorator Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sonatype-lift ignore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've recorded this as ignored for this pull request. If you change your mind, just comment |
||
@classmethod | ||
def _validate_address(cls, values): | ||
def _validate_address(cls, values: dict) -> dict: | ||
# TODO: use internal library to validate abbreviation ISO codes for: | ||
# 1. country | ||
# 2. state | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unexpected keyword: Unexpected keyword argument
extra
to callobject.__init_subclass__
.Reply with "@sonatype-lift help" for info about LiftBot commands.
Reply with "@sonatype-lift ignore" to tell LiftBot to leave out the above finding from this PR.
Reply with "@sonatype-lift ignoreall" to tell LiftBot to leave out all the findings from this PR and from the status bar in Github.
When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands.
Was this a good recommendation?
[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sonatype-lift ignore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've recorded this as ignored for this pull request. If you change your mind, just comment
@sonatype-lift unignore
.