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 0000000000000000000000000000000000000000..df7f4833a1e947d2240708c52e8229396ffc09d3 --- /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 aa1141734ad6b861dd3780bc39f923533c19c520..4bfeb00a4301fefe4b6a916cd5aedde8d765d74a 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 49888524124b9842bdf68786f110b3037775215b..eb9e87dd7ddca95144ef58a1337b67f141417dda 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 1be70d94ce1c722e4c45f68a55de6aecb2eb8605..de8c59457b9d5a26106e4dafccf4c99fc899725f 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 5a42783a6a7233b6b0fcf22b66ec0f8e083301a2..58e305a63de108a643a78647bfa5fd122dfb4de3 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)