Skip to content

Commit

Permalink
Merge pull request #166 from dinesh-aot/COMP-280
Browse files Browse the repository at this point in the history
Link case files
  • Loading branch information
nitheesh-aot authored Dec 7, 2024
2 parents 5695dba + 88fbb6f commit c530a28
Show file tree
Hide file tree
Showing 9 changed files with 337 additions and 21 deletions.
103 changes: 103 additions & 0 deletions compliance-api/migrations/versions/c353afd11883_case_file_link.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""case file link
Revision ID: c353afd11883
Revises: 9ca9ebdf5b64
Create Date: 2024-12-05 13:52:31.894453
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'c353afd11883'
down_revision = '9ca9ebdf5b64'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('case_file_links_version',
sa.Column('id', sa.Integer(), autoincrement=False, nullable=False, comment='The unique identifier of the case file link'),
sa.Column('source_case_id', sa.Integer(), autoincrement=False, nullable=True),
sa.Column('target_case_id', sa.Integer(), autoincrement=False, nullable=True),
sa.Column('created_date', sa.DateTime(), autoincrement=False, nullable=True),
sa.Column('updated_date', sa.DateTime(), autoincrement=False, nullable=True),
sa.Column('created_by', sa.String(length=100), autoincrement=False, nullable=True),
sa.Column('updated_by', sa.String(length=100), autoincrement=False, nullable=True),
sa.Column('is_active', sa.Boolean(), server_default='t', autoincrement=False, nullable=True),
sa.Column('is_deleted', sa.Boolean(), server_default='f', autoincrement=False, nullable=True),
sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False),
sa.Column('end_transaction_id', sa.BigInteger(), nullable=True),
sa.Column('operation_type', sa.SmallInteger(), nullable=False),
sa.Column('source_case_id_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.Column('target_case_id_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.Column('created_date_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.Column('updated_date_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.Column('created_by_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.Column('updated_by_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.Column('is_active_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.Column('is_deleted_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False),
sa.PrimaryKeyConstraint('id', 'transaction_id')
)
with op.batch_alter_table('case_file_links_version', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_case_file_links_version_end_transaction_id'), ['end_transaction_id'], unique=False)
batch_op.create_index(batch_op.f('ix_case_file_links_version_operation_type'), ['operation_type'], unique=False)
batch_op.create_index(batch_op.f('ix_case_file_links_version_source_case_id'), ['source_case_id'], unique=False)
batch_op.create_index(batch_op.f('ix_case_file_links_version_target_case_id'), ['target_case_id'], unique=False)
batch_op.create_index(batch_op.f('ix_case_file_links_version_transaction_id'), ['transaction_id'], unique=False)

op.create_table('case_file_links',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='The unique identifier of the case file link'),
sa.Column('source_case_id', sa.Integer(), nullable=False),
sa.Column('target_case_id', sa.Integer(), nullable=False),
sa.Column('created_date', sa.DateTime(), nullable=False),
sa.Column('updated_date', sa.DateTime(), nullable=True),
sa.Column('created_by', sa.String(length=100), nullable=False),
sa.Column('updated_by', sa.String(length=100), nullable=True),
sa.Column('is_active', sa.Boolean(), server_default='t', nullable=False),
sa.Column('is_deleted', sa.Boolean(), server_default='f', nullable=False),
sa.ForeignKeyConstraint(['source_case_id'], ['case_files.id'], name='source_case_id_case_files_id_fk'),
sa.ForeignKeyConstraint(['target_case_id'], ['case_files.id'], name='target_case_id_case_files_id_fk'),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('case_file_links', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_case_file_links_source_case_id'), ['source_case_id'], unique=False)
batch_op.create_index(batch_op.f('ix_case_file_links_target_case_id'), ['target_case_id'], unique=False)

with op.batch_alter_table('case_files', schema=None) as batch_op:
batch_op.add_column(sa.Column('link_case_file_id', sa.Integer(), nullable=True, comment='The case file to link to'))
batch_op.create_foreign_key('casefile_link_link_case_file_id_fk', 'case_files', ['link_case_file_id'], ['id'])

with op.batch_alter_table('case_files_version', schema=None) as batch_op:
batch_op.add_column(sa.Column('link_case_file_id', sa.Integer(), autoincrement=False, nullable=True, comment='The case file to link to'))
batch_op.add_column(sa.Column('link_case_file_id_mod', sa.Boolean(), server_default=sa.text('false'), nullable=False))

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('case_files_version', schema=None) as batch_op:
batch_op.drop_column('link_case_file_id_mod')
batch_op.drop_column('link_case_file_id')

with op.batch_alter_table('case_files', schema=None) as batch_op:
batch_op.drop_constraint('casefile_link_link_case_file_id_fk', type_='foreignkey')
batch_op.drop_column('link_case_file_id')

with op.batch_alter_table('case_file_links', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_case_file_links_target_case_id'))
batch_op.drop_index(batch_op.f('ix_case_file_links_source_case_id'))

op.drop_table('case_file_links')
with op.batch_alter_table('case_file_links_version', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_case_file_links_version_transaction_id'))
batch_op.drop_index(batch_op.f('ix_case_file_links_version_target_case_id'))
batch_op.drop_index(batch_op.f('ix_case_file_links_version_source_case_id'))
batch_op.drop_index(batch_op.f('ix_case_file_links_version_operation_type'))
batch_op.drop_index(batch_op.f('ix_case_file_links_version_end_transaction_id'))

op.drop_table('case_file_links_version')
# ### end Alembic commands ###
3 changes: 2 additions & 1 deletion compliance-api/src/compliance_api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"""This exports all of the models and schemas used by the application."""

from .agency import Agency
from .case_file import CaseFile, CaseFileInitiationEnum, CaseFileInitiationOption, CaseFileOfficer, CaseFileStatusEnum
from .case_file import (
CaseFile, CaseFileInitiationEnum, CaseFileInitiationOption, CaseFileLink, CaseFileOfficer, CaseFileStatusEnum)
from .complaint import (
Complaint, ComplaintReqOrderDetail, ComplaintReqScheduleBDetail, ComplaintRequirementDetail, ComplaintSource,
ComplaintSourceContact, ComplaintStatusEnum)
Expand Down
66 changes: 63 additions & 3 deletions compliance-api/src/compliance_api/models/case_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ class CaseFile(BaseModelVersioned):
comment="The unique case file number",
)
case_file_status = Column(Enum(CaseFileStatusEnum), nullable=True)
link_case_file_id = Column(
Integer,
ForeignKey("case_files.id", name="casefile_link_link_case_file_id_fk"),
nullable=True,
comment="The case file to link to",
)

primary_officer = relationship(
"StaffUser", foreign_keys=[primary_officer_id], lazy="joined"
Expand Down Expand Up @@ -120,7 +126,7 @@ def update_case_file(cls, case_file_id, case_file_data, session=None):
if session:
session.flush()
else:
cls.session.commit()
db.session.commit()
return case_file

@classmethod
Expand Down Expand Up @@ -224,8 +230,8 @@ def bulk_insert(cls, case_file_id: int, officer_ids: list[int], session=None):
session.add_all(case_file_officer_data)
session.flush()
else:
cls.session.add_all(case_file_officer_data)
cls.session.commit()
db.session.add_all(case_file_officer_data)
db.session.commit()


class CaseFileInitiationOption(BaseModelVersioned):
Expand All @@ -243,3 +249,57 @@ class CaseFileInitiationOption(BaseModelVersioned):
Integer,
comment="Order of priority. Mainly used order the options while listing",
)


class CaseFileLink(BaseModelVersioned):
"""CaseFileLinks Model."""

__tablename__ = "case_file_links"

id = Column(
Integer,
primary_key=True,
autoincrement=True,
comment="The unique identifier of the case file link",
)
source_case_id = Column(
Integer,
ForeignKey("case_files.id", name="source_case_id_case_files_id_fk"),
index=True,
nullable=False,
)
target_case_id = Column(
Integer,
ForeignKey("case_files.id", name="target_case_id_case_files_id_fk"),
index=True,
nullable=False,
)
source = relationship("CaseFile", foreign_keys=[source_case_id], lazy="joined")
target = relationship("CaseFile", foreign_keys=[target_case_id], lazy="joined")

@classmethod
def get_links_by_source_id(cls, source_case_file_id):
"""Get all case file links by source case file id."""
return cls.query.filter(
cls.source_case_id == source_case_file_id, cls.is_deleted.is_(False)
).all()

@classmethod
def get_links_by_source_and_target(cls, source_id, target_id):
"""Get case file link by both source case file and target case file id."""
return cls.query.filter(
cls.source_case_id == source_id,
cls.target_case_id == target_id,
cls.is_deleted.is_(False),
).first()

@classmethod
def create_link(cls, link_data, session=None):
"""Persist case file link data in database."""
case_file = CaseFileLink(**link_data)
if session:
session.add(case_file)
session.flush()
else:
case_file.save()
return case_file
33 changes: 31 additions & 2 deletions compliance-api/src/compliance_api/resources/case_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
from compliance_api.auth import auth
from compliance_api.exceptions import ResourceNotFoundError
from compliance_api.schemas import (
CaseFileCreateSchema, CaseFileOfficerSchema, CaseFileSchema, CaseFileStatusSchema, CaseFileUpdateSchema,
KeyValueSchema, StaffUserSchema)
CaseFileCreateSchema, CaseFileLinkCreateSchema, CaseFileLinkSchema, CaseFileOfficerSchema, CaseFileSchema,
CaseFileStatusSchema, CaseFileUpdateSchema, KeyValueSchema, StaffUserSchema)
from compliance_api.services import CaseFileService
from compliance_api.services.case_file_aggregate import CaseFileAggregateService
from compliance_api.utils.enum import PermissionEnum
Expand Down Expand Up @@ -54,6 +54,12 @@
case_file_status_model = ApiHelper.convert_ma_schema_to_restx_model(
API, CaseFileStatusSchema(), "CaseFileStatus"
)
case_file_link_create_model = ApiHelper.convert_ma_schema_to_restx_model(
API, CaseFileLinkCreateSchema(), "CaseFileLinkCreate"
)
case_file_link_model = ApiHelper.convert_ma_schema_to_restx_model(
API, CaseFileLinkSchema(), "CaseFileLink"
)


@cors_preflight("GET, OPTIONS")
Expand Down Expand Up @@ -218,3 +224,26 @@ def patch(case_file_id):
status = CaseFileStatusSchema().load(API.payload)
CaseFileService.change_case_file_status(case_file_id, status)
return {}, HTTPStatus.NO_CONTENT


@cors_preflight("POST, OPTIONS")
@API.route("/<int:case_file_id>/links", methods=["POST", "OPTIONS"])
@API.doc(params={"case_file_id": "The unique identifier for the case file"})
class CaseFileLinks(Resource):
"""Link the case file."""

@staticmethod
@auth.require
@API.expect(case_file_link_create_model)
@API.response(400, "Bad Request")
@API.response(404, "Not Found")
@API.response(code=201, model=case_file_link_model, description="Success")
@ApiHelper.swagger_decorators(
API, endpoint_description="Link the case file to another case file"
)
@API.response(code=204, description="Case file linked")
def post(case_file_id):
"""Link the case file."""
link = CaseFileLinkCreateSchema().load(API.payload)
created_link = CaseFileService.link(case_file_id, link)
return CaseFileLinkSchema().dump(created_link), HTTPStatus.CREATED
3 changes: 2 additions & 1 deletion compliance-api/src/compliance_api/schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"""Exposes all of the schemas in the compliance_api."""
from .agency import AgencyCreateSchema, AgencySchema
from .case_file import (
CaseFileCreateSchema, CaseFileOfficerSchema, CaseFileSchema, CaseFileStatusSchema, CaseFileUpdateSchema)
CaseFileCreateSchema, CaseFileLinkCreateSchema, CaseFileLinkSchema, CaseFileOfficerSchema, CaseFileSchema,
CaseFileStatusSchema, CaseFileUpdateSchema)
from .common import KeyValueSchema
from .complaint import (
ComplaintCreateSchema, ComplaintSchema, ComplaintSourceContactSchema, ComplaintStatusSchema, ComplaintUpdateSchema,
Expand Down
29 changes: 28 additions & 1 deletion compliance-api/src/compliance_api/schemas/case_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from marshmallow import EXCLUDE, fields, post_dump, post_load
from marshmallow_enum import EnumField

from compliance_api.models import CaseFile, CaseFileOfficer, CaseFileStatusEnum
from compliance_api.models import CaseFile, CaseFileLink, CaseFileOfficer, CaseFileStatusEnum
from compliance_api.utils.constant import INPUT_DATE_TIME_FORMAT, UNAPPROVED_PROJECT_CODE, UNAPPROVED_PROJECT_NAME

from .base_schema import AutoSchemaBase, BaseSchema
Expand Down Expand Up @@ -180,3 +180,30 @@ def extract_status_value(
if status_enum:
data["status"] = status_enum.value
return data


class CaseFileLinkCreateSchema(BaseSchema):
"""CaseFileLinkSchema."""

class Meta: # pylint: disable=too-few-public-methods
"""Exclude unknown fields in the deserialized output."""

unknown = EXCLUDE

link_case_file_id = fields.Int(
metadata={"description": "The case file to link to."}, required=True
)


class CaseFileLinkSchema(AutoSchemaBase): # pylint: disable=too-many-ancestors
"""CaseFileLinkSchema."""

class Meta(AutoSchemaBase.Meta): # pylint: disable=too-few-public-methods
"""Meta."""

unknown = EXCLUDE
model = CaseFileLink
include_fk = True

source = fields.Nested(CaseFileSchema)
target = fields.Nested(CaseFileSchema)
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def post_dump_actions(
): # pylint: disable=no-self-use, unused-argument
"""Extract the value of key_context enum."""
if "key_context" in data and data["key_context"] is not None:
data["key_context"] = ContextEnum(data["key_context"]).value
data["key_context"] = ContextEnum(data["key_context"]).name
else:
data["key_context"] = ""
return data
Expand Down Expand Up @@ -87,7 +87,7 @@ def post_dump_actions(
): # pylint: disable=no-self-use, unused-argument
"""Extract the value of context enum."""
if "context_type" in data and data["context_type"] is not None:
data["context_type"] = ContextEnum(data["context_type"]).value
data["context_type"] = ContextEnum(data["context_type"]).name
else:
data["context_type"] = ""
return data
Expand Down
Loading

0 comments on commit c530a28

Please sign in to comment.