Skip to content

Commit

Permalink
Merge pull request #2416 from uktrade/uat
Browse files Browse the repository at this point in the history
PROD Release
  • Loading branch information
depsiatwal authored Feb 13, 2025
2 parents 179bfe8 + fe726d5 commit 0c5e583
Show file tree
Hide file tree
Showing 71 changed files with 1,988 additions and 302 deletions.
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ repos:
- id: detect-aws-credentials
args: ["--allow-missing-credentials"]
- repo: https://github.com/uktrade/pii-secret-check-hooks
rev: 0.0.0.35
# TODO: Switch back to a versioned release after this feature branch merges on the PII repo
rev: add-file-content-ignore-strings-option
hooks:
- id: pii_secret_filename
files: ''
Expand Down
12 changes: 2 additions & 10 deletions api/applications/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
from api.applications.serializers.standard_application import (
StandardApplicationCreateSerializer,
StandardApplicationUpdateSerializer,
StandardApplicationViewSerializer,
)
from api.applications.serializers.good import GoodOnStandardLicenceSerializer
from api.cases.enums import CaseTypeSubTypeEnum, CaseTypeEnum, AdviceType, AdviceLevel
Expand All @@ -31,15 +30,8 @@


def get_application_view_serializer(application: BaseApplication):
if application.case_type.sub_type == CaseTypeSubTypeEnum.STANDARD:
return StandardApplicationViewSerializer
else:
raise BadRequestError(
{
f"get_application_view_serializer does "
f"not support this application type: {application.case_type.sub_type}"
}
)
application_manifest = application.get_application_manifest()
return application_manifest.caseworker_serializers["view"]


def get_application_create_serializer(case_type):
Expand Down
72 changes: 6 additions & 66 deletions api/applications/libraries/get_applications.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from api.applications.models import BaseApplication, StandardApplication
from api.cases.enums import CaseTypeSubTypeEnum
from api.cases.models import Case
from api.core.exceptions import NotFoundError


Expand All @@ -8,68 +7,9 @@ def get_application(pk, organisation_id=None):
if organisation_id:
kwargs["organisation_id"] = str(organisation_id)

application_type = _get_application_type(pk)
if application_type != CaseTypeSubTypeEnum.STANDARD:
raise NotImplementedError(f"get_application does not support this application type: {application_type}")

qs = StandardApplication.objects.select_related(
"baseapplication_ptr",
"baseapplication_ptr__end_user",
"baseapplication_ptr__end_user__party",
"case_officer",
"case_officer__team",
"case_type",
"organisation",
"organisation__primary_site",
"status",
"submitted_by",
"submitted_by__baseuser_ptr",
).prefetch_related(
"goods",
"goods__control_list_entries",
"goods__good",
"goods__good__control_list_entries",
"goods__good__flags",
"goods__good__flags__team",
"goods__good__gooddocument_set",
"goods__good__firearm_details",
"goods__good__pv_grading_details",
"goods__good__goods_on_application",
"goods__good__goods_on_application__application",
"goods__good__goods_on_application__application__queues",
"goods__good__goods_on_application__good",
"goods__good__goods_on_application__good__flags",
"goods__good__goods_on_application__regime_entries",
"goods__good__goods_on_application__regime_entries__subsection",
"goods__good__goods_on_application__regime_entries__subsection__regime",
"goods__regime_entries",
"goods__regime_entries__subsection",
"goods__regime_entries__subsection__regime",
"goods__good__report_summary_prefix",
"goods__good__report_summary_subject",
"goods__goodonapplicationdocument_set",
"goods__goodonapplicationdocument_set__user",
"goods__good_on_application_internal_documents",
"goods__good_on_application_internal_documents__document",
"denial_matches",
"denial_matches__denial_entity",
"application_sites",
"application_sites__site",
"application_sites__site__address",
"application_sites__site__address__country",
"external_application_sites",
"applicationdocument_set",
"goods__report_summary_prefix",
"goods__report_summary_subject",
"goods__firearm_details",
"goods__assessed_by",
)
obj = qs.get(pk=pk, **kwargs)
return obj


def _get_application_type(pk):
try:
return BaseApplication.objects.values_list("case_type__sub_type", flat=True).get(pk=pk)
except BaseApplication.DoesNotExist:
raise NotFoundError({"application_type": "Application type not found - " + str(pk)})
case = Case.objects.get(pk=pk, **kwargs)
except Case.DoesNotExist:
raise NotFoundError(f"Case with id {pk} does not exist")
application = case.get_application()
return application
Empty file.
33 changes: 33 additions & 0 deletions api/applications/libraries/tests/test_get_applications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytest
from uuid import uuid4

