From 0982755b082d9329763859739c3ff3b231161258 Mon Sep 17 00:00:00 2001 From: Justin Johnson Date: Tue, 9 Jan 2018 09:38:05 -0800 Subject: [PATCH] DIV-578 --- edivorce/apps/core/context_processors.py | 1 + edivorce/apps/core/static/css/weasyprint.css | 22 ++ .../core/templates/dashboard/print_form.html | 2 +- .../apps/core/templates/pdf/form37_we.html | 327 ++++++++++++++++++ .../apps/core/templates/pdf/form38_we.html | 2 +- .../apps/core/templatetags/format_utils.py | 58 +++- edivorce/apps/core/utils/derived.py | 114 +++++- edivorce/apps/core/views/pdf.py | 67 ++-- edivorce/settings/base.py | 6 +- 9 files changed, 543 insertions(+), 56 deletions(-) create mode 100644 edivorce/apps/core/templates/pdf/form37_we.html diff --git a/edivorce/apps/core/context_processors.py b/edivorce/apps/core/context_processors.py index a4c3fc38..262f467d 100644 --- a/edivorce/apps/core/context_processors.py +++ b/edivorce/apps/core/context_processors.py @@ -2,6 +2,7 @@ from django.conf import settings def settings_processor(request): + """ Add settings data to context to make visible in debug toolbar """ return { 'gtm_id': settings.GTM_ID, 'proxy_root_path': settings.FORCE_SCRIPT_NAME, diff --git a/edivorce/apps/core/static/css/weasyprint.css b/edivorce/apps/core/static/css/weasyprint.css index 29707385..8cecc6be 100644 --- a/edivorce/apps/core/static/css/weasyprint.css +++ b/edivorce/apps/core/static/css/weasyprint.css @@ -57,6 +57,11 @@ i.fa { min-width: 200px; } +.form-entry-md { + display: inline-block; + min-width: 100px; +} + .form-entry-sm { display: inline-block; min-width: 50px; @@ -155,3 +160,20 @@ ol.numbered-paragraphs li { .td-list-text .form-textarea { margin-left: 0em; } + +.notary-signature { + width: 100%; +} + +.notary-signature td { + width: 47%; +} + +.notary-signature td.signature-above { + border-top: solid 1px black !important; +} + +.notary-signature td.parens { + text-align: center; + width: 6%; +} diff --git a/edivorce/apps/core/templates/dashboard/print_form.html b/edivorce/apps/core/templates/dashboard/print_form.html index dfcb8711..796ce274 100644 --- a/edivorce/apps/core/templates/dashboard/print_form.html +++ b/edivorce/apps/core/templates/dashboard/print_form.html @@ -184,7 +184,7 @@

- Review and Print diff --git a/edivorce/apps/core/templates/pdf/form37_we.html b/edivorce/apps/core/templates/pdf/form37_we.html new file mode 100644 index 00000000..68e86cfa --- /dev/null +++ b/edivorce/apps/core/templates/pdf/form37_we.html @@ -0,0 +1,327 @@ +{% load static %} +{% load composites %} +{% load input_field %} +{% load format_utils %} +{% load humanize %} + + + + + + + + Form 37, Joint + + + + + + + + + {% include 'partials/gtm_head.html' %} + + + +

+ + + diff --git a/edivorce/apps/core/templates/pdf/form38_we.html b/edivorce/apps/core/templates/pdf/form38_we.html index 15c3cd71..4cccca67 100644 --- a/edivorce/apps/core/templates/pdf/form38_we.html +++ b/edivorce/apps/core/templates/pdf/form38_we.html @@ -240,7 +240,7 @@ - A commissioner for taking + A commissioner for taking ) diff --git a/edivorce/apps/core/templatetags/format_utils.py b/edivorce/apps/core/templatetags/format_utils.py index 6dc9f4fb..b99b2b3b 100644 --- a/edivorce/apps/core/templatetags/format_utils.py +++ b/edivorce/apps/core/templatetags/format_utils.py @@ -1,14 +1,19 @@ -from django import template from datetime import datetime +import locale import re + +from django import template from django.utils.safestring import mark_safe +from django.utils.timesince import timesince + +locale.setlocale(locale.LC_ALL, 'en_CA.utf-8') register = template.Library() @register.filter def linebreaksli(value): - "Converts strings with newlines into
  • s" + """ Converts strings with newlines into
  • s """ value = re.sub(r'\r\n|\r|\n', '\n', value.strip()) # normalize newlines lines = re.split('\n', value) lines = ['
  • %s
  • ' % line for line in lines if line and not line.isspace()] @@ -17,22 +22,20 @@ def linebreaksli(value): @register.filter def date_formatter(value): - """ - Changes date format from dd/mm/yyyy to dd/mmm/yyyy - """ + """ Changes date format from dd/mm/yyyy to dd/mmm/yyyy """ if value is None or value == '': return '' try: - d = datetime.strptime(value, '%d/%m/%Y') + date = datetime.strptime(value, '%d/%m/%Y') except ValueError: - d = None + date = None - if d is None: - d = datetime.strptime(value, '%b %d, %Y') + if date is None: + date = datetime.strptime(value, '%b %d, %Y') - return d.strftime('%d %b %Y') + return date.strftime('%d %b %Y') @register.simple_tag() @@ -83,6 +86,37 @@ def checkbox(context, *args, **kwargs): @register.filter def claimantize(value, claimant='1'): """ Replace 'you' with 'claimant 1' and 'spouse' with 'claimant 2' """ - value = value.replace('you', 'claimant %s' % claimant) - value = value.replace('spouse', 'claimant %s' % '2' if claimant == '1' else '1') + value = value.replace('you', 'claimant\xa0%s' % claimant) + value = value.replace('spouse', 'claimant\xa0%s' % '2' if claimant == '1' else '1') return value + +@register.filter +def age(date): + """ + Return the difference between now and date in the largest unit. + + This uses Django's timesince filter but takes only the first term, + printing '46 years' instead of print '46 years, 7 months'. + """ + try: + birth = datetime.strptime(date, '%b %d, %Y') + except ValueError: + try: + birth = datetime.strptime(date, '%b %d, %Y') + except ValueError: + birth = None + if birth is not None: + return timesince(birth).split(',')[0] + return '' + + +@register.filter +def money(amount): + """ Return a properly formatted currency string including symbol """ + + try: + return locale.currency(float(amount), grouping=True) + except ValueError: + pass + + return '' diff --git a/edivorce/apps/core/utils/derived.py b/edivorce/apps/core/utils/derived.py index caafec33..fd5d06c0 100644 --- a/edivorce/apps/core/utils/derived.py +++ b/edivorce/apps/core/utils/derived.py @@ -1,3 +1,4 @@ +# pylint: disable=W0613 """Values derived from a user's responses. This module provides functions to take a set of responses from a user and create @@ -12,13 +13,26 @@ under the _derived_ key. import json -DERIVED = [ +# This array is order sensitive: later functions may depend on values from +# earlier ones +DERIVED_DATA = [ + 'orders_wanted', + 'children', + 'wants_divorce_order', + 'wants_spousal_support', + 'wants_property_division', + 'wants_child_support', + 'wants_other_orders', 'show_fact_sheet_a', 'show_fact_sheet_b', 'show_fact_sheet_c', 'show_fact_sheet_d', 'show_fact_sheet_e', 'show_fact_sheet_f', + 'has_fact_sheets', + 'schedule_1_amount', + 'schedule_1_payor', + 'fact_sheet_a_total', ] @@ -26,10 +40,58 @@ def get_derived_data(responses): """ Return a dict of data derived from the user's responses """ functions = globals() - return {func: functions[func](responses) for func in DERIVED} + derived = {} + for func in DERIVED_DATA: + derived[func] = functions[func](responses, derived) + return derived -def show_fact_sheet_a(responses): +def orders_wanted(responses, derived): + """ Return a list of orders the user has indicated """ + + return json.loads(responses.get('want_which_orders', '[]')) + + +def children(responses, derived): + """ Return a list of child dicts, parsed from ``claimants_children`` """ + + return json.loads(responses.get('claimant_children', '[]')) + + +def wants_divorce_order(responses, derived): + """ Return whether or not the user wants an order for divorce """ + + return 'A legal end to the marriage' in derived['orders_wanted'] + + +def wants_spousal_support(responses, derived): + """ Return whether or not the user wants an order for spousal support """ + + return 'Spousal support' in derived['orders_wanted'] + + +def wants_property_division(responses, derived): + """ + Return whether or not the user wants an order for division of property and + debts + """ + + return 'Division of property and debts' in responses.get('want_which_orders', '') + + +def wants_child_support(responses, derived): + """ Return whether or not the user wants an order for child_support """ + + return 'Child support' in derived['orders_wanted'] + + +def wants_other_orders(responses, derived): + """ Return whether or not the user wants other orders """ + + return 'Other orders' in derived['orders_wanted'] + + +def show_fact_sheet_a(responses, derived): """ If the claimant is claiming special extraordinary expenses, Fact Sheet A is indicated. @@ -39,19 +101,17 @@ def show_fact_sheet_a(responses): return len(expenses) > 0 and "None" not in expenses -def show_fact_sheet_b(responses): +def show_fact_sheet_b(responses, derived): """ If any child lives with both parents, custody is shared, so Fact Sheet B is indicated. """ - children = json.loads(responses.get('claimant_children', '[]')) - print(children) return any([child['child_live_with'] == 'Lives with both' - for child in children]) + for child in derived['children']]) -def show_fact_sheet_c(responses): +def show_fact_sheet_c(responses, derived): """ If any child lives with one parent and there's another child who lives with the other parent or is shared, Fact Sheet C is indicated. @@ -60,8 +120,7 @@ def show_fact_sheet_c(responses): with_you = 0 with_spouse = 0 with_both = 0 - children = json.loads(responses.get('claimant_children', '[]')) - for child in children: + for child in derived['children']: if child['child_live_with'] == 'Lives with you': with_you += 1 elif child['child_live_with'] == 'Lives with spouse': @@ -72,7 +131,7 @@ def show_fact_sheet_c(responses): with_spouse > 0 and (with_you + with_both > 0)) -def show_fact_sheet_d(responses): +def show_fact_sheet_d(responses, derived): """ If a claimaint is claiming financial support for a child of the marriage over 19, Fact Sheet D is indicated. @@ -82,7 +141,7 @@ def show_fact_sheet_d(responses): return len(support) > 0 and 'NO' not in support and responses.get('children_of_marriage', '') == 'YES' -def show_fact_sheet_e(responses): +def show_fact_sheet_e(responses, derived): """ If the claimant is claiming undue hardship, Fact Sheet E is indicated. """ @@ -90,7 +149,7 @@ def show_fact_sheet_e(responses): return responses.get('claiming_undue_hardship', '') == 'YES' -def show_fact_sheet_f(responses): +def show_fact_sheet_f(responses, derived): """ If one of the claimants earns over $150,000, Fact Sheet F is indicated. """ @@ -106,3 +165,32 @@ def show_fact_sheet_f(responses): spouses = 0 return annual > 150000 or spouses > 150000 + + +def has_fact_sheets(responses, derived): + """ Return whether or not the user is submitting fact sheets """ + + return any([derived['show_fact_sheet_a'], derived['show_fact_sheet_b'], + derived['show_fact_sheet_c'], derived['show_fact_sheet_d'], + derived['show_fact_sheet_e'], derived['show_fact_sheet_f'], ]) + + +def schedule_1_amount(responses, derived): + """ Return the amount as defined in schedule 1 for child support """ + + return 155689.333 + + +def schedule_1_payor(responses, derived): + """ Return the amount as defined in schedule 1 for child support """ + + return 'Claimant 1' + + +def fact_sheet_a_total(responses, derived): + """ Return the total amount of special expenses from Fact Sheet A """ + + return 0 + + + diff --git a/edivorce/apps/core/views/pdf.py b/edivorce/apps/core/views/pdf.py index 097b895a..c4e233d7 100644 --- a/edivorce/apps/core/views/pdf.py +++ b/edivorce/apps/core/views/pdf.py @@ -1,43 +1,58 @@ +""" Views for generated forms """ + import json -from django.template.loader import render_to_string +from django.conf import settings from django.http import HttpResponse -import requests +from django.template.loader import render_to_string -from django.conf import settings +import requests -from edivorce.apps.core.decorators import bceid_required -from edivorce.apps.core.models import BceidUser +from ..decorators import bceid_required +from ..utils.derived import get_derived_data from ..utils.user_response import get_responses_from_db +letters = 'abcdefghijklmnopqrstuvwxyz' +exhibits = list(letters.upper()[::-1]) @bceid_required def form(request, form_number): - """ - View for rendering PDF's and previews - """ + """ View for rendering PDF's and previews """ + responses = get_responses_from_db(request.user) - if form_number == "1": + if form_number == '1' or form_number.startswith('37'): # Add an array of children that includes blanks for possible children under = int(responses.get('number_children_under_19') or 0) over = int(responses.get('number_children_under_19') or 0) actual = json.loads(responses.get('claimant_children', '[]')) total = len(actual) - responses["children"] = [actual[i] if i < total else {} + responses['children'] = [actual[i] if i < total else {} for i in range(0, max(under + over, total))] - elif form_number == "38_claimant1": - form_number = "38" + + if form_number == "37_claimant1": + form_number = "37" responses = __add_claimant_info(responses, '_you') responses["which_claimant"] = "Claimant 1" - elif form_number == "38_claimant2": - form_number = "38" + elif form_number == "37_claimant2": + form_number = "37" responses = __add_claimant_info(responses, '_spouse') responses["which_claimant"] = "Claimant 2" + if form_number == "38_claimant1": + form_number = "38" + responses = __add_claimant_info(responses, '_you') + responses['which_claimant'] = 'Claimant 1' + elif form_number == '38_claimant2': + form_number = '38' + responses = __add_claimant_info(responses, '_spouse') + responses['which_claimant'] = 'Claimant 2' + return __render_form(request, 'form%s' % form_number, { - "css_root": settings.WEASYPRINT_CSS_LOOPBACK, - "responses": responses + 'css_root': settings.WEASYPRINT_CSS_LOOPBACK, + 'responses': responses, + 'derived': get_derived_data(responses), + 'exhibits': exhibits[:], }) @@ -46,25 +61,25 @@ def __render_form(request, form_name, context): output_as_html = request.GET.get('html', None) is not None if output_as_html: - context["css_root"] = settings.FORCE_SCRIPT_NAME[:-1] + context['css_root'] = settings.FORCE_SCRIPT_NAME[:-1] # render to form as HTML - rendered_html = render_to_string('pdf/' + form_name + '.html', context=context, request=request) + rendered_html = render_to_string('pdf/' + form_name + '.html', + context=context, request=request) # if '?html' is in the querystring, then return the plain html if output_as_html: return HttpResponse(rendered_html) - else: - # post the html to the weasyprint microservice - url = settings.WEASYPRINT_URL + '/pdf?filename=' + form_name + '.pdf' - pdf = requests.post(url, data=rendered_html) + # post the html to the weasyprint microservice + url = settings.WEASYPRINT_URL + '/pdf?filename=' + form_name + '.pdf' + pdf = requests.post(url, data=rendered_html.encode('utf-8')) - # return the response as a pdf - response = HttpResponse(pdf, content_type='application/pdf') - response['Content-Disposition'] = 'inline;filename=' + form_name + '.pdf' + # return the response as a pdf + response = HttpResponse(pdf, content_type='application/pdf') + response['Content-Disposition'] = 'inline;filename=' + form_name + '.pdf' - return response + return response def __add_claimant_info(responses, claimant): diff --git a/edivorce/settings/base.py b/edivorce/settings/base.py index 38a2f36a..39d6a428 100644 --- a/edivorce/settings/base.py +++ b/edivorce/settings/base.py @@ -40,6 +40,7 @@ INSTALLED_APPS = ( 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django.contrib.humanize', 'rest_framework', 'debug_toolbar', 'edivorce.apps.core', @@ -97,14 +98,13 @@ REST_FRAMEWORK = { LANGUAGE_CODE = 'en-us' +USE_TZ = True TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True - -USE_TZ = True - +USE_THOUSANDS_SEPARATOR = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.8/howto/static-files/