Skip to content

Commit 1d9070d

Browse files
authored
Merge pull request #213 from dinesh-aot/regulatory_fix
Regulatory fix
2 parents c408b9e + 5cd007a commit 1d9070d

File tree

12 files changed

+203
-8
lines changed

12 files changed

+203
-8
lines changed

compliance-api/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ install-dev: ## Install local application
6868
ci: lint flake8 test ## CI flow
6969

7070
pylint: ## Linting with pylint
71-
. venv/bin/activate && pylint --rcfile=setup.cfg src/$(PROJECT_NAME)
71+
. venv/bin/activate && pylint --rcfile=setup.cfg --persistent=yes --jobs=4 src/$(PROJECT_NAME)
7272

7373
flake8: ## Linting with flake8
7474
. venv/bin/activate && flake8 --ignore=Q000,W503 src/$(PROJECT_NAME) tests
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""inspection requirement chagne for regulatory considerations
2+
3+
Revision ID: 88ed0885be9e
4+
Revises: c42e6b5815dd
5+
Create Date: 2025-01-29 21:22:17.966961
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '88ed0885be9e'
14+
down_revision = 'c42e6b5815dd'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
inspectionrequirementtypeenum = sa.Enum('REQ', 'REG', name='inspectionrequirementtypeenum')
22+
inspectionrequirementtypeenum.create(op.get_bind())
23+
with op.batch_alter_table('inspection_requirements', schema=None) as batch_op:
24+
batch_op.add_column(sa.Column('agency_id', sa.Integer(), nullable=True, comment='Associated agency if the type is regulatory consideration'))
25+
batch_op.add_column(sa.Column('req_type', sa.Enum('REQ', 'REG', name='inspectionrequirementtypeenum'), nullable=True))
26+
batch_op.create_foreign_key('inspection_req_agency_id', 'agencies', ['agency_id'], ['id'])
27+
28+
with op.batch_alter_table('inspection_requirements_version', schema=None) as batch_op:
29+
batch_op.add_column(sa.Column('agency_id', sa.Integer(), autoincrement=False, nullable=True, comment='Associated agency if the type is regulatory consideration'))
30+
batch_op.add_column(sa.Column('req_type', sa.Enum('REQ', 'REG', name='inspectionrequirementtypeenum'), autoincrement=False, nullable=True))
31+
batch_op.add_column(sa.Column('agency_id_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False))
32+
batch_op.add_column(sa.Column('req_type_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False))
33+
op.execute("UPDATE inspection_requirements SET req_type='REQ'")
34+
op.alter_column('inspection_requirements', 'req_type', nullable=False)
35+
# ### end Alembic commands ###
36+
37+
38+
def downgrade():
39+
# ### commands auto generated by Alembic - please adjust! ###
40+
with op.batch_alter_table('inspection_requirements_version', schema=None) as batch_op:
41+
batch_op.drop_column('req_type_mod')
42+
batch_op.drop_column('agency_id_mod')
43+
batch_op.drop_column('req_type')
44+
batch_op.drop_column('agency_id')
45+
46+
with op.batch_alter_table('inspection_requirements', schema=None) as batch_op:
47+
batch_op.drop_constraint('inspection_req_agency_id', type_='foreignkey')
48+
batch_op.drop_column('req_type')
49+
batch_op.drop_column('agency_id')
50+
51+
# ### end Alembic commands ###

compliance-api/requirements.txt

-2 Bytes
Binary file not shown.

compliance-api/src/compliance_api/models/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
Inspection, InspectionAgency, InspectionAttendance, InspectionAttendanceOption, InspectionAttendanceOptionEnum,
3030
InspectionFirstnation, InspectionInitiationOption, InspectionOfficer, InspectionOtherAttendance,
3131
InspectionReqDetailDocument, InspectionReqEnforcementMap, InspectionReqSourceDetail, InspectionRequirement,
32-
InspectionStatusEnum, InspectionType, InspectionTypeOption, IRStatusOption)
32+
InspectionRequirementTypeEnum, InspectionStatusEnum, InspectionType, InspectionTypeOption, IRStatusOption)
3333
from .position import Position
3434
from .project import Project
3535
from .req_source_document_map import RequirementSourceDocumentMap

compliance-api/src/compliance_api/models/inspection/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
from .inspection_req_detail_doc import InspectionReqDetailDocument
1313
from .inspection_req_enforcement_map import InspectionReqEnforcementMap
1414
from .inspection_req_source_detail import InspectionReqSourceDetail
15-
from .inspection_requirement import InspectionRequirement
15+
from .inspection_requirement import InspectionRequirement, InspectionRequirementTypeEnum
1616
from .inspection_type import InspectionType

compliance-api/src/compliance_api/models/inspection/inspection_requirement.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
"""InspectionRequirement Model."""
22

3-
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
3+
import enum
4+
5+
from sqlalchemy import Boolean, Column, Enum, ForeignKey, Integer, String
46
from sqlalchemy.orm import relationship
57

68
from compliance_api.utils.constant import DELETE_DIC_PARAMS
79

810
from ..base_model import BaseModelVersioned, db
911

1012

13+
class InspectionRequirementTypeEnum(enum.Enum):
14+
"""Type of inspection requirements."""
15+
16+
REQ = "Requirement"
17+
REG = "Regulatory Consideration"
18+
19+
1120
class InspectionRequirement(BaseModelVersioned):
1221
"""InspectionRequirementModel."""
1322

@@ -32,6 +41,17 @@ class InspectionRequirement(BaseModelVersioned):
3241
nullable=False,
3342
comment="The topic of the requirement",
3443
)
44+
agency_id = Column(
45+
Integer,
46+
ForeignKey("agencies.id", name="inspection_req_agency_id"),
47+
nullable=True,
48+
comment="Associated agency if the type is regulatory consideration",
49+
)
50+
req_type = Column(
51+
Enum(InspectionRequirementTypeEnum),
52+
nullable=False,
53+
default=InspectionRequirementTypeEnum.REG,
54+
)
3555
compliance_finding_id = Column(
3656
Integer,
3757
ForeignKey(
@@ -50,6 +70,9 @@ class InspectionRequirement(BaseModelVersioned):
5070
compliance_finding = relationship(
5171
"ComplianceFindingOption", foreign_keys=[compliance_finding_id], lazy="joined"
5272
)
73+
agency = relationship(
74+
"Agency", foreign_keys=[agency_id], lazy="joined"
75+
)
5376
requirement_source_details = relationship(
5477
"InspectionReqSourceDetail",
5578
back_populates="inspection_requirement",

compliance-api/src/compliance_api/resources/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from .enforcement_action import API as ENFORCEMENT_ACTION_API
3434
from .inspection import API as INSPECTION_API
3535
from .inspection_requirement import API as INSPECTION_REQUIREMENT_API
36+
from .inspection_requirement_type import API as REQUIREMENT_TYPE_API
3637
from .ops import API as OPS_API
3738
from .position import API as POSITION_API
3839
from .project import API as PROJECT_API
@@ -87,5 +88,8 @@
8788
API.add_namespace(CONTINUATION_REPORT_API)
8889
API.add_namespace(ENFORCEMENT_ACTION_API)
8990
API.add_namespace(COMPLIANCE_FINDING_API)
90-
API.add_namespace(INSPECTION_REQUIREMENT_API, path="inspections/<int:inspection_id>/requirements")
91+
API.add_namespace(
92+
INSPECTION_REQUIREMENT_API, path="inspections/<int:inspection_id>/requirements"
93+
)
9194
API.add_namespace(DOCUMENT_TYPE_API)
95+
API.add_namespace(REQUIREMENT_TYPE_API)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright © 2024 Province of British Columbia
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the 'License');
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an 'AS IS' BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""API endpoints for managing Inspection Requirement Type."""
15+
16+
from http import HTTPStatus
17+
18+
from flask_restx import Namespace, Resource
19+
20+
from compliance_api.auth import auth
21+
from compliance_api.schemas import KeyValueSchema
22+
from compliance_api.services import InspectionRequirementTypeService
23+
from compliance_api.utils.util import cors_preflight
24+
25+
from .apihelper import Api as ApiHelper
26+
27+
28+
API = Namespace(
29+
"inspection-requirement-types",
30+
description="Endpoints for Inspection Requirement Type",
31+
)
32+
33+
key_value_list_model = ApiHelper.convert_ma_schema_to_restx_model(
34+
API, KeyValueSchema(), "List"
35+
)
36+
37+
38+
@cors_preflight("GET, OPTIONS, POST")
39+
@API.route("", methods=["POST", "GET", "OPTIONS"])
40+
class InspectionRequirementTypes(Resource):
41+
"""Resource for inspection requirement types."""
42+
43+
@staticmethod
44+
@API.response(code=200, description="Success", model=[key_value_list_model])
45+
@ApiHelper.swagger_decorators(
46+
API, endpoint_description="Fetch all insection requirement types"
47+
)
48+
@auth.require
49+
def get():
50+
"""Fetch all inspection requirement types."""
51+
types = InspectionRequirementTypeService.get_inspection_requirement_types()
52+
type_schema = KeyValueSchema(many=True)
53+
return type_schema.dump(types), HTTPStatus.OK

compliance-api/src/compliance_api/schemas/inspection_requirement.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
"""Inspection requirement Schema Schema."""
15-
from marshmallow import EXCLUDE, ValidationError, fields, pre_dump, validates_schema
15+
from marshmallow import EXCLUDE, ValidationError, fields, post_dump, pre_dump, validates_schema
16+
from marshmallow_enum import EnumField
1617

17-
from compliance_api.models import InspectionReqDetailDocument, InspectionReqSourceDetail, InspectionRequirement
18+
from compliance_api.models import (
19+
EnforcementActionOptionEnum, InspectionReqDetailDocument, InspectionReqSourceDetail, InspectionRequirement,
20+
InspectionRequirementTypeEnum)
1821
from compliance_api.models.requirement_source import RequirementSourceEnum
1922

2023
from .base_schema import AutoSchemaBase, BaseSchema
@@ -153,6 +156,11 @@ class InspectionReqSourceDetailUpdateSchema(InspectionReqSourceDetailCreateSchem
153156
class InspectionRequirementCreateSchema(BaseSchema):
154157
"""InspectionRequirementCreateSchema."""
155158

159+
req_type = EnumField(
160+
InspectionRequirementTypeEnum,
161+
metadata={"description": "The type of inspection requirement"},
162+
required=True,
163+
)
156164
summary = fields.Str(
157165
metadata={"description": "The summary of the requirement."}, required=True
158166
)
@@ -162,13 +170,20 @@ class InspectionRequirementCreateSchema(BaseSchema):
162170
},
163171
required=True,
164172
)
173+
agency_id = fields.Int(
174+
metadata={
175+
"description": "The unique identifier of the agency only if the requirement"
176+
"type is regulatory considerations"
177+
},
178+
allow_none=True,
179+
)
165180
enforcement_action_ids = fields.List(
166181
fields.Int(metadata={"description": "The enforcement action identifier."}),
167182
allow_none=True,
168183
)
169184
compliance_finding_id = fields.Int(
170185
metadata={"description": "The unique identifier of the compliance findings."},
171-
allow_none=True
186+
allow_none=True,
172187
)
173188
findings = fields.Str(
174189
metadata={"description": "The requirement findings in html format."},
@@ -178,6 +193,29 @@ class InspectionRequirementCreateSchema(BaseSchema):
178193
fields.Nested(InspectionReqSourceDetailCreateSchema)
179194
)
180195

196+
@validates_schema
197+
def validate_agency_id(
198+
self, data, **kwargs
199+
): # pylint: disable=no-self-use, unused-argument
200+
"""Validate the agency if the requirement type is regulatory considerations."""
201+
req_type = data.get("req_type")
202+
agency_id = data.get("agency_id", None)
203+
if req_type == InspectionRequirementTypeEnum.REG and not agency_id:
204+
raise ValidationError(
205+
"Agency is required if the requirement type is Regulatory Consideration",
206+
field_name="agency_id",
207+
)
208+
enforcement_action_ids = data.get("enforcement_action_ids")
209+
if (
210+
EnforcementActionOptionEnum.REFERRAL_TO_ANOTHER_AGENCY
211+
in enforcement_action_ids
212+
and not agency_id
213+
):
214+
raise ValidationError(
215+
"Agency is required if the enforcement actions include REFERRAL_TO_ANOTHER_AGENCY",
216+
field_name="agency_id",
217+
)
218+
181219

182220
class InspectionRequirementUpdateSchema(InspectionRequirementCreateSchema):
183221
"""InspectionRequirementUpdateSchema."""
@@ -247,6 +285,7 @@ class Meta(AutoSchemaBase.Meta): # pylint: disable=too-few-public-methods
247285
)
248286
topic = fields.Nested(KeyValueSchema)
249287
compliance_finding = fields.Nested(KeyValueSchema)
288+
agency = fields.Nested(KeyValueSchema)
250289
enforcement_action_data = fields.List(fields.Nested(KeyValueSchema))
251290

252291
@pre_dump
@@ -266,3 +305,14 @@ def pre_dump_enforcement_actions(
266305
)
267306
obj.enforcement_action_data = prepared_enforcement_actions
268307
return obj
308+
309+
@post_dump
310+
def nullify_nested(
311+
self, data, **kwargs
312+
): # pylint: disable=no-self-use, unused-argument
313+
"""Make nested objects null if the referenced ID is null."""
314+
data["req_type"] = {
315+
"id": data.get("req_type").name,
316+
"name": data.get("req_type").value,
317+
}
318+
return data

compliance-api/src/compliance_api/services/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from .enforcement_action import EnforcementActionService
2222
from .inspection import InspectionService
2323
from .inspection_requirement import InspectionRequirementService
24+
from .inspection_requirement_type import InspectionRequirementTypeService
2425
from .position import PositionService
2526
from .project import ProjectService
2627
from .project_status import ProjectStatusService

0 commit comments

Comments
 (0)