Skip to content

Commit

Permalink
Merge pull request bcgov#138 from dinesh-aot/COMP-252
Browse files Browse the repository at this point in the history
Update and Delete endpoints for ContinuationReport
  • Loading branch information
dinesh-aot authored Nov 18, 2024
2 parents 46927b2 + 5595693 commit 5180d23
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 36 deletions.
47 changes: 45 additions & 2 deletions compliance-api/src/compliance_api/resources/continuation_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
from flask_restx import Namespace, Resource

from compliance_api.auth import auth
from compliance_api.schemas import ContinuationReportCreateSchema, ContinuationReportSchema, CRGetQueryParamSchema
from compliance_api.exceptions import ResourceNotFoundError
from compliance_api.schemas import (
ContinuationReportCreateSchema, ContinuationReportSchema, ContinuationReportUpdateSchema, CRGetQueryParamSchema)
from compliance_api.services import ContinuationReportService
from compliance_api.utils.util import cors_preflight

Expand All @@ -21,11 +23,14 @@
cr_list_model = ApiHelper.convert_ma_schema_to_restx_model(
API, ContinuationReportSchema(), "ContinuationReportList"
)
cr_update_model = ApiHelper.convert_ma_schema_to_restx_model(
API, ContinuationReportUpdateSchema(), "UpdateSchema"
)


@cors_preflight("GET, OPTIONS, POST")
@API.route("", methods=["POST", "GET", "OPTIONS"])
class Agencies(Resource):
class ContinuationReports(Resource):
"""Resource for managing continuation report."""

@staticmethod
Expand Down Expand Up @@ -81,3 +86,41 @@ def post():
report_data = ContinuationReportCreateSchema().load(API.payload)
created_entry = ContinuationReportService.create(report_data)
return ContinuationReportSchema().dump(created_entry), HTTPStatus.CREATED


@cors_preflight("OPTIONS, DELETE, PATCH")
@API.route("/<int:entry_id>", methods=["PATCH", "DELETE", "OPTIONS"])
@API.doc(params={"entry_id": "The unique identifier of continuation report entry"})
class ContinuationReport(Resource):
"""Resource for managing a single continuation report entry."""

@staticmethod
@auth.require
@ApiHelper.swagger_decorators(
API, endpoint_description="Update a continuation report entry by id"
)
@API.expect(cr_request_model)
@API.response(code=200, model=cr_list_model, description="Success")
@API.response(400, "Bad Request")
@API.response(404, "Not Found")
def patch(entry_id):
"""Update an agency by id."""
cr_data = ContinuationReportUpdateSchema().load(API.payload)
updated_cr = ContinuationReportService.update(entry_id, cr_data)
if not updated_cr:
raise ResourceNotFoundError(
f"Continuation report entry with {entry_id} not found"
)
return ContinuationReportSchema().dump(updated_cr), HTTPStatus.OK

@staticmethod
@auth.require
@ApiHelper.swagger_decorators(API, endpoint_description="Delete a continuation report by id")
@API.response(code=200, model=cr_list_model, description="Deleted")
@API.response(404, "Not Found")
def delete(entry_id):
"""Delete a continuation report entry by id."""
deleted_cr = ContinuationReportService.delete(entry_id)
if not deleted_cr:
raise ResourceNotFoundError(f"Continuation report entry with {entry_id} not found")
return ContinuationReportSchema().dump(deleted_cr), HTTPStatus.OK
2 changes: 1 addition & 1 deletion compliance-api/src/compliance_api/schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
RequirementSoruceDetailSchema)
from .continuation_report import (
ContinuationReportCreateSchema, ContinuationReportKeyCreateSchema, ContinuationReportKeySchema,
ContinuationReportSchema, CRGetQueryParamSchema)
ContinuationReportSchema, ContinuationReportUpdateSchema, CRGetQueryParamSchema)
from .inspection import (
InspectionAttendanceSchema, InspectionCreateSchema, InspectionOfficerSchema, InspectionSchema,
InspectionUpdateSchema)
Expand Down
23 changes: 14 additions & 9 deletions compliance-api/src/compliance_api/schemas/continuation_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,9 @@ def post_dump_actions(
return data


class ContinuationReportCreateSchema(BaseSchema): # pylint: disable=too-many-ancestors
"""ContinuationReportCreateSchema."""
class ContinuationReportUpdateSchema(BaseSchema): # pylint: disable=too-many-ancestors
"""ContinuationReportUpdateSchema."""

case_file_id = fields.Int(
metadata={"description": "The unique identifier of the associated case file."},
required=True,
)
text = fields.Str(
metadata={"description": "The content in plane text format"},
allow_none=True,
Expand All @@ -120,6 +116,17 @@ class ContinuationReportCreateSchema(BaseSchema): # pylint: disable=too-many-an
"invalid": f"Not a valid datetime. Expected format: {INPUT_DATE_TIME_FORMAT}."
},
)


