diff --git a/edivorce/apps/core/efilinghub.py b/edivorce/apps/core/efilinghub.py index 931f372d..f618c2aa 100644 --- a/edivorce/apps/core/efilinghub.py +++ b/edivorce/apps/core/efilinghub.py @@ -1,12 +1,19 @@ +import datetime +import hashlib import json -import requests import logging +import re +import requests import uuid from django.conf import settings from django.core.exceptions import PermissionDenied from django.urls import reverse +from edivorce.apps.core.models import Document +from edivorce.apps.core.utils.question_step_mapping import list_of_registries +from edivorce.apps.core.views.pdf import images_to_pdf, pdf_form + logger = logging.getLogger(__name__) PACKAGE_DOCUMENT_FORMAT = { @@ -46,10 +53,58 @@ PACKAGE_FORMAT = { } } +NJF_ALIAS_FORMAT = { + "nameType": "AKA", + "surname": "", + "given1": "", + "given2": "", + "given3": "" +} + +NJF_JSON_FORMAT = { + "parties": [ + { + "label": "Claimant 1", + "surname": "", + "given1": "", + "given2": "", + "given3": "", + "birthDate": "", + "email": "", + "aliases": [], + "surnameAtBirth": "", + "surnameBeforeMarriage": "", + "signingVirtually": False + }, + { + "label": "Claimant 2", + "surname": "", + "given1": "", + "given2": "", + "given3": "", + "birthDate": "", + "email": "", + "aliases": [], + "surnameAtBirth": "", + "surnameBeforeMarriage": "", + "signingVirtually": False + } + ], + "placeOfMarriage": { + "country": "", + "province": "", + "city": "" + }, + "dateOfMarriage": "", + "reasonForDivorce": "S", + "act": "", + "ordersSought": [] +} + class EFilingHub: - def __init__(self): + def __init__(self, initial_filing): self.client_id = settings.EFILING_HUB_CLIENT_ID self.client_secret = settings.EFILING_HUB_CLIENT_SECRET self.token_base_url = settings.EFILING_HUB_TOKEN_BASE_URL @@ -57,6 +112,7 @@ class EFilingHub: self.api_base_url = settings.EFILING_HUB_API_BASE_URL self.submission_id = None + self.initial_filing = initial_filing def _get_token(self, request): payload = f'client_id={self.client_id}&grant_type=client_credentials&client_secret={self.client_secret}' @@ -177,12 +233,141 @@ class EFilingHub: reverse('dashboard_nav', args=['check_with_registry'])) package['navigationUrls']['error'] = request.build_absolute_uri( reverse('dashboard_nav', args=['check_with_registry'])) - package['navigationUrls']['cancel'] = request.build_absolute_uri( - reverse('dashboard_nav', args=['check_with_registry'])) + if self.initial_filing: + package['navigationUrls']['cancel'] = request.build_absolute_uri( + reverse('dashboard_nav', args=['initial_filing'])) + else: + package['navigationUrls']['cancel'] = request.build_absolute_uri( + reverse('dashboard_nav', args=['final_filing'])) return package + def _get_document(self, doc_type, party_code): + document = PACKAGE_DOCUMENT_FORMAT.copy() + filename = self._get_filename(doc_type, party_code) + document['name'] = filename + document['type'] = doc_type + return document + + def _get_filename(self, doc_type, party_code): + form_name = Document.form_types[doc_type] + slug = re.sub('[^0-9a-zA-Z]+', '-', form_name).strip('-') + if party_code == 0: + return slug + ".pdf" + elif party_code == 1: + return slug + "--Claimant1.pdf" + else: + return slug + "--Claimant2.pdf" + + def _get_json_data(self, responses): + + def format_date(str): + try: + return datetime.datetime.strptime(str, '%b %d, %Y').strftime('%Y-%m-%d') + except: + return '' + + r = responses + d = NJF_JSON_FORMAT.copy() + + signing_location_you = '' + signing_location_spouse = '' + + if r.get('how_to_sign') == 'Together': + signing_location_you = r.get('signing_location') + signing_location_spouse = r.get('signing_location') + elif r.get('how_to_sign') == 'Separately': + signing_location_you = r.get('signing_location_you') + signing_location_spouse = r.get('signing_location_spouse') + + party1 = d["parties"][0] + party1["surname"] = r.get('last_name_you', '').strip() + party1["given1"] = r.get('given_name_1_you', '').strip() + party1["given2"] = r.get('given_name_2_you', '').strip() + party1["given3"] = r.get('given_name_3_you', '').strip() + party1["birthDate"] = format_date(r.get('birthday_you')) + party1["surnameAtBirth"] = r.get('last_name_born_you', '').strip() + party1["surnameBeforeMarriage"] = r.get('last_name_before_married_you', '').strip() + email = r.get('email_you', '').strip() + if not email: + email = r.get('address_to_send_official_document_email_you', '').strip() + party1["email"] = email + party1["signingVirtually"] = signing_location_you == 'Virtual' + party1["aliases"] = [] + + party2 = d["parties"][1] + party2["surname"] = r.get('last_name_spouse', '').strip() + party2["given1"] = r.get('given_name_1_spouse', '').strip() + party2["given2"] = r.get('given_name_2_spouse', '').strip() + party2["given3"] = r.get('given_name_3_spouse', '').strip() + party2["birthDate"] = format_date(r.get('birthday_spouse')) + party2["surnameAtBirth"] = r.get('last_name_born_spouse', '').strip() + party2["surnameBeforeMarriage"] = r.get('last_name_before_married_spouse', '').strip() + email = r.get('email_spouse', '').strip() + if not email: + email = r.get('address_to_send_official_document_email_spouse', '').strip() + party2["email"] = email + party2["signingVirtually"] = signing_location_spouse == 'Virtual' + party2["aliases"] = [] + + d["dateOfMarriage"] = format_date(r.get('when_were_you_married')) + + return d + # -- EFILING HUB INTERFACE -- + def get_files(self, request, responses, uploaded, generated): + + post_files = [] + documents = [] + + for form in generated: + doc_type = form['doc_type'] + document = self._get_document(doc_type, 0) + if doc_type == 'NJF': + document['data'] = self._get_json_data(responses) + pdf_response = pdf_form(request, str(form['form_number'])) + document['md5'] = hashlib.md5(pdf_response.content).hexdigest() + post_files.append(('files', (document['name'], pdf_response.content))) + documents.append(document) + + for form in uploaded: + party_code = form['party_code'] + doc_type = form['doc_type'] + pdf_response = images_to_pdf(request, doc_type, party_code) + if pdf_response.status_code == 200: + document = self._get_document(doc_type, party_code) + document['md5'] = hashlib.md5(pdf_response.content).hexdigest() + post_files.append(('files', (document['name'], pdf_response.content))) + documents.append(document) + + return post_files, documents + + def get_parties(self, responses): + + # generate the list of parties to send to eFiling Hub + parties = [] + + party1 = PACKAGE_PARTY_FORMAT.copy() + party1['firstName'] = responses.get('given_name_1_you', '').strip() + party1['middleName'] = (responses.get('given_name_2_you', '') + + ' ' + + responses.get('given_name_3_you', '')).strip() + party1['lastName'] = responses.get('last_name_you', '').strip() + parties.append(party1) + + party2 = PACKAGE_PARTY_FORMAT.copy() + party2['firstName'] = responses.get('given_name_1_spouse', '').strip() + party2['middleName'] = (responses.get('given_name_2_spouse', '') + + ' ' + + responses.get('given_name_3_spouse', '')).strip() + party2['lastName'] = responses.get('last_name_spouse', '').strip() + parties.append(party2) + + return parties + + def get_location(self, responses): + location_name = responses.get('court_registry_for_filing', '') + return list_of_registries.get(location_name, '0000') def upload(self, request, files, documents=None, parties=None, location=None): """ @@ -202,6 +387,8 @@ class EFilingHub: if bce_id is None: raise PermissionDenied() + # package_data, files = self._get_data(request, responses, uploaded, generated) + url = f'{self.api_base_url}/submission/documents' response = self._get_api(request, url, transaction_id, bce_id, headers={}, files=files) if response.status_code == 200: @@ -214,8 +401,8 @@ class EFilingHub: } package_data = self._format_package(request, files, documents, parties, location) url = f"{self.api_base_url}/submission/{response['submissionId']}/generateUrl" - response = self._get_api(request, url, transaction_id, bce_id, headers=headers, - data=json.dumps(package_data)) + data = json.dumps(package_data) + response = self._get_api(request, url, transaction_id, bce_id, headers, data) if response.status_code == 200: response = json.loads(response.text) diff --git a/edivorce/apps/core/middleware/keycloak.py b/edivorce/apps/core/middleware/keycloak.py index b25187ef..a4260536 100644 --- a/edivorce/apps/core/middleware/keycloak.py +++ b/edivorce/apps/core/middleware/keycloak.py @@ -2,8 +2,6 @@ from django.conf import settings from mozilla_django_oidc.auth import OIDCAuthenticationBackend from mozilla_django_oidc.utils import absolutify -from ..models import BceidUser - class EDivorceKeycloakBackend(OIDCAuthenticationBackend): @@ -35,7 +33,7 @@ class EDivorceKeycloakBackend(OIDCAuthenticationBackend): user.user_guid = claims.get('universal-id', '') user.save() - self.request.session['bcgov_userguid'] = user.user_guid + self.request.session['bcgov_userguid'] = user.user_guid return user @@ -47,6 +45,8 @@ class EDivorceKeycloakBackend(OIDCAuthenticationBackend): def keycloak_logout(request): + request.session.flush() + redirect_uri = absolutify(request, settings.FORCE_SCRIPT_NAME) return f'{settings.KEYCLOAK_LOGOUT}?redirect_uri={redirect_uri}' diff --git a/edivorce/apps/core/templates/success.html b/edivorce/apps/core/templates/success.html index 5e2be4ea..2dd9aa5b 100644 --- a/edivorce/apps/core/templates/success.html +++ b/edivorce/apps/core/templates/success.html @@ -28,7 +28,7 @@

