From ee86c08defe33d964ee0775fc27fd92b15d34507 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Mon, 4 Jun 2018 12:11:53 +0200
Subject: [PATCH] voucher context added to document generation

---
 smash/web/models/constants.py                |   4 ++
 smash/web/models/language.py                 |   5 +-
 smash/web/models/mail_template.py            |  62 +++++++++++++++++--
 smash/web/tests/data/voucher_test.docx       | Bin 0 -> 11632 bytes
 smash/web/tests/functions.py                 |   4 +-
 smash/web/tests/models/test_mail_template.py |  26 +++++++-
 smash/web/views/mails.py                     |   6 +-
 7 files changed, 93 insertions(+), 14 deletions(-)
 create mode 100644 smash/web/tests/data/voucher_test.docx

diff --git a/smash/web/models/constants.py b/smash/web/models/constants.py
index 0ef157f4..865f10cc 100644
--- a/smash/web/models/constants.py
+++ b/smash/web/models/constants.py
@@ -48,14 +48,18 @@ KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE = "KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_
 MAIL_TEMPLATE_CONTEXT_SUBJECT = 'S'
 MAIL_TEMPLATE_CONTEXT_APPOINTMENT = 'A'
 MAIL_TEMPLATE_CONTEXT_VISIT = 'V'
+MAIL_TEMPLATE_CONTEXT_VOUCHER = 'C'
 
 MAIL_TEMPLATE_CONTEXT_CHOICES = (
     (MAIL_TEMPLATE_CONTEXT_APPOINTMENT, 'Appointment'),
     (MAIL_TEMPLATE_CONTEXT_SUBJECT, 'Subject'),
     (MAIL_TEMPLATE_CONTEXT_VISIT, 'Visit'),
+    (MAIL_TEMPLATE_CONTEXT_VOUCHER, 'Voucher'),
 )
 LOCALE_CHOICES = [(value, value) for value in sorted(locale.windows_locale.values())]
 
+DEFAULT_LOCALE_NAME = "fr_FR"
+
 MONDAY_AS_DAY_OF_WEEK = 1
 TUESDAY_AS_DAY_OF_WEEK = 2
 WEDNESDAY_AS_DAY_OF_WEEK = 3
diff --git a/smash/web/models/language.py b/smash/web/models/language.py
index 8db4cd27..5848ace6 100644
--- a/smash/web/models/language.py
+++ b/smash/web/models/language.py
@@ -2,7 +2,7 @@
 
 from django.db import models
 
-from .constants import LOCALE_CHOICES
+from .constants import LOCALE_CHOICES, DEFAULT_LOCALE_NAME
 
 
 class Language(models.Model):
@@ -13,7 +13,8 @@ class Language(models.Model):
     name = models.CharField(max_length=20)
     image = models.ImageField()
     order = models.IntegerField(default=0)
-    locale = models.CharField(max_length=10, choices=LOCALE_CHOICES, null=False, blank=False, default="fr_FR")
+    locale = models.CharField(max_length=10, choices=LOCALE_CHOICES, null=False, blank=False,
+                              default=DEFAULT_LOCALE_NAME)
     windows_locale_name = models.CharField(max_length=10, choices=LOCALE_CHOICES, null=False, blank=False,
                                            default="French")
 
diff --git a/smash/web/models/mail_template.py b/smash/web/models/mail_template.py
index d4f72d18..72901525 100644
--- a/smash/web/models/mail_template.py
+++ b/smash/web/models/mail_template.py
@@ -7,9 +7,9 @@ from contextlib import contextmanager
 from django.db import models
 
 from .constants import MAIL_TEMPLATE_CONTEXT_CHOICES, MAIL_TEMPLATE_CONTEXT_APPOINTMENT, \
-    MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VISIT
+    MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VISIT, MAIL_TEMPLATE_CONTEXT_VOUCHER, DEFAULT_LOCALE_NAME
 from ..docx_helper import process_file
-from ..models import Appointment, Visit, StudySubject, Worker
+from ..models import Appointment, Visit, StudySubject, Worker, Voucher
 
 DATE_FORMAT_FULL = "%A %d %B %Y"
 
