Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SPIKE] Make security approval section driven by exporter answer API #2337

Draft
wants to merge 5 commits into
base: dev
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions caseworker/cases/helpers/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from caseworker.core.services import get_user_permissions, get_status_properties, get_permissible_statuses
from lite_content.lite_internal_frontend import cases
from lite_content.lite_internal_frontend.cases import CasePage, ApplicationPage
from caseworker.exporter_answers.services import get_exporter_answer_set
from caseworker.queues.services import get_queue
from caseworker.users.services import get_gov_user

Expand Down Expand Up @@ -199,6 +200,7 @@ def get_context(self):
if rules.test_rule("can_licence_status_be_changed", self.request, licence):
show_actions_column = True
break
exporter_answers, _ = get_exporter_answer_set(self.request, self.case_id)

return {
**context,
Expand All @@ -207,6 +209,7 @@ def get_context(self):
"slices": [Slices.SUMMARY, *self.slices],
"case": self.case,
"queue": self.queue,
"exporter_answers": exporter_answers["results"],
"is_system_queue": self.queue["is_system_queue"],
"goods_summary": self.get_goods_summary(),
"destination_countries": self.get_destination_countries(),
Expand Down
Empty file.
8 changes: 8 additions & 0 deletions caseworker/exporter_answers/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from core import client


def get_exporter_answer_set(request, target_object_id):
response = client.get(
request, f"/caseworker/exporter-answers/exporter-answer-set/?target_object_id={target_object_id}&status=active"
)
return response.json(), response.status_code
12 changes: 12 additions & 0 deletions caseworker/templates/components/security-approvals.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,15 @@ <h2 class="govuk-heading-m">Security approvals</h2>
{% endif %}
{% endif %}
</dl>

{% for exporter_answer_set in exporter_answers %}
<h2 class="govuk-heading-m">{{exporter_answer_set.section}}</h2>
<dl class="govuk-summary-list">
{% for field in exporter_answer_set.answer_fields %}
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">{{exporter_answer_set.questions|getitem:field}}</dt>
<dd class="govuk-summary-list__value">{{exporter_answer_set.answers|getitem:field|to_display}}</dd>
</div>
{% endfor %}
</dl>
{% endfor %}
11 changes: 10 additions & 1 deletion core/builtins/custom_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from django import template
from django.conf import settings
from django.http import QueryDict
from django.template.defaultfilters import stringfilter, safe, capfirst
from django.template.defaultfilters import stringfilter, safe, capfirst, yesno
from django.templatetags.tz import localtime
from django.utils.html import escape
from django.utils.safestring import mark_safe, SafeString
Expand Down Expand Up @@ -1060,3 +1060,12 @@ def __init__(self, text="..."):
context["paging_form_id"] = form_id

return context


@register.filter
def to_display(value):
if type(value) == bool:
return capfirst(yesno(value))
if type(value) == list:
return ", ".join(value)
return value
8 changes: 8 additions & 0 deletions exporter/applications/views/goods/common/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from exporter.applications.services import get_application
from exporter.goods.services import get_good, get_good_on_application
from exporter.exporter_answers.services import get_exporter_answer_set


class GoodOnApplicationMixin:
Expand Down Expand Up @@ -40,4 +41,11 @@ def dispatch(self, request, *args, **kwargs):
except requests.exceptions.HTTPError:
raise Http404(f"Couldn't get application {kwargs['pk']}")

exporter_answers, _ = get_exporter_answer_set(request, kwargs["pk"])
self.exporter_answers = {}
self.exporter_questions = {}
for answer_set in exporter_answers["results"]:
self.exporter_answers[answer_set["section"]] = answer_set["answers"]
self.exporter_questions[answer_set["section"]] = answer_set["questions"]

return super().dispatch(request, *args, **kwargs)
9 changes: 9 additions & 0 deletions exporter/applications/views/goods/common/payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ def get_cleaned_data(form):
return form.cleaned_data


def get_questions_data(form):
if not form.cleaned_data:
return {}
questions = {}
for field_name, field in form.declared_fields.items():
questions[field_name] = field.label
return questions


def get_pv_grading_payload(form):
return {
"is_pv_graded": "yes" if form.cleaned_data["is_pv_graded"] else "no",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
is_f1686_approval_changed_and_selected,
is_other_approval_changed_and_selected,
)
from .payloads import get_f1686_data, SecurityApprovalStepsPayloadBuilder
from .payloads import get_f1686_data
from .constants import SecurityApprovalSteps
from .initial import (
get_initial_security_classified_details,
Expand Down
23 changes: 10 additions & 13 deletions exporter/applications/views/security_approvals/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ class SecurityClassifiedDetailsForm(BaseForm):
class Layout:
TITLE = "If you are exporting security classified products, you may need a Ministry of Defence (MOD) approval"

label = """
This includes the release of United States ITAR (International Traffic in Arms regulations) material.<br><br>
Do you have an MOD security approval, such as F680 or F1686?
"""

security_approvals = forms.MultipleChoiceField(
choices=SecurityClassifiedApprovalsType.choices,
label="What type of approval do you have?",
Expand All @@ -38,7 +33,8 @@ class Layout:
(False, "No"),
),
coerce=coerce_str_to_bool,
label=label,
label="Do you have an MOD security approval, such as F680 or F1686?",
help_text="This includes the release of United States ITAR (International Traffic in Arms regulations) material.",
error_messages={
"required": "Select no if you do not have an MOD security approval",
},
Expand Down Expand Up @@ -75,7 +71,7 @@ class SubjectToITARControlsForm(BaseForm):
class Layout:
TITLE = "Are any products on this application subject to ITAR controls?"

label = """
help_text = """
We need to know if this export involves any defence articles including technical data that are
subject to controls under the United States (US) International Traffic in Arms regulations (ITAR).
"""
Expand All @@ -87,7 +83,8 @@ class Layout:
),
coerce=coerce_str_to_bool,
widget=forms.RadioSelect,
label=label,
label="Are any products on this application subject to ITAR controls?",
help_text=help_text,
error_messages={
"required": "Select no if the products are not subject to ITAR controls",
},
Expand All @@ -103,7 +100,7 @@ class Layout:

f680_reference_number = forms.CharField(
widget=forms.TextInput,
label="",
label="What is the F680 reference number?",
error_messages={
"required": " Enter the F680 reference number",
},
Expand All @@ -128,11 +125,12 @@ class Layout:
f1686_reference_number = forms.CharField(
required=False,
widget=forms.TextInput,
label="Reference number (optional)",
label="What is the F1686 reference number?",
help_text="Reference number (optional)",
)

f1686_approval_date = CustomErrorDateInputField(
label="Approval date",
label="When was the F1686 approved?",
require_all_fields=False,
help_text=f"For example, 20 2 {datetime.now().year-2}",
error_messages={
Expand Down Expand Up @@ -169,8 +167,7 @@ class Layout:

other_security_approval_details = forms.CharField(
widget=forms.Textarea(attrs={"rows": 5}),
label="",
help_text="Enter any details you have about the MOD contracting authority, reference numbers, "
label="Enter any details you have about the MOD contracting authority, reference numbers, "
"the signatory of the approval, or the Project Security Instruction.",
error_messages={
"required": "Enter the details of your written approval",
Expand Down
14 changes: 12 additions & 2 deletions exporter/applications/views/security_approvals/payloads.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from core.wizard.payloads import MergingPayloadBuilder

from .constants import SecurityApprovalSteps
from exporter.applications.views.goods.common.payloads import get_cleaned_data
from exporter.applications.views.goods.common.payloads import get_cleaned_data, get_questions_data


def get_f1686_data(form):
Expand All @@ -10,11 +10,21 @@ def get_f1686_data(form):
return payload


class SecurityApprovalStepsPayloadBuilder(MergingPayloadBuilder):
class SecurityApprovalStepsAnswerPayloadBuilder(MergingPayloadBuilder):
payload_dict = {
SecurityApprovalSteps.SECURITY_CLASSIFIED: get_cleaned_data,
SecurityApprovalSteps.SUBJECT_TO_ITAR_CONTROLS: get_cleaned_data,
SecurityApprovalSteps.F680_REFERENCE_NUMBER: get_cleaned_data,
SecurityApprovalSteps.F1686_DETAILS: get_f1686_data,
SecurityApprovalSteps.SECURITY_OTHER_DETAILS: get_cleaned_data,
}


class SecurityApprovalStepsQuestionPayloadBuilder(MergingPayloadBuilder):
payload_dict = {
SecurityApprovalSteps.SECURITY_CLASSIFIED: get_questions_data,
SecurityApprovalSteps.SUBJECT_TO_ITAR_CONTROLS: get_questions_data,
SecurityApprovalSteps.F680_REFERENCE_NUMBER: get_questions_data,
SecurityApprovalSteps.F1686_DETAILS: get_questions_data,
SecurityApprovalSteps.SECURITY_OTHER_DETAILS: get_questions_data,
}
31 changes: 21 additions & 10 deletions exporter/applications/views/security_approvals/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from core.constants import SecurityClassifiedApprovalsType

from core.wizard.views import BaseSessionWizardView
from exporter.applications.services import put_application
from exporter.exporter_answers.services import post_exporter_answer_set
from exporter.applications.views.goods.common.mixins import ApplicationMixin

from .forms import (
Expand All @@ -23,7 +23,7 @@

from .constants import SecurityApprovalSteps
from .conditionals import is_f680_approval, is_f1686_approval, is_other_approval
from .payloads import SecurityApprovalStepsPayloadBuilder
from .payloads import SecurityApprovalStepsAnswerPayloadBuilder, SecurityApprovalStepsQuestionPayloadBuilder

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -65,8 +65,12 @@ def get_context_data(self, form, **kwargs):

return ctx

def get_payload(self, form_dict):
export_details_payload = SecurityApprovalStepsPayloadBuilder().build(form_dict)
def get_answers_payload(self, form_dict):
export_details_payload = SecurityApprovalStepsAnswerPayloadBuilder().build(form_dict)
return export_details_payload

def get_questions_payload(self, form_dict):
export_details_payload = SecurityApprovalStepsQuestionPayloadBuilder().build(form_dict)
return export_details_payload

def get_success_url(self):
Expand All @@ -76,20 +80,25 @@ def get_success_url(self):
)

@expect_status(
HTTPStatus.OK,
HTTPStatus.CREATED,
"Error updating export details",
"Unexpected error updating export details",
)
def update_application(self, form_dict):
payload = self.get_payload(form_dict)
return put_application(
def submit_answers(self, form_dict):
answers_payload = self.get_answers_payload(form_dict)
questions_payload = self.get_questions_payload(form_dict)
return post_exporter_answer_set(
self.request,
"application",
"security_approvals",
"standardapplication",
self.application["id"],
payload,
answers_payload,
questions_payload,
)

def done(self, form_list, form_dict, **kwargs):
self.update_application(form_dict)
self.submit_answers(form_dict)
return redirect(self.get_success_url())


Expand All @@ -98,6 +107,8 @@ class SecurityApprovalsSummaryView(LoginRequiredMixin, ApplicationMixin, Templat

def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context["security_approval_answers"] = self.exporter_answers.get("security_approvals")
context["security_approval_questions"] = self.exporter_questions.get("security_approvals")
context["application"] = self.application
context["back_link_url"] = reverse("applications:task_list", kwargs={"pk": self.kwargs["pk"]})
context["security_classified_approvals_types"] = SecurityClassifiedApprovalsType
Expand Down
Empty file.
25 changes: 25 additions & 0 deletions exporter/exporter_answers/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from django.conf import settings


from core import client


def post_exporter_answer_set(request, flow, section, target_content_type, target_object_id, answers, questions):
data = {
"flow": flow,
"section": section,
"answers": answers,
"questions": questions,
"frontend_commit_sha": settings.GIT_COMMIT,
"target_content_type": target_content_type,
"target_object_id": target_object_id,
}
response = client.post(request, f"/exporter/exporter-answers/exporter-answer-set/", data)
return response.json(), response.status_code


def get_exporter_answer_set(request, target_object_id):
response = client.get(
request, f"/exporter/exporter-answers/exporter-answer-set/?target_object_id={target_object_id}&status=active"
)
return response.json(), response.status_code
Loading