Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 3 additions & 25 deletions application/common/constants.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,10 @@
PHARMACY_SERVICE_KEY = "PHARMACY"
DENTIST_SERVICE_KEY = "DENTIST"
PHARMACY_ORG_TYPE_ID = "PHA"
DENTIST_ORG_TYPE_ID = "Dentist"
SERVICE_TYPES_ALIAS_KEY = "SERVICE_TYPE_NAME"
ORGANISATION_SUB_TYPES_KEY = "ORGANISATION_SUB_TYPES"
VALID_SERVICE_TYPES_KEY = "VALID_SERVICE_TYPES"
ODSCODE_LENGTH_KEY = "ODSCODE_LENGTH"

CLOSED_AND_HIDDEN_STATUSES = ["HIDDEN", "CLOSED"]

SERVICE_TYPES = {
PHARMACY_ORG_TYPE_ID: {
SERVICE_TYPES_ALIAS_KEY: PHARMACY_SERVICE_KEY,
ORGANISATION_SUB_TYPES_KEY: ["Community"],
VALID_SERVICE_TYPES_KEY: [13, 131, 132, 134, 137, 148, 149],
ODSCODE_LENGTH_KEY: 5,
},
DENTIST_ORG_TYPE_ID: {
SERVICE_TYPES_ALIAS_KEY: DENTIST_SERVICE_KEY,
ORGANISATION_SUB_TYPES_KEY: ["TBA"],
VALID_SERVICE_TYPES_KEY: [12],
ODSCODE_LENGTH_KEY: 7,
},
}

DENTIST_SERVICE_TYPE_IDS = SERVICE_TYPES[DENTIST_ORG_TYPE_ID][VALID_SERVICE_TYPES_KEY]
PHARMACY_SERVICE_TYPE_IDS = SERVICE_TYPES[PHARMACY_ORG_TYPE_ID][VALID_SERVICE_TYPES_KEY]

PHARMACY_SERVICE_TYPE_IDS = [13, 131, 132, 134, 137, 148, 149]
PHARMACY_ORGANISATION_SUB_TYPES = ["Community"]
PHARMACY_ODSCODE_LENGTH = 5
PHARMACY_SERVICE_TYPE_ID = 13

DOS_DEMOGRAPHICS_AREA_TYPE = "demographic"
Expand Down
4 changes: 0 additions & 4 deletions application/common/dos.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from psycopg import Connection