Login with a
BCeID or BC Services Card

Once you login, you’ll be taken back to this website.

- + Login
diff --git a/edivorce/apps/core/templatetags/format_utils.py b/edivorce/apps/core/templatetags/format_utils.py index 9959c17c..988eae47 100644 --- a/edivorce/apps/core/templatetags/format_utils.py +++ b/edivorce/apps/core/templatetags/format_utils.py @@ -117,7 +117,7 @@ def age(date): printing '46 years' instead of print '46 years, 7 months'. """ try: - birth = datetime.strptime(date, '%b %d, %Y') + birth = datetime.strptime(date, strptime) except ValueError: try: birth = datetime.strptime(date, '%b %d, %Y') diff --git a/edivorce/apps/core/tests/test_efiling_hub.py b/edivorce/apps/core/tests/test_efiling_hub.py index 79e3380b..6bf11565 100644 --- a/edivorce/apps/core/tests/test_efiling_hub.py +++ b/edivorce/apps/core/tests/test_efiling_hub.py @@ -47,7 +47,7 @@ class EFilingHubTests(TransactionTestCase): middleware.process_request(self.request) self.request.session.save() - self.hub = EFilingHub() + self.hub = EFilingHub(initial_filing=True) def _mock_response(self, status=200, text="Text", json_data=None, raise_for_status=None): mock_resp = mock.Mock() diff --git a/edivorce/apps/core/urls.py b/edivorce/apps/core/urls.py index ce578fac..fb749177 100644 --- a/edivorce/apps/core/urls.py +++ b/edivorce/apps/core/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import url from django.urls import path -from .views import main, system, pdf, api +from .views import main, system, pdf, api, efiling urlpatterns = [ # url(r'^guide$', styleguide.guide), @@ -13,17 +13,16 @@ urlpatterns = [ # we add an extra 'x' to the file extension so the siteminder proxy doesn't treat it as an image path('api/documents///x//', api.DocumentView.as_view(), name='document'), - url(r'^signin$', main.signin, name="signin"), + url(r'^signin$', main.after_login, name="signin"), url(r'^register$', main.register, name="register"), url(r'^register_sc$', main.register_sc, name="register_sc"), - url(r'^logout$', main.logout, name="logout"), url(r'^overview$', main.overview, name="overview"), url(r'^success$', main.success, name="success"), url(r'^incomplete$', main.incomplete, name="incomplete"), url(r'^intercept$', main.intercept_page, name="intercept"), url(r'^dashboard/(?P.*)', main.dashboard_nav, name="dashboard_nav"), - path('submit/initial', main.submit_initial_files, name="submit_initial_files"), - path('submit/final', main.submit_final_files, name="submit_final_files"), + path('submit/initial', efiling.submit_initial_files, name="submit_initial_files"), + path('submit/final', efiling.submit_final_files, name="submit_final_files"), url(r'^health$', system.health), url(r'^legal$', main.legal, name="legal"), url(r'^acknowledgements$', main.acknowledgements, name="acknowledgements"), diff --git a/edivorce/apps/core/utils/cso_filing.py b/edivorce/apps/core/utils/cso_filing.py index 317e293b..ae8925be 100644 --- a/edivorce/apps/core/utils/cso_filing.py +++ b/edivorce/apps/core/utils/cso_filing.py @@ -1,13 +1,14 @@ import random -import re from django.conf import settings +from edivorce.apps.core.efilinghub import EFilingHub from edivorce.apps.core.models import Document, UserResponse from edivorce.apps.core.utils.derived import get_derived_data -def file_documents(user, responses, initial=False): +def file_documents(request, responses, initial=False): + user = request.user errors = [] if not initial: user_has_submitted_initial = _get_response(user, 'initial_filing_submitted') @@ -16,14 +17,26 @@ def file_documents(user, responses, initial=False): court_file_number = _get_response(user, 'court_file_number') if not court_file_number: errors.append("You must input your Court File Number") - uploaded_forms, _ = forms_to_file(responses, initial) - for form in uploaded_forms: + + uploaded, generated = forms_to_file(responses, initial) + for form in uploaded: docs = Document.objects.filter(bceid_user=user, doc_type=form['doc_type'], party_code=form.get('party_code', 0)) if docs.count() == 0: errors.append(f"Missing documents for {Document.form_types[form['doc_type']]}") if errors: - return errors + return errors, None + + hub = EFilingHub(initial_filing=initial) + + post_files, documents = hub.get_files(request, responses, uploaded, generated) + location = hub.get_location(responses) + parties = hub.get_parties(responses) + + redirect_url, msg = hub.upload(request, post_files, documents, parties, location) + + if redirect_url: + return errors, redirect_url # Save dummy data for now. Eventually replace with data from CSO prefix = 'initial' if initial else 'final' @@ -52,6 +65,7 @@ def file_documents(user, responses, initial=False): package_link = base_url + '/cso/register.do?packageNumber=' + package_number _save_response(user, f'{prefix}_filing_package_link', package_link) + return None, None def _save_response(user, question, value): @@ -164,14 +178,3 @@ def forms_to_file(responses_dict, initial=False): return [], [] return uploaded, generated - - -def get_filename(doc_type, party_code): - form_name = Document.form_types[doc_type] - slug = re.sub('[^0-9a-zA-Z]+', '-', form_name).strip('-') - if party_code == 0: - return slug + ".pdf" - elif party_code == 1: - return slug + "--Claimant1.pdf" - else: - return slug + "--Claimant2.pdf" diff --git a/edivorce/apps/core/views/efiling.py b/edivorce/apps/core/views/efiling.py new file mode 100644 index 00000000..470035fb --- /dev/null +++ b/edivorce/apps/core/views/efiling.py @@ -0,0 +1,45 @@ +from django.contrib import messages +from django.shortcuts import redirect +from django.urls import reverse +from django.contrib.auth.decorators import login_required + +from ..decorators import prequal_completed +from ..utils.cso_filing import file_documents +from ..utils.user_response import get_data_for_user + + +@login_required +@prequal_completed +def submit_initial_files(request): + return _submit_files(request, initial=True) + + +@login_required +@prequal_completed +def submit_final_files(request): + return _submit_files(request, initial=False) + + +def _submit_files(request, initial=False): + responses_dict = get_data_for_user(request.user) + + errors, hub_redirect_url = file_documents(request, responses_dict, initial=initial) + + if hub_redirect_url: + return redirect(hub_redirect_url) + + if initial: + original_step = 'initial_filing' + next_page = 'wait_for_number' + else: + original_step = 'final_filing' + next_page = 'next_steps' + + if errors: + next_page = original_step + for error in errors: + messages.add_message(request, messages.ERROR, error) + + responses_dict['active_page'] = next_page + + return redirect(reverse('dashboard_nav', kwargs={'nav_step': next_page}), context=responses_dict) diff --git a/edivorce/apps/core/views/main.py b/edivorce/apps/core/views/main.py index 14cc384b..5970880b 100644 --- a/edivorce/apps/core/views/main.py +++ b/edivorce/apps/core/views/main.py @@ -1,17 +1,12 @@ -import datetime -import hashlib - from django.conf import settings from django.contrib import messages from django.shortcuts import render, redirect from django.urls import reverse -from django.utils import timezone from django.contrib.auth.decorators import login_required from edivorce.apps.core.utils.derived import get_derived_data from ..decorators import intercept, prequal_completed -from ..utils.cso_filing import file_documents, forms_to_file, get_filename -from ..efilinghub import EFilingHub, PACKAGE_PARTY_FORMAT, PACKAGE_DOCUMENT_FORMAT +from ..utils.cso_filing import forms_to_file from ..utils.question_step_mapping import list_of_registries from ..utils.step_completeness import get_error_dict, get_missed_question_keys, get_step_completeness, is_complete, get_formatted_incomplete_list from ..utils.template_step_order import template_step_order @@ -22,7 +17,6 @@ from ..utils.user_response import ( get_responses_from_session, get_responses_from_session_grouped_by_steps, ) -from .pdf import images_to_pdf, pdf_form def home(request): @@ -117,34 +111,15 @@ def register_sc(request): return redirect(settings.REGISTER_BCSC_URL) -def signin(request): +def after_login(request): if not request.user.is_authenticated: return render(request, '407.html') - # I think Django might be doing this automatically now that we have switched to mozilla-django-oidc? - # if timezone.now() - request.user.last_login > datetime.timedelta(minutes=1): - # request.user.last_login = timezone.now() - # request.user.save() - copy_session_to_db(request, request.user) return redirect(settings.PROXY_BASE_URL + settings.FORCE_SCRIPT_NAME[:-1] + '/overview') -def logout(request): - """ - Clear session and log out of BCeID - """ - request.session.flush() - - response = redirect(settings.LOGOUT_URL) - - if settings.DEPLOYMENT_TYPE in ['localdev', 'minishift']: - response = redirect('/') - - return response - - @login_required @prequal_completed @intercept @@ -218,97 +193,6 @@ def _add_error_messages(nav_step, request, responses_dict): 'Please try again now and if this issue persists try again later.') -@login_required -@prequal_completed -def submit_initial_files(request): - return _submit_files(request, initial=True) - - -@login_required -@prequal_completed -def submit_final_files(request): - return _submit_files(request, initial=False) - - -def _submit_files(request, initial=False): - responses_dict = get_data_for_user(request.user) - if initial: - original_step = 'initial_filing' - next_page = 'wait_for_number' - else: - original_step = 'final_filing' - next_page = 'next_steps' - errors = file_documents(request.user, responses_dict, initial=initial) - if errors: - next_page = original_step - for error in errors: - messages.add_message(request, messages.ERROR, error) - responses_dict['active_page'] = next_page - return redirect(reverse('dashboard_nav', kwargs={'nav_step': next_page}), context=responses_dict) - responses_dict['active_page'] = next_page - - ################# - # todo: refactor this!!!! - - post_files = [] - documents = [] - - (uploaded, generated) = forms_to_file(responses_dict, initial) - - for form in generated: - pdf_response = pdf_form(request, str(form['form_number'])) - document = PACKAGE_DOCUMENT_FORMAT.copy() - filename = get_filename(form['doc_type'], 0) - document['name'] = filename - document['type'] = form['doc_type'] - document['md5'] = hashlib.md5(pdf_response.content).hexdigest() - post_files.append(('files', (filename, pdf_response.content))) - documents.append(document) - - for form in uploaded: - pdf_response = images_to_pdf(request, form['doc_type'], form['party_code']) - if pdf_response.status_code == 200: - document = PACKAGE_DOCUMENT_FORMAT.copy() - filename = get_filename(form['doc_type'], 0) - document['name'] = filename - document['type'] = form['doc_type'] - document['md5'] = hashlib.md5(pdf_response.content).hexdigest() - post_files.append(('files', (filename, pdf_response.content))) - documents.append(document) - - # generate the list of parties to send to eFiling Hub - parties = [] - - party1 = PACKAGE_PARTY_FORMAT.copy() - party1['firstName'] = responses_dict.get('given_name_1_you', '').strip() - party1['middleName'] = (responses_dict.get('given_name_2_you', '') + - ' ' + - responses_dict.get('given_name_3_you', '')).strip() - party1['lastName'] = responses_dict.get('last_name_you', '').strip() - parties.append(party1) - - party2 = PACKAGE_PARTY_FORMAT.copy() - party2['firstName'] = responses_dict.get('given_name_1_spouse', '').strip() - party2['middleName'] = (responses_dict.get('given_name_2_spouse', '') + - ' ' + - responses_dict.get('given_name_3_spouse', '')).strip() - party2['lastName'] = responses_dict.get('last_name_spouse', '').strip() - parties.append(party2) - - location_name = responses_dict.get('court_registry_for_filing', '') - location = list_of_registries.get(location_name, '0000') - - hub = EFilingHub() - redirect_url, msg = hub.upload(request, post_files, documents, parties, location) - - if redirect_url: - return redirect(redirect_url) - - ################# - - return redirect(reverse('dashboard_nav', kwargs={'nav_step': next_page}), context=responses_dict) - - @login_required @prequal_completed def question(request, step, sub_step=None): diff --git a/edivorce/apps/poc/views.py b/edivorce/apps/poc/views.py index 69c8c566..f6f09fae 100644 --- a/edivorce/apps/poc/views.py +++ b/edivorce/apps/poc/views.py @@ -99,7 +99,7 @@ class EfilingHubUpload(FormView): party['lastName'] = 'Test' parties.append(party) - hub = EFilingHub() + hub = EFilingHub(initial_filing=True) redirect, msg = hub.upload(request, post_files, parties=parties) if redirect: self.success_url = redirect