Skip to content
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

Merged
merged 14 commits into from
Jul 18, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ repos:
"--disable=C",
"--disable=C0415",
"--disable=W",
"--disable=R0903",
"--disable=E1101",
"--msg-template={path}||{msg_id}||{symbol}||{category}||{line}||{column}||{msg}",
]
- repo: https://github.com/pre-commit/mirrors-mypy
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ extension-pkg-allow-list=
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
# for backward compatibility.)
extension-pkg-whitelist=
extension-pkg-whitelist=ErrorWrapper

# Return non-zero exit code if any of these messages/categories are detected,
# even if score is above --fail-under value. Syntax same as enable. Messages
Expand Down
68 changes: 66 additions & 2 deletions registrations/domain/dto.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""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.domain.services import hospital_registration_services
from registrations.utils import enum_utils


class RegisterKeyContact(
Copy link

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 call object.__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 ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

Copy link

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.

Expand All @@ -21,7 +24,7 @@ class RegisterKeyContact(
email: Optional[pydantic.EmailStr]


class HospitalRegistrationEntry(
class ToHospitalRegistrationEntry(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uninitialized attribute: Attribute hospital_contact_number is declared in class ToHospitalRegistrationEntry to have type str but is never initialized.

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 ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

Copy link

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.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uninitialized attribute: Attribute name is declared in class ToHospitalRegistrationEntry to have type str but is never initialized.

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 ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

Copy link

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.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uninitialized attribute: Attribute verified_status is declared in class ToHospitalRegistrationEntry to have type Optional[str] but is never initialized.

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 ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

Copy link

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.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uninitialized attribute: Attribute address is declared in class ToHospitalRegistrationEntry to have type Address but is never initialized.

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 ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

Copy link

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.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uninitialized attribute: Attribute key_contact is declared in class ToHospitalRegistrationEntry to have type Optional[RegisterKeyContact] but is never initialized.

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 ]

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uninitialized attribute: Attribute ownership_type is declared in class ToHospitalRegistrationEntry to have type str but is never initialized.

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 ]

pydantic.BaseModel,
allow_mutation=False,
validate_assignment=True,
Expand All @@ -35,3 +38,64 @@ 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."""
if not (name and len(name.strip().split()[0]) >= 3):
raise ValueError(f"Invalid name: {name}. Not enough characters.")
return name

@pydantic.root_validator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid decoration: While applying decorator pydantic.root_validator

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 ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonatype-lift ignore

Copy link

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.

@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")
if (
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
):
error_msg = (
f"Cannot have {verified_status} verification status with "
f"{(key_contact and key_contact.name) or key_contact} key contact."
)
raise ValueError(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"] = key_contact
builder_dict["phone_number"] = registration.PhoneNumber(
number=builder_dict.pop("hospital_contact_number")
)
return builder_dict
1 change: 1 addition & 0 deletions registrations/domain/hospital/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class OwnershipType(enum_utils.EnumWithItems):
Public = "public"
Private = "private"
Pub_Pvt = "public_private"
Charitable = "charitable"


# Value Object
Expand Down
56 changes: 8 additions & 48 deletions registrations/domain/services/application_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
from typing import Protocol
from typing import Type

from registrations.domain.dto import HospitalRegistrationEntry
from registrations.domain.hospital.registration import ContactPerson
from registrations.domain.hospital.registration import PhoneNumber
from registrations.domain.hospital.registration import VerificationStatus
from registrations.domain.dto import ToHospitalRegistrationEntry
from registrations.domain.hospital import registration
from registrations.domain.services import hospital_registration_services


Expand All @@ -29,55 +27,17 @@ class HospitalRegistrationApplicationService:
@classmethod
async def register_hospital(
cls,
hospital_uow_async: Type[hospital_registration_services.IUOW],
registration_entry: HospitalRegistrationEntry,
) -> HospitalRegistrationEntry:
hospital_uow_async: Type[hospital_registration_services.InterfaceHospitalUOW],
registration_entry: ToHospitalRegistrationEntry,
) -> ToHospitalRegistrationEntry:
"""Registers a hospital."""
builder_dict = {}
# **************************************************************** #
# 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.
# **************************************************************** #
if registration_entry.verified_status:
builder_dict["verified_status"] = VerificationStatus(
registration_entry.verified_status
)
if registration_entry.key_contact:
builder_dict["key_contact_registrar"] = ContactPerson(
name=registration_entry.key_contact.name,
mobile_number=PhoneNumber(number=registration_entry.key_contact.mobile),
email=registration_entry.key_contact.email,
)
builder_dict = {
**builder_dict,
**dict(
hospital_name=registration_entry.name,
ownership_type=registration_entry.ownership_type,
address=registration_entry.address,
phone_number=PhoneNumber(
number=registration_entry.hospital_contact_number
),
),
}
# Most of the field attributes will be validated by the pydantic library
hospital_entry_dict = registration_entry.build_hospital_entity_dict()
# Most of the domain attributes will be validated by the pydantic library
# for the relevant entry via RegisterHospitalService.
cls._validate_registration_entry(builder_dict)
hospital_entry = hospital_registration_services.RegisterHospitalService.build_hospital_factory(
**builder_dict
**hospital_entry_dict
)
await hospital_registration_services.RegisterHospitalService.register_hospital(
hospital_uow_async, hospital_entry
)
return registration_entry

@classmethod
def _validate_registration_entry(cls, registration_entry_dict: dict) -> bool:
"""Validates a registration entry else raises error."""
# TODO:
# - validate ownership is limited to OwnershipType.
# - validate name has atleast one word with 3 characters.
# - validate entry is either verifiable and hence unclaimed or manual entry.
# - validate verified status if given, is one of VerificationStatus.
return True
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
# Contains interface(ports) definition and implementation.
# ************************************************* #

# See: https://github.com/python/mypy/issues/5374#issuecomment-406218346
IHUOW = TypeVar("IHUOW", bound=Type[InterfaceHospitalUOW])


class InterfaceEmailVerificationService(Protocol):
"""Infrastructure service interface for email verification."""
Expand Down Expand Up @@ -66,7 +69,7 @@ def build_hospital_factory(cls, **kwargs) -> HospitalEntityType:
@classmethod
async def register_hospital(
cls,
hospital_uow_async: Type[InterfaceHospitalUOW],
hospital_uow_async: IHUOW,
hospital_entry: HospitalEntityType,
):
if isinstance(hospital_entry, UnclaimedHospital):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from __future__ import annotations

from typing import Optional

import fastapi

from registrations.domain.dto import HospitalRegistrationEntry
from registrations.domain.dto import RegisterKeyContact
from registrations.domain.dto import ToHospitalRegistrationEntry

router = fastapi.APIRouter(
tags=["hospitals", "registration"],
Expand All @@ -13,16 +15,19 @@
@router.post(
"/register-hospital",
status_code=fastapi.status.HTTP_201_CREATED,
response_model=HospitalRegistrationEntry,
response_model=ToHospitalRegistrationEntry,
)
async def register_hospital_center(
healthcare_data: HospitalRegistrationEntry,
q: Optional[dict[str, str]] = None,
healthcare_data: ToHospitalRegistrationEntry,
q: Optional[dict] = None,
):
# TODO: pass this into HospitalRegistrationApplicationService
# post dependency injection
if q and (verified_status := q.get("verified_status")):
healthcare_data.verified_status = verified_status
elif q and (key_contact := q.get("key_contact")):
elif (
isinstance(q, dict)
and q.keys() == RegisterKeyContact.schema()["properties"].keys()
):
healthcare_data.key_contact = RegisterKeyContact(**q)
return healthcare_data
9 changes: 9 additions & 0 deletions registrations/utils/enum_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import enum


def enum_value_of(enum_class) -> str:
assert isinstance(enum_class, enum.Enum)
return enum_class.value


class EnumWithItems(enum.Enum):
@classmethod
def items(cls) -> dict[str, str]:
return {key.name: key.value for key in cls}

@classmethod
def values(cls) -> list[str]:
return [key.value for key in cls]