From 21f01f6782ed54e304cc49e8c76d3b5709284755 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Thu, 14 Dec 2017 14:52:36 +0100
Subject: [PATCH] notification about subject with expired vouchers

---
 ...rameters_subject_voucher_expiry_visible.py | 20 ++++++++
 smash/web/models/notification_columns.py      |  5 ++
 smash/web/tests/functions.py                  | 17 +++++++
 smash/web/tests/view/test_notifications.py    | 48 +++++++++++++++++--
 smash/web/views/notifications.py              | 32 +++++++++++--
 5 files changed, 116 insertions(+), 6 deletions(-)
 create mode 100644 smash/web/migrations/0102_studynotificationparameters_subject_voucher_expiry_visible.py

diff --git a/smash/web/migrations/0102_studynotificationparameters_subject_voucher_expiry_visible.py b/smash/web/migrations/0102_studynotificationparameters_subject_voucher_expiry_visible.py
new file mode 100644
index 00000000..df7f4833
--- /dev/null
+++ b/smash/web/migrations/0102_studynotificationparameters_subject_voucher_expiry_visible.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.7 on 2017-12-14 12:55
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0101_auto_20171214_1047'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='studynotificationparameters',
+            name='subject_voucher_expiry_visible',
+            field=models.BooleanField(default=False, verbose_name=b'subject vouchers almost expired'),
+        ),
+    ]
diff --git a/smash/web/models/notification_columns.py b/smash/web/models/notification_columns.py
index aa114173..4bfeb00a 100644
--- a/smash/web/models/notification_columns.py
+++ b/smash/web/models/notification_columns.py
@@ -31,6 +31,11 @@ class StudyNotificationParameters(models.Model):
         verbose_name='subject without visit',
     )
 
+    subject_voucher_expiry_visible = models.BooleanField(
+        default=False,
+        verbose_name='subject vouchers almost expired',
+    )
+
     unfinished_visits_visible = models.BooleanField(
         default=True,
         verbose_name='unfinished visits',
diff --git a/smash/web/tests/functions.py b/smash/web/tests/functions.py
index 49888524..eb9e87dd 100644
--- a/smash/web/tests/functions.py
+++ b/smash/web/tests/functions.py
@@ -102,6 +102,23 @@ def create_empty_notification_parameters():
         subject_require_contact_visible=False,
         missing_redcap_subject_visible=False,
         inconsistent_redcap_subject_visible=False,
+        subject_voucher_expiry_visible=False,
+    )
+
+
+def create_full_notification_parameters():
+    return StudyNotificationParameters.objects.create(
+        exceeded_visits_visible=True,
+        unfinished_visits_visible=True,
+        approaching_visits_without_appointments_visible=True,
+        unfinished_appointments_visible=True,
+        visits_with_missing_appointments_visible=True,
+        subject_no_visits_visible=True,
+        approaching_visits_for_mail_contact_visible=True,
+        subject_require_contact_visible=True,
+        missing_redcap_subject_visible=True,
+        inconsistent_redcap_subject_visible=True,
+        subject_voucher_expiry_visible=True,
     )
 
 
diff --git a/smash/web/tests/view/test_notifications.py b/smash/web/tests/view/test_notifications.py
index 1be70d94..de8c5945 100644
--- a/smash/web/tests/view/test_notifications.py
+++ b/smash/web/tests/view/test_notifications.py
@@ -7,7 +7,8 @@ from web.models import Appointment, Location, AppointmentTypeLink, Study, Visit
 from web.models.constants import GLOBAL_STUDY_ID, VOUCHER_STATUS_USED
 from web.tests import LoggedInTestCase
 from web.tests.functions import create_appointment, create_location, create_worker, create_appointment_type, \
-    create_empty_notification_parameters, create_study_subject, create_visit, create_voucher
+    create_empty_notification_parameters, create_study_subject, create_visit, create_voucher, create_contact_attempt, \
+    create_full_notification_parameters
 from web.views.notifications import \
     get_approaching_visits_for_mail_contact, \
     get_approaching_visits_for_mail_contact_count, \
@@ -23,7 +24,7 @@ from web.views.notifications import \
     get_today_midnight_date, \
     get_unfinished_appointments, \
     get_unfinished_appointments_count, \
-    get_unfinished_visits, get_exceeded_visits
+    get_unfinished_visits, get_exceeded_visits, get_subject_voucher_expiry_notifications_count
 
 logger = logging.getLogger(__name__)
 
@@ -57,7 +58,6 @@ class NotificationViewTests(LoggedInTestCase):
         appointment.status = Appointment.APPOINTMENT_STATUS_FINISHED
         appointment.save()
         notification = get_exceeded_visit_notifications_count(self.user)
-        logger.debug(get_exceeded_visits(self.user).query)
         self.assertEquals(1, notification.count)
 
     def test_get_exceeded_visit_notifications_count_2(self):
@@ -127,6 +127,17 @@ class NotificationViewTests(LoggedInTestCase):
         self.assertEquals(0, result[0])
         self.assertEquals(0, len(result[1]))
 