class ContinuationReportCreateSchema(
ContinuationReportUpdateSchema
): # pylint: disable=too-many-ancestors
"""ContinuationReportCreateSchema."""

case_file_id = fields.Int(
metadata={"description": "The unique identifier of the associated case file."},
required=True,
)
context_type = EnumField(
ContextEnum,
metadata={
Expand All @@ -140,6 +147,4 @@ class CRGetQueryParamSchema(PaginationParameterSchema):
case_file_id = fields.Int(
metadata={"description": "The case file id."}, required=True
)
search_text = fields.Str(
metadata={"description": "The text to be searched"}
)
search_text = fields.Str(metadata={"description": "The text to be searched"})
3 changes: 1 addition & 2 deletions compliance-api/src/compliance_api/services/case_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
from compliance_api.utils.constant import INPUT_DATE_TIME_FORMAT
from compliance_api.utils.enum import ContextEnum, PermissionEnum

from .continuation_report import ContinuationReportService


class CaseFileService:
"""CaseFile Service."""
Expand Down Expand Up @@ -44,6 +42,7 @@ def get_other_officers(cls, case_file_id: int):
@classmethod
def create(cls, case_file_data: dict):
"""Create case file."""
from .continuation_report import ContinuationReportService # pylint: disable=import-outside-toplevel
case_file_obj = _create_case_file_object(case_file_data)
_validate_existence_by_file_number(case_file_obj.get("case_file_number", None))
with session_scope() as session:
Expand Down
93 changes: 73 additions & 20 deletions compliance-api/src/compliance_api/services/continuation_report.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""ContinuationReport Service."""

from flask import g

from compliance_api.auth import auth
from compliance_api.exceptions import PermissionDeniedError
from compliance_api.models.case_file import CaseFile as CaseFileModel
from compliance_api.models.continuation_report import ContinuationReport as ContinuationReportModel
from compliance_api.models.continuation_report import ContinuationReportKey as ContinuationReportKeyModel
from compliance_api.models.db import session_scope
Expand All @@ -24,6 +27,35 @@ def create(cls, report_entry: dict, system_generated=False, ho_session=None):
_insert_or_update_keys(created_entry.id, keys, ho_session or session)
return created_entry

@classmethod
def update(cls, entry_id, report_entry: dict):
"""Update continuation report entry."""
entry = ContinuationReportModel.find_by_id(entry_id)
if not entry:
return None
_access_check_update_delete(entry.case_file_id, entry.created_by)
with session_scope() as session:
updated_entry = ContinuationReportModel.update_entry(
entry_id, report_entry, session
)
keys = report_entry.get("keys", [])
_insert_or_update_keys(entry_id, keys, session)
return updated_entry

@classmethod
def delete(cls, entry_id):
"""Delete continuation report entry."""
entry = ContinuationReportModel.find_by_id(entry_id)
_access_check_update_delete(entry.case_file_id, entry.created_by)
if not entry:
return None
with session_scope() as session:
deleted_entry = ContinuationReportModel.update_entry(
entry_id, {"is_deleted": True, "is_active": False}, session
)
_insert_or_update_keys(entry_id, [], session)
return deleted_entry

@classmethod
def get_by_case_file_id(cls, case_file_id, page_no, page_size, search_text):
"""Get all crs by case file id."""
Expand All @@ -34,35 +66,56 @@ def get_by_case_file_id(cls, case_file_id, page_no, page_size, search_text):

def _access_check(report_entry: dict):
"""Access check."""
from compliance_api.services.case_file import CaseFileService # pylint: disable=import-outside-toplevel
if not auth.has_permission(
[PermissionEnum.SUPERUSER]
) and not CaseFileService.is_logged_user_primary_or_officer(
report_entry.get("case_file_id")
):
) and not _is_logged_user_primary_or_officer(report_entry.get("case_file_id")):
raise PermissionDeniedError(
"You don't have the correct permission to perform this operation."
)


def _access_check_update_delete(case_file_id, created_by):
"""Access check for update."""

auth_user_guid = g.token_info["preferred_username"]
if auth.has_permission([PermissionEnum.SUPERUSER]):
return
if auth_user_guid == created_by and _is_logged_user_primary_or_officer(
case_file_id
):
return
raise PermissionDeniedError(
"You don't have the correct permission to perform this operation."
)


def _is_logged_user_primary_or_officer(case_file_id):
"""Check to see if the given user is primary or other officer in the case file."""
auth_user_guid = g.token_info["preferred_username"]
case_file = CaseFileModel.find_by_id(case_file_id)
# The logged in user should be primary or officer in the associated
# case file
return case_file.primary_officer.auth_user_guid == auth_user_guid or any(
officer.officer.auth_user_guid == auth_user_guid
for officer in case_file.case_file_officers
)


def _insert_or_update_keys(report_id, keys, session=None):
"""Insert or update keys for continuatino report."""
if keys:
existing_keys = ContinuationReportKeyModel.get_by_report_id(report_id)
existing_keys = {
entry.key for entry in existing_keys if entry.is_active is True
}

new_keys = {key.get("key") for key in keys}
keys_to_be_deleted = existing_keys.difference(new_keys)
keysto_be_added = new_keys.difference(existing_keys)
if keys_to_be_deleted:
ContinuationReportKeyModel.bulk_delete(
report_id, list(keys_to_be_deleted), session
)
if keysto_be_added:
key_objects = [key for key in keys if key.get("key") in keysto_be_added]
ContinuationReportKeyModel.bulk_insert(report_id, key_objects, session)
existing_keys = ContinuationReportKeyModel.get_by_report_id(report_id)
existing_keys = {entry.key for entry in existing_keys if entry.is_active is True}

new_keys = {key.get("key") for key in keys}
keys_to_be_deleted = existing_keys.difference(new_keys)
keysto_be_added = new_keys.difference(existing_keys)
if keys_to_be_deleted:
ContinuationReportKeyModel.bulk_delete(
report_id, list(keys_to_be_deleted), session
)
if keysto_be_added:
key_objects = [key for key in keys if key.get("key") in keysto_be_added]
ContinuationReportKeyModel.bulk_insert(report_id, key_objects, session)


def _create_report_entry(report_entry_data: dict, sys_generated=False):
Expand Down
4 changes: 2 additions & 2 deletions compliance-api/src/compliance_api/services/inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
from compliance_api.models import IRStatusOption as IRStatusOptionModel
from compliance_api.models.db import session_scope
from compliance_api.models.inspection.inspection_enum import InspectionAttendanceOptionEnum, InspectionStatusEnum
from compliance_api.services.case_file import CaseFileService
from compliance_api.utils.constant import INPUT_DATE_TIME_FORMAT, UNAPPROVED_PROJECT_CODE, UNAPPROVED_PROJECT_NAME
from compliance_api.utils.enum import ContextEnum, PermissionEnum

from .continuation_report import ContinuationReportService
from .epic_track_service.track_service import TrackService


Expand Down Expand Up @@ -146,6 +146,7 @@ def get_attendance_options(cls, inspection_id):
@classmethod
def create(cls, inspection_data: dict):
"""Create inspection."""
from .continuation_report import ContinuationReportService # pylint: disable=import-outside-toplevel
_access_check_create(inspection_data)
inspection_obj = _create_inspection_object(inspection_data)
with session_scope() as session:
Expand Down Expand Up @@ -274,7 +275,6 @@ def update(cls, inspection_id: int, inspection_data: dict):

def _access_check_create(inspection_data: dict):
"""Access check."""
from .case_file import CaseFileService # pylint: disable=import-outside-toplevel
if not auth.has_permission(
[PermissionEnum.SUPERUSER]
) and not CaseFileService.is_logged_user_primary_or_officer(
Expand Down

0 comments on commit 5180d23

Please sign in to comment.