from api.applications.libraries.get_applications import get_application
from api.core.exceptions import NotFoundError
from api.applications.tests.factories import StandardApplicationFactory
from api.organisations.tests.factories import OrganisationFactory

pytestmark = pytest.mark.django_db


class TestGetApplication:

def test_get_application_no_matching_case(self):
with pytest.raises(NotFoundError):
get_application(uuid4())

def test_get_application_success(self):
standard_application = StandardApplicationFactory()
assert get_application(standard_application.id) == standard_application

def test_get_application_with_organisation_success(self):
standard_application = StandardApplicationFactory()
assert (
get_application(standard_application.id, organisation_id=standard_application.organisation_id)
== standard_application
)

def test_get_application_with_differing_organisation(self):
standard_application = StandardApplicationFactory()
differing_organisation = OrganisationFactory()
with pytest.raises(NotFoundError):
get_application(standard_application.id, organisation_id=differing_organisation.id)
61 changes: 61 additions & 0 deletions api/applications/managers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.db import models
from django.db.models import Q

from model_utils.managers import InheritanceManager
Expand All @@ -24,3 +25,63 @@ def submitted(self, organisation, sort_by):
def finalised(self, organisation, sort_by):
finalised = get_case_status_by_status(CaseStatusEnum.FINALISED)
return self.get_queryset().filter(status=finalised, organisation=organisation).order_by(sort_by)


class StandardApplicationQuerySet(models.QuerySet):
def get_prepared_object(self, pk):
return (
self.select_related(
"baseapplication_ptr",
"baseapplication_ptr__end_user",
"baseapplication_ptr__end_user__party",
"case_officer",
"case_officer__team",
"case_type",
"organisation",
"organisation__primary_site",
"status",
"submitted_by",
"submitted_by__baseuser_ptr",
)
.prefetch_related(
"goods",
"goods__control_list_entries",
"goods__good",
"goods__good__control_list_entries",
"goods__good__flags",
"goods__good__flags__team",
"goods__good__gooddocument_set",
"goods__good__firearm_details",
"goods__good__pv_grading_details",
"goods__good__goods_on_application",
"goods__good__goods_on_application__application",
"goods__good__goods_on_application__application__queues",
"goods__good__goods_on_application__good",
"goods__good__goods_on_application__good__flags",
"goods__good__goods_on_application__regime_entries",
"goods__good__goods_on_application__regime_entries__subsection",
"goods__good__goods_on_application__regime_entries__subsection__regime",
"goods__regime_entries",
"goods__regime_entries__subsection",
"goods__regime_entries__subsection__regime",
"goods__good__report_summary_prefix",
"goods__good__report_summary_subject",
"goods__goodonapplicationdocument_set",
"goods__goodonapplicationdocument_set__user",
"goods__good_on_application_internal_documents",
"goods__good_on_application_internal_documents__document",
"denial_matches",
"denial_matches__denial_entity",
"application_sites",
"application_sites__site",
"application_sites__site__address",
"application_sites__site__address__country",
"external_application_sites",
"applicationdocument_set",
"goods__report_summary_prefix",
"goods__report_summary_subject",
"goods__firearm_details",
"goods__assessed_by",
)
.get(pk=pk)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2025-02-05 12:48

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("applications", "0084_standardapplication_subject_to_itar_controls"),
]

operations = [
migrations.AddIndex(
model_name="partyonapplication",
index=models.Index(fields=["created_at"], name="application_created_a502bc_idx"), # /PS-IGNORE
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2025-02-05 14:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("applications", "0085_partyonapplication_application_created_a502bc_idx_and_more"), # /PS-IGNORE
]

operations = [
migrations.AddIndex(
model_name="goodonapplication",
index=models.Index(fields=["created_at"], name="application_created_37e2a2_idx"), # /PS-IGNORE
),
]
15 changes: 12 additions & 3 deletions api/applications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
SecurityClassifiedApprovalsType,
NSGListType,
)
from api.appeals.models import Appeal
from api.applications.exceptions import AmendmentError
from api.applications.managers import BaseApplicationManager
from api.applications.managers import BaseApplicationManager, StandardApplicationQuerySet
from api.applications.libraries.application_helpers import create_submitted_audit
from api.appeals.models import Appeal
from api.audit_trail.models import AuditType
from api.audit_trail import service as audit_trail_service
from api.cases.enums import CaseTypeEnum
Expand Down Expand Up @@ -47,7 +47,6 @@
from api.users.enums import SystemUser
from api.users.models import ExporterUser, GovUser, BaseUser
from lite_content.lite_api.strings import PartyErrors

from lite_routing.routing_rules_internal.enums import QueuesEnum