@@ -99,9 +99,26 @@ class MailTemplate(models.Model):
         ("##A_TYPES##", "Appointment's types", "comma separated"),
     ]
 
+    MAILS_TEMPLATE_VOUCHER_TAGS = [
+        ("##C_NUMBER##", "Number", ''),
+        ("##C_PATIENT_NAME##", "Voucher Partner name", ''),
+        ("##C_VOUCHER_TYPE##", "Voucher type", ''),
+
+        ("##C_ISSUE_DATE_SHORT##", "Issue date", get_formatted_time(DATE_FORMAT_SHORT)),
+        ("##C_EXPIRY_START_SHORT##", "Expiry date", get_formatted_time(DATE_FORMAT_SHORT)),
+
+        ("##C_PARTNER_NAME##", "Voucher Partner name", ''),
+        ("##C_PARTNER_ADDRESS##", "Voucher Partner address", ''),
+        ("##C_PARTNER_CITY##", "Voucher Partner city", ''),
+        ("##C_PARTNER_POSTAL_CODE##", "Voucher Partner postal code", ''),
+        ("##C_PARTNER_COUNTRY##", "Voucher Partner country", ''),
+        ("##C_PARTNER_PHONE##", "Voucher Partner phone", ''),
+        ("##C_HOURS##", "Hours", ''),
+    ]
+
     name = models.CharField(max_length=255)
     context = models.CharField(max_length=1, choices=MAIL_TEMPLATE_CONTEXT_CHOICES)
-    language = models.ForeignKey("web.Language", on_delete=models.CASCADE)
+    language = models.ForeignKey("web.Language", on_delete=models.CASCADE, null=True)
     template_file = models.FileField(upload_to='templates/')
 
     @staticmethod
@@ -112,6 +129,10 @@ class MailTemplate(models.Model):
     def get_subject_mail_templates(languages):
         return MailTemplate.get_mail_templates_for_context(languages, MAIL_TEMPLATE_CONTEXT_SUBJECT)
 
+    @staticmethod
+    def get_voucher_mail_templates(languages):
+        return MailTemplate.get_mail_templates_for_context(languages, MAIL_TEMPLATE_CONTEXT_VOUCHER)
+
     @staticmethod
     def get_visit_mail_templates(languages):
         return MailTemplate.get_mail_templates_for_context(languages, MAIL_TEMPLATE_CONTEXT_VISIT)
@@ -134,6 +155,7 @@ class MailTemplate(models.Model):
         appointment = None
         visit = None
         study_subject = None
+        voucher = None
         if isinstance(instance, Appointment):
             appointment = instance
             visit = instance.visit
@@ -143,19 +165,29 @@ class MailTemplate(models.Model):
             study_subject = visit.subject
         elif isinstance(instance, StudySubject):
             study_subject = instance
+        elif isinstance(instance, Voucher):
+            voucher = instance
         # set locale to get correct date format
-        locale_name = self.language.locale
-        if platform.system() == 'Windows':
-            locale_name = self.language.windows_locale_name
+        locale_name = self.get_locale_name()
         with setlocale(locale_name.encode('utf8')):
             replacements = {}
             self._add_generic_replacements(replacements, Worker.get_by_user(user))
             self._add_appointment_replacements(replacements, appointment)
             self._add_visit_replacements(replacements, visit)
             self._add_subject_replacements(replacements, study_subject)
+            self._add_voucher_replacements(replacements, voucher)
             process_file(self.template_file.path, stream, replacements)
         return stream
 
+    def get_locale_name(self):
+        if self.language is None:
+            locale_name = DEFAULT_LOCALE_NAME
+        else:
+            locale_name = self.language.locale
+            if platform.system() == 'Windows':
+                locale_name = self.language.windows_locale_name
+        return locale_name
+
     def _add_generic_replacements(self, replacements, worker):
         current_datetime = datetime.datetime.now()
         replacements.update({
@@ -237,3 +269,21 @@ class MailTemplate(models.Model):
                 '##S_MAIL_LANGUAGE##': str(study_subject.subject.default_written_communication_language),
                 '##S_KNOWN_LANGUAGES##': ", ".join([l.name for l in study_subject.subject.languages.all()])
             })