from .constants import (
DENTIST_ORG_TYPE_ID,
DOS_PALLIATIVE_CARE_SYMPTOM_DISCRIMINATOR,
DOS_PALLIATIVE_CARE_SYMPTOM_GROUP,
PHARMACY_ORG_TYPE_ID,
Expand Down Expand Up @@ -108,9 +107,6 @@ def get_matching_dos_services(odscode: str, org_type_id: str) -> list[DoSService
if org_type_id == PHARMACY_ORG_TYPE_ID:
conditions = "odscode LIKE %(ODS)s"
named_args = {"ODS": f"{odscode[:5]}%"}
elif org_type_id == DENTIST_ORG_TYPE_ID:
conditions = "odscode = %(ODS)s or odscode LIKE %(ODS7)s"
named_args = {"ODS": f"{odscode[0] + odscode[2:]}", "ODS7": f"{odscode[:7]}%"}
else:
conditions = "odscode = %(ODS)s"
named_args = {"ODS": f"{odscode}%"}
Expand Down
7 changes: 0 additions & 7 deletions application/common/nhs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from common.constants import (
CLOSED_AND_HIDDEN_STATUSES,
DENTIST_SERVICE_TYPE_IDS,
NHS_UK_PALLIATIVE_CARE_SERVICE_CODE,
PHARMACY_SERVICE_TYPE_IDS,
)
Expand Down Expand Up @@ -193,12 +192,6 @@ def is_matching_dos_service(self, dos_service: DoSService) -> bool:
and dos_service.odscode[:5] == self.odscode[:5]
)

if dos_service.typeid in DENTIST_SERVICE_TYPE_IDS:
if len(dos_service.odscode) < 6 or len(self.odscode) < 7: # noqa: PLR2004
return False
odscode_extra_0 = f"{dos_service.odscode[0]}0{dos_service.odscode[1:]}"
return self.odscode[:7] in (dos_service.odscode[:7], odscode_extra_0[:7])

logger.warning(f"Failed nhs code match check for unknown typeid '{dos_service.typeid}'")
return False

Expand Down
36 changes: 0 additions & 36 deletions application/common/tests/test_dos.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from application.common.opening_times import OpenPeriod, SpecifiedOpeningTime, StandardOpeningTimes
from application.conftest import dummy_dos_service
from common.constants import (
DENTIST_ORG_TYPE_ID,
DOS_PALLIATIVE_CARE_SYMPTOM_DISCRIMINATOR,
DOS_PALLIATIVE_CARE_SYMPTOM_GROUP,
PHARMACY_ORG_TYPE_ID,
Expand Down Expand Up @@ -192,41 +191,6 @@ def test_any_generic_bankholiday_open_periods():
assert dos_service.any_generic_bankholiday_open_periods() is False


@patch(f"{FILE_PATH}.connect_to_dos_db_replica")
@patch(f"{FILE_PATH}.query_dos_db")
def test_get_matching_dos_services_dentist_services_returned(mock_query_dos_db, mock_connect_to_dos_db_replica):
# Arrange
odscode = "V00393a"
name = "My Dental Practice"
service_id = 22851351399
db_return = [get_db_item(odscode, name, id=service_id)]
mock_connection = MagicMock()
mock_connect_to_dos_db_replica.return_value.__enter__.return_value = mock_connection
mock_cursor = MagicMock()
mock_cursor.fetchall.return_value = db_return
mock_query_dos_db.return_value = mock_cursor
ods6_code = "V0393a"
# Act
response = get_matching_dos_services(odscode, DENTIST_ORG_TYPE_ID)
# Assert
service = response[0]
assert service.odscode == odscode
assert service.id == service_id
assert service.name == name
mock_query_dos_db.assert_called_once_with(
connection=mock_connection,
query=(
"SELECT s.id, uid, s.name, odscode, address, postcode, web, typeid,"
"statusid, publicphone, publicname, st.name service_type_name "
"FROM services s LEFT JOIN servicetypes st ON s.typeid = st.id "
"WHERE odscode = %(ODS)s or odscode LIKE %(ODS7)s"
),
query_vars={"ODS": f"{ods6_code}", "ODS7": f"{odscode}%"},
)
mock_cursor.fetchall.assert_called_with()
mock_cursor.close.assert_called_with()


@patch(f"{FILE_PATH}.connect_to_dos_db_replica")
@patch(f"{FILE_PATH}.query_dos_db")
def test_get_matching_dos_services_no_services_returned(mock_query_dos_db, mock_connect_to_dos_db_replica):
Expand Down
44 changes: 4 additions & 40 deletions application/common/tests/test_nhs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
skip_if_key_is_none,
)
from application.conftest import PHARMACY_STANDARD_EVENT, dummy_dos_service
from common.constants import DENTIST_SERVICE_TYPE_IDS, PHARMACY_SERVICE_TYPE_IDS
from common.constants import PHARMACY_SERVICE_TYPE_IDS
from common.opening_times import OpenPeriod, SpecifiedOpeningTime, StandardOpeningTimes

test_attr_names = ("odscode", "website", "PublicPhone", "Phone", "Postcode")
DENTIST_SERVICE_ID = DENTIST_SERVICE_TYPE_IDS[0]
PHARMACY_SERVICE_ID = PHARMACY_SERVICE_TYPE_IDS[0]


Expand Down Expand Up @@ -559,7 +558,7 @@ def test_is_spec_opening_json(open_time_json, expected):
assert actual == expected, f"Spec time should be valid={expected} but wasn't. open_time={open_time_json}"


def test_is_matching_dos_service(): # noqa: PLR0915
def test_is_matching_dos_service():
nhs_entity = NHSEntity({})
dos_service = dummy_dos_service()

Expand Down Expand Up @@ -596,43 +595,8 @@ def test_is_matching_dos_service(): # noqa: PLR0915
dos_service.odscode = "1ABCDEFGHI"
assert nhs_entity.is_matching_dos_service(dos_service)

dos_service.typeid = DENTIST_SERVICE_TYPE_IDS[0]
assert nhs_entity.is_matching_dos_service(dos_service) is False

nhs_entity.odscode = "VABCDEU"
dos_service.odscode = "1ABCDEFGHI"
assert nhs_entity.is_matching_dos_service(dos_service) is False

nhs_entity.odscode = "VABCDEU"
dos_service.odscode = "VABCDEU123"
assert nhs_entity.is_matching_dos_service(dos_service)

nhs_entity.odscode = "VBCDEU"
dos_service.odscode = "VABCDEU123"
assert nhs_entity.is_matching_dos_service(dos_service) is False

nhs_entity.odscode = "V0ABCDE"
dos_service.odscode = "VABCDEU123"
assert nhs_entity.is_matching_dos_service(dos_service)

nhs_entity.odscode = "VABCDEU"
dos_service.odscode = "VABCDEU123"
assert nhs_entity.is_matching_dos_service(dos_service)

nhs_entity.odscode = ""
dos_service.odscode = "VABCDEU123"
assert nhs_entity.is_matching_dos_service(dos_service) is False

nhs_entity.odscode = "VABCDEU"
dos_service.odscode = ""
assert nhs_entity.is_matching_dos_service(dos_service) is False

nhs_entity.odscode = "VABCDEU"
dos_service.odscode = "VABCDEU123"
assert nhs_entity.is_matching_dos_service(dos_service)

nhs_entity.odscode = "VABCDEU"
dos_service.odscode = "VABCDEU123"
nhs_entity.odscode = "VABCDU"
dos_service.odscode = "VABCDU123"
dos_service.typeid = 324634324
assert nhs_entity.is_matching_dos_service(dos_service) is False

Expand Down
29 changes: 11 additions & 18 deletions application/ingest_change_event/change_event_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@

from .appconfig import AppConfig
from common.constants import (
DENTIST_ORG_TYPE_ID,
ODSCODE_LENGTH_KEY,
ORGANISATION_SUB_TYPES_KEY,
PHARMACY_ODSCODE_LENGTH,
PHARMACY_ORG_TYPE_ID,
SERVICE_TYPES,
SERVICE_TYPES_ALIAS_KEY,
PHARMACY_ORGANISATION_SUB_TYPES,
)
from common.errors import ValidationError

Expand All @@ -30,11 +27,13 @@ def validate_change_event(event: dict[str, Any]) -> None:
except SchemaValidationError as exception:
raise ValidationError(exception) from exception
validate_organisation_keys(event.get("OrganisationTypeId"), event.get("OrganisationSubType"))
check_ods_code_length(event["ODSCode"], SERVICE_TYPES[event["OrganisationTypeId"]][ODSCODE_LENGTH_KEY])
check_ods_code_length(event["ODSCode"])
logger.info("Event has been validated")


def check_ods_code_length(odscode: str, odscode_length: int) -> None:
def check_ods_code_length(
odscode: str,
) -> None:
"""Check ODS code length as expected, exception raise if error.

Note: ods code type is checked by schema validation
Expand All @@ -44,8 +43,8 @@ def check_ods_code_length(odscode: str, odscode_length: int) -> None:
odscode_length (int): expected length of odscode.
"""
logger.debug(f"Checking ODSCode {odscode} length")
if len(odscode) != odscode_length:
msg = f"ODSCode Wrong Length, '{odscode}' is not length {odscode_length}."
if len(odscode) != PHARMACY_ODSCODE_LENGTH:
msg = f"ODSCode Wrong Length, '{odscode}' is not length {PHARMACY_ODSCODE_LENGTH}."
raise ValidationError(msg)


Expand All @@ -60,7 +59,7 @@ def validate_organisation_keys(org_type_id: str, org_sub_type: str) -> None:
ValidationError: Either Org Type ID or Org Sub Type is not part of the valid list
"""
validate_organisation_type_id(org_type_id)
if org_sub_type in SERVICE_TYPES[org_type_id][ORGANISATION_SUB_TYPES_KEY]:
if org_sub_type in PHARMACY_ORGANISATION_SUB_TYPES:
logger.info(f"Subtype type id: {org_sub_type} validated")
else:
msg = f"Unexpected Org Sub Type ID: '{org_sub_type}'"
Expand All @@ -81,15 +80,9 @@ def validate_organisation_type_id(org_type_id: str) -> None:
default=False,
)
logger.debug(f"Accepted org types: {in_accepted_org_types}")
if (
org_type_id == PHARMACY_ORG_TYPE_ID
and in_accepted_org_types
or org_type_id == DENTIST_ORG_TYPE_ID
and in_accepted_org_types
):
logger.append_keys(service_type_alias=SERVICE_TYPES[org_type_id][SERVICE_TYPES_ALIAS_KEY])
if org_type_id == PHARMACY_ORG_TYPE_ID and in_accepted_org_types:
logger.info(
f"Org type id: {org_type_id} validated",
f"Org type id: {org_type_id} validated as a pharmacy",
extra={"in_accepted_org_types": in_accepted_org_types},
)
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
validate_organisation_keys,
validate_organisation_type_id,
)
from common.constants import DENTIST_ORG_TYPE_ID, PHARMACY_ORG_TYPE_ID
from common.constants import PHARMACY_ORG_TYPE_ID

FILE_PATH = "application.ingest_change_event.change_event_validation"

Expand All @@ -34,41 +34,36 @@ def test_validate_change_event_missing_key(mock_check_ods_code_length, mock_vali


@pytest.mark.parametrize(
("odscode", "odscode_length"),
("odscode"),
[
("FXXX1", 5),
("AAAAA", 5),
("00000", 5),
("V001234", 7),
("FXXX1"),
("AAAAA"),
("00000"),
],
)
def test_check_ods_code_length(odscode, odscode_length):
def test_check_ods_code_length(odscode):
# Act & Assert
check_ods_code_length(odscode, odscode_length)
check_ods_code_length(odscode)


@pytest.mark.parametrize(
("odscode", "odscode_length"),
("odscode"),
[
("FXXX11", 5),
("AAAA", 5),
("V0345", 7),
("V01234567", 7),
("FXXX11"),
("AAAA"),
("V0345A"),
("V01234567"),
],
)
def test_check_ods_code_length_incorrect_length(odscode, odscode_length):
def test_check_ods_code_length_incorrect_length(odscode):
# Act & Assert
with pytest.raises(ValidationError):
check_ods_code_length(odscode, odscode_length)
check_ods_code_length(odscode)


@pytest.mark.parametrize(
("org_type_id", "org_sub_type"),
[
(
"Dentist",
"TBA",
),
(
"PHA",
"Community",
Expand All @@ -89,7 +84,7 @@ def test_validate_organisation_keys(
("org_type_id", "org_sub_type"),
[
(
"Dentist",
"GP",
"RANDOM",
),
(
Expand All @@ -110,7 +105,7 @@ def test_validate_organisation_keys_org_sub_type_id_exception(
assert f"Unexpected Org Sub Type ID: '{org_sub_type}'" in str(exception.value)


@pytest.mark.parametrize("org_type_id", [PHARMACY_ORG_TYPE_ID, DENTIST_ORG_TYPE_ID])
@pytest.mark.parametrize("org_type_id", [PHARMACY_ORG_TYPE_ID])
@patch(f"{FILE_PATH}.AppConfig")
def test_validate_organisation_type_id(mock_app_config, org_type_id):
# Arrange
Expand All @@ -127,7 +122,7 @@ def test_validate_organisation_type_id(mock_app_config, org_type_id):
)


@pytest.mark.parametrize("org_type_id", [PHARMACY_ORG_TYPE_ID, DENTIST_ORG_TYPE_ID])
@pytest.mark.parametrize("org_type_id", [PHARMACY_ORG_TYPE_ID])
@patch(f"{FILE_PATH}.AppConfig")
def test_validate_organisation_type_id_wrong_org_type_id_exception(mock_app_config, org_type_id):
# Arrange
Expand Down
17 changes: 4 additions & 13 deletions application/service_matcher/service_matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
log_unmatched_nhsuk_service,
log_unmatched_service_types,
)
from .service_type import get_valid_service_types
from common.constants import DENTIST_ORG_TYPE_ID, PHARMACY_ORG_TYPE_ID, PHARMACY_SERVICE_TYPE_ID
from common.constants import PHARMACY_SERVICE_TYPE_ID, PHARMACY_SERVICE_TYPE_IDS
from common.dos import VALID_STATUS_ID, DoSService, get_matching_dos_services
from common.middlewares import unhandled_exception_logging
from common.nhs import NHSEntity
Expand Down Expand Up @@ -140,7 +139,7 @@ def get_matching_services(nhs_entity: NHSEntity) -> list[DoSService]:

# Filter for matched and unmatched service types and valid status
matching_services, non_matching_services = [], []
valid_service_types = get_valid_service_types(nhs_entity.org_type_id)
valid_service_types = PHARMACY_SERVICE_TYPE_IDS
for service in matching_dos_services:
if int(service.statusid) == VALID_STATUS_ID:
if int(service.typeid) in valid_service_types:
Expand All @@ -150,17 +149,9 @@ def get_matching_services(nhs_entity: NHSEntity) -> list[DoSService]:
if non_matching_services:
log_unmatched_service_types(nhs_entity, non_matching_services)

if nhs_entity.org_type_id == PHARMACY_ORG_TYPE_ID:
logger.info(
f"Found {len(matching_dos_services)} services in DB with "
f"matching first 5 chars of ODSCode: {matching_dos_services}",
)
elif nhs_entity.org_type_id == DENTIST_ORG_TYPE_ID:
logger.info(f"Found {len(matching_dos_services)} services in DB with matching ODSCode: {matching_dos_services}")
logger.info(
f"Found {len(matching_services)} services with typeid in "
f"allowlist {valid_service_types} and status id = "
f"{VALID_STATUS_ID}: {matching_services}",
f"Found {len(matching_dos_services)} services in DB with "
f"matching first 5 chars of ODSCode: {matching_dos_services}",
)

return matching_services
Expand Down
Loading