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
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,22 @@ def delete_by_context(cls, context_id, context_type, session=None):
session.flush()

@classmethod
def get_by_case_file(cls, case_file_id, page_no, page_size, search_text):
"""Get crs by case file id."""
def get_by_case_file_paginated(cls, case_file_id, page_no, page_size, search_text):
"""Get crs by case file id paginated."""
query = cls.query.filter_by(case_file_id=case_file_id, is_deleted=False)
if search_text:
query = query.filter(cls.text.ilike(f"%{search_text}%"))
query = query.order_by(cls.date_created.desc())
pagination = query.paginate(page=page_no, per_page=page_size)
return pagination.items, pagination.total

@classmethod
def get_all_by_case_file(cls, case_file_id):
"""Get all crs by case file id."""
query = cls.query.filter_by(case_file_id=case_file_id, is_deleted=False)
query = query.order_by(cls.date_created.asc())
return query.all()


class ContinuationReportKey(BaseModelVersioned):
"""ContinationReportKey Model."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

from http import HTTPStatus

from flask import request
from flask_restx import Namespace, Resource
from flask import current_app, request, send_file
from flask_restx import Namespace, Resource, ValidationError

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

Expand Down Expand Up @@ -133,3 +134,35 @@ def delete(entry_id):
f"Continuation report entry with {entry_id} not found"
)
return ContinuationReportSchema().dump(deleted_cr), HTTPStatus.OK


@cors_preflight("OPTIONS, POST")
@API.route("/render", methods=["POST", "OPTIONS"])
class ContinuationReportExport(Resource):
"""Resource for exporting continuation report."""

@staticmethod
@auth.require
@ApiHelper.swagger_decorators(
API, endpoint_description="Export continuation report as PDF"
)
@API.response(code=200, description="Success")
@API.response(400, "Bad Request")
def post():
"""Export continuation report as PDF."""
current_app.logger.info(f"Received request to export continuation report with payload: {API.payload}")
try:
request_params = CRExport().load(API.payload)
case_file_number = request_params.get("case_file_number")
inspection_number = request_params.get("inspection_number")
pdf_data = ContinuationReportService.render(case_file_number, inspection_number)
return send_file(
pdf_data,
as_attachment=True,
download_name=f'{inspection_number or case_file_number}.pdf',
mimetype='application/pdf'
)
except ValidationError as validation_error:
return {"message": str(validation_error)}, HTTPStatus.BAD_REQUEST
except ValueError as value_error:
return {"message": str(value_error)}, HTTPStatus.BAD_REQUEST
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,15 @@ class CRGetQueryParamSchema(PaginationParameterSchema):
metadata={"description": "The case file id."}, required=True
)
search_text = fields.Str(metadata={"description": "The text to be searched"})


class CRExport(BaseSchema):
"""Query parameter for the CR query."""

case_file_number = fields.String(
metadata={"description": "The case file number."}, required=False
)

inspection_number = fields.String(
metadata={"description": "The inspection number."}, required=False
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""ContinuationReport Service."""

from io import BytesIO

from flask import g

from compliance_api.auth import auth
Expand All @@ -8,6 +10,9 @@
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
from compliance_api.models.inspection import Inspection as InspectionModel
from compliance_api.services.case_file import CaseFileService
from compliance_api.services.docgen_service.docgen_service import DocGenService
from compliance_api.utils.enum import PermissionEnum


Expand Down Expand Up @@ -94,10 +99,29 @@ def delete_by_context(cls, context_id, context_type, ho_session=None):
@classmethod
def get_by_case_file_id(cls, case_file_id, page_no, page_size, search_text):
"""Get all crs by case file id."""
return ContinuationReportModel.get_by_case_file(
return ContinuationReportModel.get_by_case_file_paginated(
case_file_id, page_no, page_size, search_text
)

@classmethod
def render(cls, case_file_number, inspection_number):
"""Export continuation report as PDF."""
if case_file_number is None and inspection_number is None:
raise ValueError("Either case_file_number or inspection_number must be provided.")

if case_file_number:
case_file = CaseFileService.get_by_file_number(case_file_number)
elif inspection_number:
inspection = InspectionModel.get_by_ir_number(inspection_number)
case_file = CaseFileService.get_by_file_number(inspection.case_file.case_file_number)

if not case_file:
raise ResourceNotFoundError("Case file not found.")

data = _get_report_data(case_file)
response = DocGenService.render_template("CONTINUATION_REPORT_TEMPLATE", data, "pdf")
return BytesIO(response.content)


def _access_check(report_entry: dict):
"""Access check."""
Expand Down Expand Up @@ -164,3 +188,38 @@ def _create_report_entry(report_entry_data: dict, sys_generated: bool = False):
"context_type": report_entry_data.get("context_type"),
"context_id": report_entry_data.get("context_id"),
}


def _get_report_data(case_file):
"""Get the data for continuation report."""
continuation_report = ContinuationReportModel.get_all_by_case_file(case_file.id)
data = {
"project_name": case_file.project.name if case_file.project else "",
"case_file_number": case_file.case_file_number,
"primary_officer": (
f"{case_file.primary_officer.first_name} {case_file.primary_officer.last_name}"
if case_file.primary_officer
else ""
),
"authorization": case_file.authorization,
"other_officers": [
f"{cfo.officer.first_name} {cfo.officer.last_name}" for cfo in case_file.case_file_officers
],
"continuation_report_entries": [
{
"date": entry.date_created.strftime("%Y-%m-%d"),
"time": entry.date_created.strftime("%H:%M"),
"action": _build_action_text(entry),
}
for entry in continuation_report
]
}
return data


def _build_action_text(entry):
"""Build action text with creator info if not system-generated."""
action = entry.text
if not entry.system_generated and entry.created_by_user:
action += f" <i>Created by {entry.created_by_user.first_name} {entry.created_by_user.last_name}</i>"
return action
Loading