diff --git a/compliance-api/migrations/versions/e2f642264b01_inspection_status_enum_change.py b/compliance-api/migrations/versions/e2f642264b01_inspection_status_enum_change.py new file mode 100644 index 0000000..8bfbebc --- /dev/null +++ b/compliance-api/migrations/versions/e2f642264b01_inspection_status_enum_change.py @@ -0,0 +1,35 @@ +"""inspection_status_enum change + +Revision ID: e2f642264b01 +Revises: 36a31040a452 +Create Date: 2024-12-13 09:49:30.968604 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'e2f642264b01' +down_revision = '36a31040a452' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute("ALTER TYPE inspectionstatusenum ADD VALUE 'CANCELED'") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute("ALTER TYPE inspectionstatusenum RENAME TO inspectionstatusenum_old") + op.execute("CREATE TYPE inspectionstatusenum AS ENUM('OPEN', 'CLOSED')") + op.execute(""" + ALTER TABLE inspections + ALTER COLUMN inspection_status + TYPE inspectionstatusenum + USING inspection_status::text::inspectionstatusenum + """) + # ### end Alembic commands ### diff --git a/compliance-api/src/compliance_api/models/inspection/inspection_enum.py b/compliance-api/src/compliance_api/models/inspection/inspection_enum.py index 4384371..d10c0bc 100644 --- a/compliance-api/src/compliance_api/models/inspection/inspection_enum.py +++ b/compliance-api/src/compliance_api/models/inspection/inspection_enum.py @@ -21,3 +21,4 @@ class InspectionStatusEnum(enum.Enum): OPEN = "Open" CLOSED = "Closed" + CANCELED = "Canceled" diff --git a/compliance-api/src/compliance_api/resources/inspection.py b/compliance-api/src/compliance_api/resources/inspection.py index 41a707e..b2b5ebb 100644 --- a/compliance-api/src/compliance_api/resources/inspection.py +++ b/compliance-api/src/compliance_api/resources/inspection.py @@ -21,7 +21,7 @@ from compliance_api.auth import auth from compliance_api.schemas import ( InspectionAttendanceSchema, InspectionCreateSchema, InspectionOfficerSchema, InspectionSchema, - InspectionUpdateSchema, KeyValueSchema, StaffUserSchema) + InspectionStatusSchema, InspectionUpdateSchema, KeyValueSchema, StaffUserSchema) from compliance_api.services import InspectionService from compliance_api.utils.enum import PermissionEnum from compliance_api.utils.util import cors_preflight @@ -50,6 +50,9 @@ inspection_update_model = ApiHelper.convert_ma_schema_to_restx_model( API, InspectionUpdateSchema(), "InspectionUpdate" ) +inspection_status_model = ApiHelper.convert_ma_schema_to_restx_model( + API, InspectionStatusSchema(), "InspectionStatus" +) @cors_preflight("GET, OPTIONS") @@ -242,7 +245,7 @@ def get(ir_number): @cors_preflight("PATCH, OPTIONS") -@API.route("//close", methods=["PATCH", "OPTIONS"]) +@API.route("//status", methods=["PATCH", "OPTIONS"]) @API.doc(params={"inspection_id": "The unique identifier for the inspection"}) class InspectionStatus(Resource): """Update the inspection status.""" @@ -250,10 +253,12 @@ class InspectionStatus(Resource): @staticmethod @auth.require @API.response(400, "Bad Request") + @API.expect(inspection_status_model) @API.response(404, "Not Found") - @ApiHelper.swagger_decorators(API, endpoint_description="Close the inspection") - @API.response(code=204, description="Inspection Closed") + @ApiHelper.swagger_decorators(API, endpoint_description="Change inspection status") + @API.response(code=204, description="Inspection status changed") def patch(inspection_id): - """Close complaint.""" - InspectionService.close_inspection(inspection_id) + """Change Inspection Status.""" + status = InspectionStatusSchema().load(API.payload) + InspectionService.change_status(inspection_id, status) return {}, HTTPStatus.NO_CONTENT diff --git a/compliance-api/src/compliance_api/schemas/__init__.py b/compliance-api/src/compliance_api/schemas/__init__.py index 2390241..201fd45 100644 --- a/compliance-api/src/compliance_api/schemas/__init__.py +++ b/compliance-api/src/compliance_api/schemas/__init__.py @@ -25,7 +25,7 @@ ContinuationReportSchema, ContinuationReportUpdateSchema, CRGetQueryParamSchema) from .inspection import ( InspectionAttendanceSchema, InspectionCreateSchema, InspectionOfficerSchema, InspectionSchema, - InspectionUpdateSchema) + InspectionStatusSchema, InspectionUpdateSchema) from .paginate import PaginationParameterSchema from .project import ProjectSchema from .staff_user import StaffUserCreateSchema, StaffUserSchema, StaffUserUpdateSchema diff --git a/compliance-api/src/compliance_api/schemas/case_file.py b/compliance-api/src/compliance_api/schemas/case_file.py index 410e644..5606673 100644 --- a/compliance-api/src/compliance_api/schemas/case_file.py +++ b/compliance-api/src/compliance_api/schemas/case_file.py @@ -212,21 +212,13 @@ class Meta(AutoSchemaBase.Meta): # pylint: disable=too-few-public-methods class CaseFileOptionSchema(Schema): # pylint: disable=too-many-ancestors """CaseFileOptionSchema.""" - case_file_id = fields.Int( + id = fields.Int( metadata={"description": "The unique identifier of the case file."} ) case_file_number = fields.Str( metadata={"description": "The unique case file number of the case file."} ) - @pre_dump - def map_id_to_case_file_id( - self, data, **kwargs - ): # pylint: disable=no-self-use, unused-argument - """Map id to case_file_id.""" - setattr(data, "case_file_id", data.id) - return data - class CaseFileUnlinkSchema(BaseSchema): """CaseFileUnlinkSchema.""" diff --git a/compliance-api/src/compliance_api/schemas/inspection.py b/compliance-api/src/compliance_api/schemas/inspection.py index 5a950cd..efa9ca5 100644 --- a/compliance-api/src/compliance_api/schemas/inspection.py +++ b/compliance-api/src/compliance_api/schemas/inspection.py @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. """Inspection Schema Schema.""" -from marshmallow import EXCLUDE, ValidationError, fields, post_dump, validates_schema +from marshmallow import EXCLUDE, ValidationError, fields, post_dump, post_load, validates_schema +from marshmallow_enum import EnumField from compliance_api.models.inspection import ( Inspection, InspectionAttendance, InspectionAttendanceOptionEnum, InspectionOfficer, InspectionStatusEnum) @@ -347,3 +348,44 @@ def get_inspection_type_names( if obj.types: return ", ".join([o.type.name for o in obj.types]) return [] + + +class InspectionStatusSchema(BaseSchema): + """ComplaintStatusSchema.""" + + class Meta: # pylint: disable=too-few-public-methods + """Exclude unknown fields in the deserialized output.""" + + unknown = EXCLUDE + + status = EnumField( + InspectionStatusEnum, + metadata={"description": "The status of the complaint"}, + required=True, + ) + + @post_load + def extract_status_value( + self, data, **kwargs + ): # pylint: disable=no-self-use, unused-argument + """Extract the value of the status enum.""" + status_enum = data.get("status") + if status_enum: + data["status"] = status_enum.value + return data + + @validates_schema + def validate_status( + self, data, **kwargs + ): # pylint: disable=no-self-use, unused-argument + """Ensure only Closed and Canceled status are passed.""" + # Retrieve the context to access other fields + status = data.get("status") + if status not in [ + InspectionStatusEnum("Closed"), + InspectionStatusEnum("Canceled"), + ]: + raise ValidationError( + "Invalid status value passed", + field_name="status", + ) diff --git a/compliance-api/src/compliance_api/services/inspection.py b/compliance-api/src/compliance_api/services/inspection.py index 691983b..caee786 100644 --- a/compliance-api/src/compliance_api/services/inspection.py +++ b/compliance-api/src/compliance_api/services/inspection.py @@ -282,7 +282,7 @@ def update(cls, inspection_id: int, inspection_data: dict): return updated_case_file @classmethod - def close_inspection(cls, inspection_id): + def change_status(cls, inspection_id, status): """Close the inspection.""" from .continuation_report import ContinuationReportService # pylint: disable=import-outside-toplevel @@ -290,18 +290,22 @@ def close_inspection(cls, inspection_id): inspection = InspectionModel.find_by_id(inspection_id) if not inspection: raise ResourceNotFoundError("Inspection not found.") - if inspection.inspection_status == "Closed": + status_enum = InspectionStatusEnum(status.get("status")) + if inspection.inspection_status != InspectionStatusEnum.OPEN: raise UnprocessableEntityError( - "The inspection is already in Closed status." + "Inspection has to be in open state to perform the requested action" ) with session_scope() as session: InspectionModel.update_inspection( inspection_id, - {"inspection_status": InspectionStatusEnum("Closed")}, + {"inspection_status": InspectionStatusEnum(status_enum.value)}, session, ) cr_entry = _create_cr_entry( - inspection.id, inspection.ir_number, inspection.case_file_id, "closed" + inspection.id, + inspection.ir_number, + inspection.case_file_id, + status_enum.value.lower(), ) ContinuationReportService.create( cr_entry, sys_generated=True, ho_session=session