+
+    def _add_voucher_replacements(self, replacements, voucher):
+        if voucher is not None:
+            replacements.update({
+                "##C_NUMBER##": voucher.number,
+                "##C_PATIENT_NAME##": voucher.study_subject.subject.first_name + ' ' + voucher.study_subject.subject.last_name,
+                "##C_VOUCHER_TYPE##": voucher.voucher_type.description,
+                "##C_ISSUE_DATE_SHORT##": voucher.issue_date.strftime(DATE_FORMAT_SHORT).decode(date_format_encoding()),
+                "##C_EXPIRY_START_SHORT##": voucher.expiry_date.strftime(DATE_FORMAT_SHORT).decode(
+                    date_format_encoding()),
+                "##C_PARTNER_NAME##": voucher.usage_partner.first_name + ' ' + voucher.usage_partner.last_name,
+                "##C_PARTNER_ADDRESS##": voucher.usage_partner.address,
+                "##C_PARTNER_POSTAL_CODE##": voucher.usage_partner.postal_code,
+                "##C_PARTNER_CITY##": voucher.usage_partner.city,
+                "##C_PARTNER_COUNTRY##": unicode(voucher.usage_partner.country),
+                "##C_PARTNER_PHONE##": voucher.usage_partner.phone_number,
+                "##C_HOURS##": str(voucher.hours),
+            })
diff --git a/smash/web/tests/data/voucher_test.docx b/smash/web/tests/data/voucher_test.docx
new file mode 100644
index 0000000000000000000000000000000000000000..8f263326ba270ab7bfe216c812631225bb1fd105
GIT binary patch
literal 11632
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
NVqVYCK8m+r{{x{imgWEe

literal 0
HcmV?d00001

diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py
index f7da4add..0164230b 100644
--- a/smash/web/tests/functions.py
+++ b/smash/web/tests/functions.py
@@ -9,7 +9,7 @@ from web.models import Location, AppointmentType, StudySubject, Worker, Visit, A
     VoucherType, VoucherTypePrice, Voucher, Room, Item, WorkerStudyRole
 from web.models.constants import REDCAP_TOKEN_CONFIGURATION_TYPE, REDCAP_BASE_URL_CONFIGURATION_TYPE, \
     SEX_CHOICES_MALE, SUBJECT_TYPE_CHOICES_CONTROL, CONTACT_TYPES_PHONE, \
-    MONDAY_AS_DAY_OF_WEEK, COUNTRY_AFGHANISTAN_ID, VOUCHER_STATUS_NEW, GLOBAL_STUDY_ID
+    MONDAY_AS_DAY_OF_WEEK, COUNTRY_AFGHANISTAN_ID, VOUCHER_STATUS_NEW, GLOBAL_STUDY_ID, DEFAULT_LOCALE_NAME
 from web.models.worker_study_role import ROLE_CHOICES_DOCTOR, WORKER_VOUCHER_PARTNER
 from web.redcap_connector import RedcapSubject
 from web.views.notifications import get_today_midnight_date
@@ -311,7 +311,7 @@ def create_room(owner='Test owner', city='Test city',
     return room
 
 
-def create_language(name="French", locale="fr_FR"):
+def create_language(name="French", locale=DEFAULT_LOCALE_NAME):
     language = Language(name=name, locale=locale)
     language.save()
     return language
diff --git a/smash/web/tests/models/test_mail_template.py b/smash/web/tests/models/test_mail_template.py
index 49c84a13..27d5dcca 100644
--- a/smash/web/tests/models/test_mail_template.py
+++ b/smash/web/tests/models/test_mail_template.py
@@ -5,11 +5,11 @@ from docx import Document
 
 from web.models import MailTemplate
 from web.models.constants import MAIL_TEMPLATE_CONTEXT_APPOINTMENT, MAIL_TEMPLATE_CONTEXT_VISIT, \
-    MAIL_TEMPLATE_CONTEXT_SUBJECT
+    MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VOUCHER
 from web.models.mail_template import DATE_FORMAT_SHORT
 from web.tests.functions import create_language, get_resource_path, create_appointment, create_user, \
     create_study_subject, \
-    create_visit
+    create_visit, create_voucher
 
 
 class MailTemplateModelTests(TestCase):
@@ -34,6 +34,11 @@ class MailTemplateModelTests(TestCase):
         function_to_test = MailTemplate.get_subject_mail_templates
         self.check_get_mail_templates(context, function_to_test)
 
+    def test_get_voucher_mail_templates(self):
+        context = MAIL_TEMPLATE_CONTEXT_VOUCHER
+        function_to_test = MailTemplate.get_voucher_mail_templates
+        self.check_get_mail_templates(context, function_to_test)
+
     def check_get_mail_templates(self, context, function_to_test):
         # create french template
         template_name_french = "test_fr"
@@ -93,6 +98,23 @@ class MailTemplateModelTests(TestCase):
         self.check_doc_contains(doc, [worker_name, str(subject), str(subject.subject.country), subject.nd_number,
                                       subject.get_type_display()])
 
+    def test_apply_voucher(self):
+        template_name_french = "test_without_language"
+        subject = create_study_subject()
+        subject_template_french = MailTemplate(name=template_name_french, language=None,
+                                               context=MAIL_TEMPLATE_CONTEXT_VOUCHER,
+                                               template_file="voucher_test.docx")
+        voucher = create_voucher(study_subject=subject)
+        stream = StringIO.StringIO()
+        subject_template_french.apply(voucher, self.user, stream)
+        doc = Document(stream)
+        worker_name = str(self.user.worker)
+
+        self.check_doc_contains(doc, [worker_name, str(subject), voucher.number, voucher.usage_partner.address,
+                                      voucher.expiry_date.strftime(DATE_FORMAT_SHORT),
+                                      voucher.issue_date.strftime(DATE_FORMAT_SHORT)
+                                      ])
+
     def test_apply_visit(self):
         template_name_french = "test_fr"
         visit = create_visit()
diff --git a/smash/web/views/mails.py b/smash/web/views/mails.py
index 1fc450b3..53b8557e 100644
--- a/smash/web/views/mails.py
+++ b/smash/web/views/mails.py
@@ -12,15 +12,16 @@ from django.views.generic import ListView
 from django.views.generic import UpdateView
 
 from . import WrappedView
-from ..models import StudySubject, Visit, Appointment, MailTemplate
+from ..models import StudySubject, Visit, Appointment, MailTemplate, Voucher
 from ..models.constants import MAIL_TEMPLATE_CONTEXT_SUBJECT, MAIL_TEMPLATE_CONTEXT_VISIT, \
-    MAIL_TEMPLATE_CONTEXT_APPOINTMENT
+    MAIL_TEMPLATE_CONTEXT_APPOINTMENT, MAIL_TEMPLATE_CONTEXT_VOUCHER
 
 MIMETYPE_DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
 
 CONTEXT_TYPES_MAPPING = {
     MAIL_TEMPLATE_CONTEXT_SUBJECT: StudySubject,
     MAIL_TEMPLATE_CONTEXT_VISIT: Visit,
+    MAIL_TEMPLATE_CONTEXT_VOUCHER: Voucher,
     MAIL_TEMPLATE_CONTEXT_APPOINTMENT: Appointment
 }
 
@@ -35,6 +36,7 @@ class MailTemplatesListView(ListView, WrappedView):
         context['explanations'] = {"generic": MailTemplate.MAILS_TEMPLATE_GENERIC_TAGS,
                                    "subject": MailTemplate.MAILS_TEMPLATE_SUBJECT_TAGS,
                                    "visit": MailTemplate.MAILS_TEMPLATE_VISIT_TAGS,
+                                   "voucher": MailTemplate.MAILS_TEMPLATE_VOUCHER_TAGS,
                                    "appointment": MailTemplate.MAILS_TEMPLATE_APPOINTMENT_TAGS,
                                    }
         return context
-- 
GitLab