Expand Down Expand Up @@ -274,6 +273,8 @@ def create_amendment(self, user):

# Licence Applications
class StandardApplication(BaseApplication, Clonable):
objects = StandardApplicationQuerySet.as_manager()

GB = "GB"
NI = "NI"
GOODS_STARTING_POINT_CHOICES = [
Expand Down Expand Up @@ -645,6 +646,9 @@ def clone(self, exclusions=None, **overrides):

class Meta:
ordering = ["created_at"]
indexes = [
models.Index(fields=["created_at"]),
]

@property
def name(self):
Expand Down Expand Up @@ -729,6 +733,11 @@ class PartyOnApplication(TimestampableModel, Clonable):

objects = PartyOnApplicationManager()

class Meta:
indexes = [
models.Index(fields=["created_at"]),
]

def __repr__(self):
return str(
{
Expand Down
6 changes: 2 additions & 4 deletions api/applications/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@
from api.staticdata.statuses.enums import (
CaseStatusEnum,
)
from api.users.enums import SystemUser
from api.users.models import ExporterUser
from api.users.tests.factories import BaseUserFactory
from api.users.tests.factories import SystemUserFactory


class TestBaseApplication(DataTestClient):
Expand Down Expand Up @@ -603,7 +601,7 @@ def test_clone_with_party_override(self):
@pytest.mark.requires_transactions
class TestStandardApplicationRaceConditions(TransactionTestCase):
def test_create_amendment_race_condition_success(self):
BaseUserFactory(id=SystemUser.id)
SystemUserFactory()

original_application = StandardApplicationFactory()

Expand Down
6 changes: 5 additions & 1 deletion api/applications/views/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
from api.audit_trail import service as audit_trail_service
from api.audit_trail.enums import AuditType
from api.cases.enums import AdviceLevel, AdviceType, CaseTypeSubTypeEnum, CaseTypeEnum
from api.cases.models import CaseQueueMovement
from api.cases.generated_documents.models import GeneratedCaseDocument
from api.cases.generated_documents.helpers import auto_generate_case_document
from api.cases.libraries.get_flags import get_flags
Expand Down Expand Up @@ -342,7 +343,10 @@ def put(self, request, pk):
AutoGeneratedDocuments.APPLICATION_FORM,
request.build_absolute_uri(),
)
run_routing_rules(application)
queues_assigned = run_routing_rules(application)
created_at = timezone.now()
for queue in queues_assigned:
CaseQueueMovement.objects.create(case=application.case_ptr, queue_id=queue, created_at=created_at)

# Set the sites on this application as used so their name/site records located at are no longer editable
sites_on_application = SiteOnApplication.objects.filter(application=application)
Expand Down
2 changes: 1 addition & 1 deletion api/assessments/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def test_valid_data_updates_single_record_on_already_verified_good(self):

good = good_on_application.good
assert good.status == GoodStatus.VERIFIED
assert [cle.rating for cle in good.control_list_entries.all()] == ["ML3", "ML1"]
assert sorted([cle.rating for cle in good.control_list_entries.all()]) == sorted(["ML3", "ML1"])
assert good_on_application.report_summary == f"{report_summary_prefix.name} {report_summary_subject.name}"
assert good.report_summary_prefix_id == report_summary_prefix.id
assert good.report_summary_subject_id == report_summary_subject.id
Expand Down
39 changes: 39 additions & 0 deletions api/cases/application_manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from api.applications.models import StandardApplication
from api.applications.serializers.standard_application import StandardApplicationViewSerializer
from api.cases.enums import CaseTypeSubTypeEnum
from api.f680.models import F680Application
from api.f680.caseworker.serializers import F680ApplicationSerializer


class BaseManifest:
caseworker_serializers = {}
model_class = None


class StandardApplicationManifest(BaseManifest):
model_class = StandardApplication
caseworker_serializers = {"view": StandardApplicationViewSerializer}


class F680ApplicationManifest(BaseManifest):
model_class = F680Application
caseworker_serializers = {"view": F680ApplicationSerializer}


# TODO: Make it so that each application django app defines/registers its own
# manifest, instead of doing it all in this file. Probably using a decorator
class ManifestRegistry:

def __init__(self):
self.manifests = {}

def register(self, application_type, manifest):
self.manifests[application_type] = manifest

def get_manifest(self, application_type):
return self.manifests[application_type]


application_manifest_registry = ManifestRegistry()
application_manifest_registry.register(CaseTypeSubTypeEnum.STANDARD, StandardApplicationManifest())
application_manifest_registry.register(CaseTypeSubTypeEnum.F680, F680ApplicationManifest())
Loading

0 comments on commit 0c5e583

Please sign in to comment.