+    def test_get_notifications_with_full_study_notification(self):
+        study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0]
+        study.notification_parameters = create_full_notification_parameters()
+        study.save()
+
+        create_worker(self.user)
+        result = get_notifications(self.user)
+
+        self.assertEquals(0, result[0])
+        self.assertTrue(len(result[1]) > 0)
+
     def test_get_visits_without_appointments_count_2(self):
         appointment_type = create_appointment_type()
         original_notification = get_visits_without_appointments_count(self.user)
@@ -493,3 +504,34 @@ class NotificationViewTests(LoggedInTestCase):
             self.fail("Exception expected")
         except TypeError:
             pass
+
+    def test_get_subjects_with_expiry_vouchers(self):
+        original_notification = get_subject_voucher_expiry_notifications_count(self.user)
+        voucher = create_voucher()
+        voucher.expiry_date = get_today_midnight_date()
+        voucher.save()
+
+        notification = get_subject_voucher_expiry_notifications_count(self.user)
+        self.assertEquals(original_notification.count + 1, notification.count)
+
+        voucher.expiry_date = get_today_midnight_date() + datetime.timedelta(days=365)
+        voucher.save()
+
+        notification = get_subject_voucher_expiry_notifications_count(self.user)
+        self.assertEquals(original_notification.count, notification.count)
+
+    def test_get_subjects_with_expiry_vouchers_and_contact_attempt(self):
+        original_notification = get_subject_voucher_expiry_notifications_count(self.user)
+        voucher = create_voucher()
+        voucher.expiry_date = get_today_midnight_date()
+        voucher.save()
+        contact_attempt = create_contact_attempt(voucher.study_subject)
+
+        notification = get_subject_voucher_expiry_notifications_count(self.user)
+        self.assertEquals(original_notification.count, notification.count)
+
+        contact_attempt.datetime_when = "2011-11-11"
+        contact_attempt.save()
+
+        notification = get_subject_voucher_expiry_notifications_count(self.user)
+        self.assertEquals(original_notification.count + 1, notification.count)
diff --git a/smash/web/views/notifications.py b/smash/web/views/notifications.py
index 5a42783a..58e305a6 100644
--- a/smash/web/views/notifications.py
+++ b/smash/web/views/notifications.py
@@ -1,13 +1,15 @@
 # coding=utf-8
 import datetime
+import logging
 
 from django.contrib.auth.models import User, AnonymousUser
-from django.db.models import Count, Case, When, Q, F
+from django.db.models import Count, Case, When, Q, F, Max
 from django.utils import timezone
 
-from web.models import Study
+from web.models import Study, Worker, StudySubject, Visit, Appointment, Location, MissingSubject, InconsistentSubject
 from web.models.constants import GLOBAL_STUDY_ID, VOUCHER_STATUS_NEW
-from ..models import Worker, StudySubject, Visit, Appointment, Location, MissingSubject, InconsistentSubject
+
+logger = logging.getLogger(__name__)
 
 
 class NotificationCount(object):
@@ -68,6 +70,15 @@ def get_subject_with_no_visit_notifications_count(user):
     return notification
 
 
+def get_subject_voucher_expiry_notifications_count(user):
+    notification = NotificationCount(
+        title="subject vouchers almost expired",
+        count=get_subjects_with_almost_expired_vouchers(user).count(),
+        style="fa fa-users text-aqua",
+        type='web.views.subject_voucher_expiry')
+    return notification
+
+
 def get_visits_without_appointments_count(user):
     notification = NotificationCount(
         title="unfinished visits",
@@ -130,6 +141,8 @@ def get_notifications(the_user):
             notifications.append(get_visits_with_missing_appointments_count(worker))
         if study.notification_parameters.subject_no_visits_visible:
             notifications.append(get_subject_with_no_visit_notifications_count(worker))
+        if study.notification_parameters.subject_voucher_expiry_visible:
+            notifications.append(get_subject_voucher_expiry_notifications_count(worker))
         if study.notification_parameters.approaching_visits_for_mail_contact_visible:
             notifications.append(get_approaching_visits_for_mail_contact_count(worker))
         if study.notification_parameters.subject_require_contact_visible:
@@ -157,6 +170,19 @@ def get_subjects_with_no_visit(user):
     return result
 
 
+def get_subjects_with_almost_expired_vouchers(user):
+    notification_min_date = get_today_midnight_date() + datetime.timedelta(days=14)
+    contact_attempt_min_date = get_today_midnight_date() - datetime.timedelta(days=14)
+    result = StudySubject.objects.filter(
+        subject__dead=False,
+        resigned=False,
+        default_location__in=get_filter_locations(user),
+    ).annotate(last_contact=Max(Case(When(contactattempt__success=True, then="contactattempt__datetime_when")))).filter(
+        Q(vouchers__status=VOUCHER_STATUS_NEW) & Q(vouchers__expiry_date__lte=notification_min_date)).filter(
+        Q(last_contact__lt=contact_attempt_min_date) | Q(last_contact__isnull=True))
+    return result
+
+
 def get_subjects_with_reminder(user):
     tomorrow = datetime.datetime.now() + datetime.timedelta(hours=1)
 
-- 
GitLab