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
10 changes: 5 additions & 5 deletions jobs/batch-permit-validator/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion jobs/batch-permit-validator/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ gunicorn = "^21.2.0"
pg8000 = "^1.31.2"
gcp-queue = { git = "https://github.com/bcgov/sbc-connect-common.git", subdirectory = "python/gcp-queue", branch = "main" }
structured-logging = { git = "https://github.com/bcgov/sbc-connect-common.git", subdirectory = "python/structured-logging", branch = "main" }
strr-api = {git = "https://github.com/bcgov/STRR.git", rev = "main", subdirectory = "strr-api"}
strr-api = {git = "https://github.com/bcgov/STRR.git", branch = "feature-provicinal-workflow", subdirectory = "strr-api"}
nanoid = "^2.0.0"
flake8-pyproject = "^1.2.3"
redis = "^5.2.1"
Expand Down
2 changes: 1 addition & 1 deletion strr-api/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "strr-api"
version = "0.0.74"
version = "0.0.77"
description = ""
authors = ["thorwolpert <thor@wolpert.ca>"]
license = "BSD 3-Clause"
Expand Down
2 changes: 2 additions & 0 deletions strr-api/src/strr_api/services/application_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
Application.Status.PROVISIONAL,
Application.Status.NOC_PENDING,
Application.Status.NOC_EXPIRED,
Application.Status.PROVISIONAL_REVIEW_NOC_PENDING,
Application.Status.PROVISIONAL_REVIEW_NOC_EXPIRED,
Application.Status.ADDITIONAL_INFO_REQUESTED,
Application.Status.FULL_REVIEW_APPROVED,
Application.Status.PROVISIONALLY_APPROVED,
Expand Down
91 changes: 64 additions & 27 deletions strr-api/src/strr_api/services/validation_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# pylint: disable=R0914
"""Permit Validation Service."""
import copy
import json
Expand Down Expand Up @@ -92,56 +93,70 @@ def check_permit_details(cls, request_json: dict, registration: Registration):
if registration.registration_type == RegistrationType.HOST.value:
if (
registration.rental_property.address.street_number
and str(address_json.get("streetNumber")) != registration.rental_property.address.street_number
and str(address_json.get("streetNumber")).lower()
!= registration.rental_property.address.street_number.lower()
):
errors.append(
{
"code": ErrorMessage.STREET_NUMBER_MISMATCH.name,
"message": ErrorMessage.STREET_NUMBER_MISMATCH.value,
}
)
if (
address_json.get("postalCode", "").replace(" ", "").lower()
!= registration.rental_property.address.postal_code.replace(" ", "").lower()

# Postal code validation
request_postal_code = address_json.get("postalCode", "").replace(" ", "").lower()
permit_postal_code = registration.rental_property.address.postal_code.replace(" ", "").lower()
if not (
request_postal_code == permit_postal_code
or (len(request_postal_code) >= 4 and (request_postal_code[:4] == permit_postal_code[:4]))
):
errors.append(
{"code": ErrorMessage.POSTAL_CODE_MISMATCH.name, "message": ErrorMessage.POSTAL_CODE_MISMATCH.value}
)

if unit_number := address_json.get("unitNumber", None):
reg_unit_number = registration.rental_property.address.unit_number
if reg_unit_number and reg_unit_number.startswith("#"):
reg_unit_number = reg_unit_number[1:]
if unit_number and unit_number.startswith("#"):
unit_number = unit_number[1:]
if unit_number != reg_unit_number:
errors.append(
{
"code": ErrorMessage.UNIT_NUMBER_MISMATCH.name,
"message": ErrorMessage.UNIT_NUMBER_MISMATCH.value,
}
)
# Unit number validation.
has_unit_number_validation_error = False
if input_unit_number := address_json.get("unitNumber", None):
if permit_unit_number := registration.rental_property.address.unit_number:
filtered_input_unit_number = ValidationService._get_filtered_unit_number(input_unit_number)
filtered_permit_unit_number = ValidationService._get_filtered_unit_number(permit_unit_number)
if filtered_input_unit_number != filtered_permit_unit_number:
has_unit_number_validation_error = True
else:
has_unit_number_validation_error = True

else:
if registration.rental_property.address.unit_number:
has_unit_number_validation_error = True

if has_unit_number_validation_error:
errors.append(
{
"code": ErrorMessage.UNIT_NUMBER_MISMATCH.name,
"message": ErrorMessage.UNIT_NUMBER_MISMATCH.value,
}
)

if registration.registration_type == RegistrationType.STRATA_HOTEL.value:
match_found = False
strata_hotel = registration.strata_hotel_registration.strata_hotel
location = strata_hotel.location

if (
str(address_json.get("streetNumber"))
== str(cls._extract_street_number(cls._get_text_after_hyphen(location.street_address)))
and address_json.get("postalCode", "").replace(" ", "").lower()
== location.postal_code.replace(" ", "").lower()
if str(address_json.get("streetNumber")) == str(
cls._extract_street_number(cls._get_text_after_hyphen(location.street_address))
) and (
address_json.get("postalCode", "").replace(" ", "").lower()[:4]
== location.postal_code.replace(" ", "").lower()[:4]
):
match_found = True

if not match_found:
for building in strata_hotel.buildings:
if (
str(address_json.get("streetNumber"))
== str(cls._extract_street_number(cls._get_text_after_hyphen(building.address.street_address)))
and address_json.get("postalCode", "").replace(" ", "").lower()
== building.address.postal_code.replace(" ", "").lower()
if str(address_json.get("streetNumber")) == str(
cls._extract_street_number(cls._get_text_after_hyphen(building.address.street_address))
) and (
address_json.get("postalCode", "").replace(" ", "").lower()[:4]
== building.address.postal_code.replace(" ", "").lower()[:4]
):
match_found = True
break
Expand All @@ -162,6 +177,28 @@ def check_permit_details(cls, request_json: dict, registration: Registration):
response["validUntil"] = DateUtil.as_legislation_timezone(registration.expiry_date).strftime("%Y-%m-%d")
return response, status_code

@classmethod
def _get_filtered_unit_number(cls, unit_number):
# Remove leading # or -
unit_number = re.sub(r"^[#-]+\s*", "", unit_number, flags=re.IGNORECASE)

# Remove keywords (case-insensitive): Suite, Unit, SL, Strata Lot, Room, Cabin, No.
unit_number = re.sub(
r"\b(Suite|Unit|SL|Strata Lot|Room|Lot|RM|Cabin|Bldg|ste|Nbr|Unt|Apartment|Apt|Number|Num|Floor|Flr|Fl|BUILDING|No\.?)", # noqa: E501
"",
unit_number,
flags=re.IGNORECASE,
)

# Remove all hyphens and spaces (including in-between)
unit_number = re.sub(r"[-.\s]+", "", unit_number, flags=re.IGNORECASE)

# Remove standalone leading zeros
unit_number = re.sub(r"\b0+(\w+)", r"\1", unit_number, flags=re.IGNORECASE)

# Convert to uppercase
return unit_number.upper()

@classmethod
def _get_text_after_hyphen(cls, address_line):
if "-" in address_line:
Expand Down
16 changes: 15 additions & 1 deletion strr-api/tests/unit/resources/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def test_permit_details_mismatch(session, client, jwt):

validate_permit_request = {
"identifier": registration_number,
"address": {"streetNumber": "12165", "postalCode": "V2X 7N2"},
"address": {"streetNumber": "12165", "postalCode": "V2X 555"},
}
rv = client.post("/permits/:validatePermit", json=validate_permit_request, headers=headers)
assert rv.status_code == HTTPStatus.BAD_REQUEST
Expand All @@ -125,6 +125,20 @@ def test_permit_details_mismatch(session, client, jwt):
response_json.get("errors")[1].get("message") == "Postal code does not match with the data in the permit."
)

validate_permit_request = {
"identifier": registration_number,
"address": {"streetNumber": "12165", "postalCode": "V2X 7N2"},
}
rv = client.post("/permits/:validatePermit", json=validate_permit_request, headers=headers)
assert rv.status_code == HTTPStatus.BAD_REQUEST
response_json = rv.json

assert len(response_json.get("errors")) == 1
assert response_json.get("errors")[0].get("code") == "STREET_NUMBER_MISMATCH"
assert (
response_json.get("errors")[0].get("message") == "Street number does not match with the data in the permit."
)


def test_invalid_request_with_identifier(session, client, jwt):
headers = create_header(jwt, [STRR_EXAMINER], "Account-Id")
Expand Down
Loading