diff --git a/smash/web/api_views/subject.py b/smash/web/api_views/subject.py index fb81d9624b40750c5f24315112d873516b2ff3ab..c31d13903ab43b26fa7db0fd1f9c570d28e5cb5e 100644 --- a/smash/web/api_views/subject.py +++ b/smash/web/api_views/subject.py @@ -70,6 +70,10 @@ def get_subject_columns(request, subject_list_type): add_column(result, "Resigned", "resigned", study_subject_columns, "yes_no_filter", study.columns) add_column(result, "Endpoint Reached", "endpoint_reached", study_subject_columns, "yes_no_filter", study.columns) add_column(result, "Postponed", "postponed", study_subject_columns, "yes_no_filter", study.columns) + add_column(result, "Next of keen", "next_of_keen_name", subject_columns, "string_filter") + add_column(result, "Next of keen phone", "next_of_keen_phone", subject_columns, "string_filter") + add_column(result, "Next of keen address", "next_of_keen_address", subject_columns, "string_filter") + add_column(result, "Brain donation agreement", "brain_donation_agreement", study_subject_columns, "yes_no_filter", study.columns) add_column(result, "Excluded", "excluded", study_subject_columns, "yes_no_filter", study.columns) add_column(result, "Info sent", "information_sent", study_subject_columns, "yes_no_filter", study.columns) add_column(result, "Type", "type", study_subject_columns, "type_filter", study.columns) @@ -113,6 +117,12 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction, co result = subjects_to_be_ordered.order_by(order_direction + 'subject__last_name') elif order_column == "address": result = subjects_to_be_ordered.order_by(order_direction + 'subject__address') + elif order_column == "next_of_keen_name": + result = subjects_to_be_ordered.order_by(order_direction + 'subject__next_of_keen_name') + elif order_column == "next_of_keen_phone": + result = subjects_to_be_ordered.order_by(order_direction + 'subject__next_of_keen_phone') + elif order_column == "next_of_keen_address": + result = subjects_to_be_ordered.order_by(order_direction + 'subject__next_of_keen_address') elif order_column == "nd_number": result = subjects_to_be_ordered.order_by(order_direction + 'nd_number') elif order_column == "referral": @@ -144,6 +154,8 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction, co result = subjects_to_be_ordered.order_by(order_direction + 'subject__social_security_number') elif order_column == "postponed": result = subjects_to_be_ordered.order_by(order_direction + 'postponed') + elif order_column == "brain_donation_agreement": + result = subjects_to_be_ordered.order_by(order_direction + 'brain_donation_agreement') elif order_column == "excluded": result = subjects_to_be_ordered.order_by(order_direction + 'excluded') elif order_column == "type": @@ -242,6 +254,12 @@ def get_subjects_filtered(subjects_to_be_filtered, filters): result = result.filter(subject__last_name__icontains=value) elif column == "address": result = result.filter(subject__address__icontains=value) + elif column == "next_of_keen_name": + result = result.filter(subject__next_of_keen_name__icontains=value) + elif column == "next_of_keen_phone": + result = result.filter(subject__next_of_keen_phone__icontains=value) + elif column == "next_of_keen_address": + result = result.filter(subject__next_of_keen_address__icontains=value) elif column == "nd_number": result = result.filter(nd_number__icontains=value) elif column == "referral": @@ -254,6 +272,8 @@ def get_subjects_filtered(subjects_to_be_filtered, filters): result = result.filter(resigned=(value == "true")) elif column == "endpoint_reached": result = result.filter(endpoint_reached=(value == "true")) + elif column == "brain_donation_agreement": + result = result.filter(brain_donation_agreement=(value == "true")) elif column == "postponed": result = result.filter(postponed=(value == "true")) elif column == "excluded": @@ -402,6 +422,9 @@ def serialize_subject(study_subject): "first_name": study_subject.subject.first_name, "last_name": study_subject.subject.last_name, "address": study_subject.subject.pretty_address(), + "next_of_keen_name": study_subject.subject.next_of_keen_name, + "next_of_keen_phone": study_subject.subject.next_of_keen_phone, + "next_of_keen_address": study_subject.subject.next_of_keen_address, "date_born": study_subject.subject.date_born, "datetime_contact_reminder": contact_reminder, "last_contact_attempt": last_contact_attempt_string, @@ -414,6 +437,7 @@ def serialize_subject(study_subject): "resigned": bool_to_yes_no(study_subject.resigned), "endpoint_reached": bool_to_yes_no(study_subject.endpoint_reached), "postponed": bool_to_yes_no(study_subject.postponed), + "brain_donation_agreement": bool_to_yes_no(study_subject.brain_donation_agreement), "excluded": bool_to_yes_no(study_subject.excluded), "information_sent": bool_to_yes_no(study_subject.information_sent), "health_partner_first_name": health_partner_first_name, diff --git a/smash/web/forms/study_subject_forms.py b/smash/web/forms/study_subject_forms.py index ea2849fc6ae8ddce7b8f5ddc62d1bb7467908b8a..2bc880780226734cfa1653501960eb47bc8138b0 100644 --- a/smash/web/forms/study_subject_forms.py +++ b/smash/web/forms/study_subject_forms.py @@ -161,6 +161,7 @@ def prepare_study_subject_fields(fields, study): prepare_field(fields, study.columns, 'nd_number') prepare_field(fields, study.columns, 'datetime_contact_reminder') prepare_field(fields, study.columns, 'postponed') + prepare_field(fields, study.columns, 'brain_donation_agreement') prepare_field(fields, study.columns, 'flying_team') prepare_field(fields, study.columns, 'mpower_id') prepare_field(fields, study.columns, 'comments') diff --git a/smash/web/migrations/0148_auto_20200319_1301.py b/smash/web/migrations/0148_auto_20200319_1301.py new file mode 100644 index 0000000000000000000000000000000000000000..1b2abecbe955819c77d988944336b22ad28bd016 --- /dev/null +++ b/smash/web/migrations/0148_auto_20200319_1301.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2020-03-19 13:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('web', '0147_auto_20200320_0931'), + ] + + operations = [ + migrations.AlterModelOptions( + name='appointmenttypelink', + options={'permissions': [('view_daily_planning', 'Can see daily planning')]}, + ), + migrations.AlterField( + model_name='appointmenttype', + name='calendar_font_color', + field=models.CharField(default=b'#00000', max_length=2000, verbose_name=b'Calendar font color'), + ), + ] diff --git a/smash/web/migrations/0149_auto_20200319_1415.py b/smash/web/migrations/0149_auto_20200319_1415.py new file mode 100644 index 0000000000000000000000000000000000000000..c948e9780d3d5da309c62035ff5afb27f0d835fc --- /dev/null +++ b/smash/web/migrations/0149_auto_20200319_1415.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2020-03-19 14:15 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0148_auto_20200319_1301'), + ] + + operations = [ + migrations.AlterModelOptions( + name='appointment', + options={'permissions': [('send_sample_mail_for_appointments', 'Can send sample collection list')]}, + ), + ] diff --git a/smash/web/migrations/0150_auto_20200319_1446.py b/smash/web/migrations/0150_auto_20200319_1446.py new file mode 100644 index 0000000000000000000000000000000000000000..0fcdea7e40cf414c94558b82d7befe42e7dbc275 --- /dev/null +++ b/smash/web/migrations/0150_auto_20200319_1446.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2020-03-19 14:46 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0149_auto_20200319_1415'), + ] + + operations = [ + migrations.AlterModelOptions( + name='appointment', + options={'permissions': [('send_sample_mail_for_appointments', 'Can send sample collection list'), ('view_statistics', 'Can see statistics')]}, + ), + ] diff --git a/smash/web/migrations/0151_auto_20200319_1518.py b/smash/web/migrations/0151_auto_20200319_1518.py new file mode 100644 index 0000000000000000000000000000000000000000..a034d7b50283e5a6011cbcf3e9bf24a465c0cfcb --- /dev/null +++ b/smash/web/migrations/0151_auto_20200319_1518.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2020-03-19 15:18 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0150_auto_20200319_1446'), + ] + + operations = [ + migrations.AlterModelOptions( + name='subject', + options={'permissions': [('send_sample_mail_for_appointments', 'Can send sample collection list'), ('export_subjects', 'Can export subject data to excel/csv')]}, + ), + ] diff --git a/smash/web/migrations/0152_add_permissions_to_existing_workers.py b/smash/web/migrations/0152_add_permissions_to_existing_workers.py new file mode 100644 index 0000000000000000000000000000000000000000..67752e4690b59643da8f7ae2fe375db39a43f2a2 --- /dev/null +++ b/smash/web/migrations/0152_add_permissions_to_existing_workers.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2020-03-19 13:01 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ('web', '0151_auto_20200319_1518'), + ] + + operations = [ + migrations.RunSQL("insert into web_workerstudyrole_permissions(workerstudyrole_id, permission_id) " + "select web_workerstudyrole.id, auth_permission.id from web_workerstudyrole,auth_permission " + "where codename='view_daily_planning';"), + migrations.RunSQL("insert into web_workerstudyrole_permissions(workerstudyrole_id, permission_id) " + "select web_workerstudyrole.id, auth_permission.id from web_workerstudyrole,auth_permission " + "where codename='change_flyingteam';"), + migrations.RunSQL("insert into web_workerstudyrole_permissions(workerstudyrole_id, permission_id) " + "select web_workerstudyrole.id, auth_permission.id from web_workerstudyrole,auth_permission " + "where codename='export_subjects';"), + + ] diff --git a/smash/web/migrations/0153_auto_20200320_0932.py b/smash/web/migrations/0153_auto_20200320_0932.py new file mode 100644 index 0000000000000000000000000000000000000000..06a7de106240cf856fceb9720477be0b9aa87526 --- /dev/null +++ b/smash/web/migrations/0153_auto_20200320_0932.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2020-03-20 09:32 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0152_add_permissions_to_existing_workers'), + ] + + operations = [ + migrations.AddField( + model_name='studycolumns', + name='brain_donation_agreement', + field=models.BooleanField(default=False, verbose_name=b'Brain donation agreement'), + ), + migrations.AddField( + model_name='studysubject', + name='brain_donation_agreement', + field=models.BooleanField(default=False, verbose_name=b'Brain donation agreement'), + ), + migrations.AddField( + model_name='subject', + name='next_of_keen_address', + field=models.TextField(blank=True, max_length=2000, verbose_name=b'Next of keen address'), + ), + migrations.AddField( + model_name='subject', + name='next_of_keen_name', + field=models.CharField(blank=True, max_length=50, verbose_name=b'Next of keen'), + ), + migrations.AddField( + model_name='subject', + name='next_of_keen_phone', + field=models.CharField(blank=True, max_length=50, verbose_name=b'Next of keen phone'), + ), + migrations.AddField( + model_name='subjectcolumns', + name='next_of_keen_address', + field=models.BooleanField(default=False, max_length=1, verbose_name=b'Next of keen address'), + ), + migrations.AddField( + model_name='subjectcolumns', + name='next_of_keen_name', + field=models.BooleanField(default=False, max_length=1, verbose_name=b'Next of keen'), + ), + migrations.AddField( + model_name='subjectcolumns', + name='next_of_keen_phone', + field=models.BooleanField(default=False, max_length=1, verbose_name=b'Next of keen phone'), + ), + ] diff --git a/smash/web/models/appointment.py b/smash/web/models/appointment.py index 9b5b3944c623d47a8e090bccab83fbfed6eb245b..c7a52d416f21c313e7930088f85944c91f35cd50 100644 --- a/smash/web/models/appointment.py +++ b/smash/web/models/appointment.py @@ -11,6 +11,10 @@ from . import ConfigurationItem class Appointment(models.Model): class Meta: app_label = 'web' + permissions = [ + ("send_sample_mail_for_appointments", "Can send sample collection list"), + ("view_statistics", "Can see statistics"), + ] APPOINTMENT_STATUS_SCHEDULED = 'SCHEDULED' APPOINTMENT_STATUS_FINISHED = 'FINISHED' diff --git a/smash/web/models/appointment_type_link.py b/smash/web/models/appointment_type_link.py index a48a55026880f9437ee66c390a19645dfe3a5671..c2de6050854dc57820b72b3d53b3f945f3d08aaf 100644 --- a/smash/web/models/appointment_type_link.py +++ b/smash/web/models/appointment_type_link.py @@ -2,6 +2,10 @@ from django.db import models class AppointmentTypeLink(models.Model): + class Meta: + permissions = [ + ("view_daily_planning", "Can see daily planning"), + ] appointment = models.ForeignKey("web.Appointment", on_delete=models.CASCADE) appointment_type = models.ForeignKey("web.AppointmentType", on_delete=models.CASCADE) date_when = models.DateTimeField(null=True, default=None) diff --git a/smash/web/models/study_columns.py b/smash/web/models/study_columns.py index a1b345700db1db01741b050496d1e609524c30da..7d20b3913d40a71f72fc0b70327a20084e83a158 100644 --- a/smash/web/models/study_columns.py +++ b/smash/web/models/study_columns.py @@ -1,8 +1,6 @@ # coding=utf-8 from django.db import models -from web.models.constants import BOOL_CHOICES - class StudyColumns(models.Model): class Meta: @@ -81,7 +79,7 @@ class StudyColumns(models.Model): excluded = models.BooleanField(default=False, verbose_name='Excluded') endpoint_reached = models.BooleanField(default=True, verbose_name='Endpoint reached') - + resign_reason = models.BooleanField(default=True, verbose_name='Endpoint reached comments') referral_letter = models.BooleanField( @@ -117,4 +115,9 @@ class StudyColumns(models.Model): vouchers = models.BooleanField( default=False, verbose_name='Vouchers', - ) \ No newline at end of file + ) + + brain_donation_agreement = models.BooleanField( + default=False, + verbose_name='Brain donation agreement', + ) diff --git a/smash/web/models/study_subject.py b/smash/web/models/study_subject.py index a4d217784f01a67e34d2698777f743f5a2e06548..f08d450d3b0b516c9de4c943b3347eddc610aad5 100644 --- a/smash/web/models/study_subject.py +++ b/smash/web/models/study_subject.py @@ -164,6 +164,11 @@ class StudySubject(models.Model): verbose_name='PD in family', default=None, ) + brain_donation_agreement = models.BooleanField( + default=False, + verbose_name='Brain donation agreement', + ) + resigned = models.BooleanField( verbose_name='Resigned', default=False, diff --git a/smash/web/models/subject.py b/smash/web/models/subject.py index 40c390932411e2f7a4c5bcf622927e5a5935631c..bc70691410f4d7459a0ea7b08336abd8a506d7a0 100644 --- a/smash/web/models/subject.py +++ b/smash/web/models/subject.py @@ -1,19 +1,24 @@ # coding=utf-8 import logging + from django.db import models +from django.db.models.signals import post_save +from django.dispatch import receiver from constants import SEX_CHOICES, COUNTRY_OTHER_ID from web.models import Country, Visit, Appointment, Provenance from . import Language -from django.db.models.signals import post_save -from django.dispatch import receiver logger = logging.getLogger(__name__) -class Subject(models.Model): +class Subject(models.Model): class Meta: app_label = 'web' + permissions = [ + ("send_sample_mail_for_appointments", "Can send sample collection list"), + ("export_subjects", "Can export subject data to excel/csv"), + ] sex = models.CharField(max_length=1, choices=SEX_CHOICES, @@ -96,6 +101,21 @@ class Subject(models.Model): verbose_name='Country' ) + next_of_keen_name = models.CharField(max_length=50, + blank=True, + verbose_name='Next of keen' + ) + + next_of_keen_phone = models.CharField(max_length=50, + blank=True, + verbose_name='Next of keen phone' + ) + + next_of_keen_address = models.TextField(max_length=2000, + blank=True, + verbose_name='Next of keen address' + ) + dead = models.BooleanField( verbose_name='Deceased', default=False, @@ -103,7 +123,7 @@ class Subject(models.Model): ) def pretty_address(self): - return u'{} ({}), {}. {}'.format(self.address, self.postal_code, self.city, self.country) + return u'{} ({}), {}. {}'.format(self.address, self.postal_code, self.city, self.country) def mark_as_dead(self): self.dead = True @@ -130,7 +150,7 @@ class Subject(models.Model): return "%s %s" % (self.first_name, self.last_name) -#SIGNALS +# SIGNALS @receiver(post_save, sender=Subject) def set_as_deceased(sender, instance, **kwargs): if instance.dead: diff --git a/smash/web/models/subject_columns.py b/smash/web/models/subject_columns.py index d83847c7d6589aa98adbcd1740137fcb1a80526b..04a1869b0787f66eb61e8c76bd858419db16798f 100644 --- a/smash/web/models/subject_columns.py +++ b/smash/web/models/subject_columns.py @@ -83,3 +83,18 @@ class SubjectColumns(models.Model): default=True, verbose_name='Deceased', ) + + next_of_keen_name = models.BooleanField(max_length=1, + default=False, + verbose_name='Next of keen', + ) + + next_of_keen_phone = models.BooleanField(max_length=1, + default=False, + verbose_name='Next of keen phone', + ) + + next_of_keen_address = models.BooleanField(max_length=1, + default=False, + verbose_name='Next of keen address', + ) diff --git a/smash/web/templates/sidebar.html b/smash/web/templates/sidebar.html index a91be3d75937d828cecab8216c779c3b557271a7..a575319957dc7bab0a8d2cf03b4204c9796d53b3 100644 --- a/smash/web/templates/sidebar.html +++ b/smash/web/templates/sidebar.html @@ -16,12 +16,14 @@ </a> </li> - <li data-desc="daily_planning"> - <a href="{% url 'web.views.daily_planning' %}"> - <i class="fa fa-clock-o"></i> - <span>Daily Planning</span> - </a> - </li> + {% if "view_daily_planning" in permissions %} + <li data-desc="daily_planning"> + <a href="{% url 'web.views.daily_planning' %}"> + <i class="fa fa-clock-o"></i> + <span>Daily Planning</span> + </a> + </li> + {% endif %} {% if "change_worker" in permissions %} <li data-desc="workers"> @@ -32,44 +34,63 @@ </li> {% endif %} - <li data-desc="equipment_and_rooms" class="treeview"> - <a href="{% url 'web.views.equipment_and_rooms' %}"> - <i class="fa fa-building-o"></i> <span>Equipment & Rooms</span> - <span class="pull-right-container"> + {% if equipment_perms %} + <li data-desc="equipment_and_rooms" class="treeview"> + <a href="{% url 'web.views.equipment_and_rooms' %}"> + <i class="fa fa-building-o"></i> <span>Equipment & Rooms</span> + <span class="pull-right-container"> <i class="fa fa-angle-left pull-right"></i> </span> - </a> - <ul class="treeview-menu"> - <li data-desc="equipment_items"><a href="{% url 'web.views.equipment' %}">Equipment items</a></li> - {% if "change_appointmenttype" in permissions %} - <li data-desc="appointment_types"><a href="{% url 'web.views.appointment_types' %}">Appointment Types</a></li> - {% endif %} - <li data-desc="flying_teams"><a href="{% url 'web.views.equipment_and_rooms.flying_teams' %}">Flying teams</a></li> - <li data-desc="kit_requests"><a href="{% url 'web.views.kit_requests' %}">Kit requests</a></li> - <li data-desc="rooms"><a href="{% url 'web.views.equipment_and_rooms.rooms' %}">Rooms</a></li> - </ul> - </li> + </a> + <ul class="treeview-menu"> + {% if "change_item" in permissions %} + <li data-desc="equipment_items"><a href="{% url 'web.views.equipment' %}">Equipment items</a></li> + {% endif %} + {% if "change_appointmenttype" in permissions %} + <li data-desc="appointment_types"><a href="{% url 'web.views.appointment_types' %}">Appointment + Types</a></li> + {% endif %} + {% if "change_flyingteam" in permissions %} + <li data-desc="flying_teams"><a href="{% url 'web.views.equipment_and_rooms.flying_teams' %}">Flying + teams</a></li> + {% endif %} + {% if "send_sample_mail_for_appointments" in permissions %} + <li data-desc="kit_requests"><a href="{% url 'web.views.kit_requests' %}">Kit requests</a></li> + {% endif %} + {% if "change_room" in permissions %} + <li data-desc="rooms"><a href="{% url 'web.views.equipment_and_rooms.rooms' %}">Rooms</a></li> + {% endif %} + </ul> + </li> + {% endif %} - <li data-desc="statistics"> - <a href="{% url 'web.views.statistics' %}"> - <i class="fa fa-bar-chart" aria-hidden="true"></i> - <span>Statistics</span> - </a> - </li> + {% if "view_statistics" in permissions %} + <li data-desc="statistics"> + <a href="{% url 'web.views.statistics' %}"> + <i class="fa fa-bar-chart" aria-hidden="true"></i> + <span>Statistics</span> + </a> + </li> + {% endif %} - <li data-desc="mail_templates"> - <a href="{% url 'web.views.mail_templates' %}"> - <i class="fa fa-envelope-o"></i> - <span>Mail templates</span> - </a> - </li> - <li data-desc="export"> - <a href="{% url 'web.views.export' %}"> - <i class="fa fa-file-excel-o"></i> - <span>Export</span> - </a> - </li> + {% if "change_mailtemplate" in permissions %} + <li data-desc="mail_templates"> + <a href="{% url 'web.views.mail_templates' %}"> + <i class="fa fa-envelope-o"></i> + <span>Mail templates</span> + </a> + </li> + {% endif %} + + {% if "export_subjects" in permissions %} + <li data-desc="export"> + <a href="{% url 'web.views.export' %}"> + <i class="fa fa-file-excel-o"></i> + <span>Export</span> + </a> + </li> + {% endif %} {% if study.has_vouchers and "change_voucher" in permissions%} <li data-desc="vouchers"> diff --git a/smash/web/templates/subjects/edit.html b/smash/web/templates/subjects/edit.html index a8fe444044085df8d4bb26c9c8203b8e2bc848c1..4fbc1ca90b4063e780d625b930c047c6f4792aef 100644 --- a/smash/web/templates/subjects/edit.html +++ b/smash/web/templates/subjects/edit.html @@ -204,6 +204,11 @@ $("#confirm-dead-resigned-mark-dialog").modal("show"); return false; } + var brainDonation = $("#id_study_subject-brain_donation_agreement").is(":checked"); + if (brainDonation && ($("#id_subject-next_of_keen_phone").val() === '' || $("#id_subject-next_of_keen_address").val() === '' || $("#id_subject-next_of_keen_name").val() === '')) { + alert("Next of keen data must be entered when brain donation agreement is in place"); + return false; + } }); $("#confirm-save").click(function () { confirmed = true; diff --git a/smash/web/tests/view/test_daily_planning.py b/smash/web/tests/view/test_daily_planning.py new file mode 100644 index 0000000000000000000000000000000000000000..096cc9200a770d92d5665aa33ff5cb94ac08635e --- /dev/null +++ b/smash/web/tests/view/test_daily_planning.py @@ -0,0 +1,20 @@ +import logging + +from django.urls import reverse + +from web.tests import LoggedInTestCase + +logger = logging.getLogger(__name__) + + +class DailyPlanningViewTests(LoggedInTestCase): + def test_visit_details_request(self): + self.login_as_admin() + response = self.client.get(reverse('web.views.daily_planning')) + + self.assertEqual(response.status_code, 200) + + def test_visit_details_request_without_permissions(self): + self.login_as_staff() + response = self.client.get(reverse('web.views.daily_planning')) + self.assertEqual(response.status_code, 302) diff --git a/smash/web/tests/view/test_equipments.py b/smash/web/tests/view/test_equipments.py index 99e312a8c85d747a66ca3a90396f1c9ece48b9f5..34cba24a89c5595b000dbec28d2f36e967c8f223 100644 --- a/smash/web/tests/view/test_equipments.py +++ b/smash/web/tests/view/test_equipments.py @@ -10,7 +10,14 @@ logger = logging.getLogger(__name__) class EquipmentTests(LoggedInTestCase): + + def test_list_without_permissions(self): + self.login_as_staff() + response = self.client.get(reverse('web.views.equipment')) + self.assertEqual(response.status_code, 302) + def test_equipment_requests(self): + self.login_as_admin() pages = [ 'web.views.equipment', 'web.views.equipment_add', @@ -21,6 +28,7 @@ class EquipmentTests(LoggedInTestCase): self.assertEqual(response.status_code, 200) def test_equipment_edit_request(self): + self.login_as_admin() item = create_item() page = reverse('web.views.equipment_edit', kwargs={'equipment_id': str(item.id)}) @@ -28,6 +36,7 @@ class EquipmentTests(LoggedInTestCase): self.assertEqual(response.status_code, 200) def test_equipment_delete_request(self): + self.login_as_admin() item = create_item() page = reverse('web.views.equipment_delete', kwargs={'equipment_id': str(item.id)}) @@ -35,6 +44,7 @@ class EquipmentTests(LoggedInTestCase): self.assertEqual(response.status_code, 302) def test_equipment_add(self): + self.login_as_admin() page = reverse('web.views.equipment_add') data = { 'name': 'The mysterious potion', @@ -48,6 +58,7 @@ class EquipmentTests(LoggedInTestCase): self.assertEqual(len(freshly_created), 1) def test_equipment_edit(self): + self.login_as_admin() item = create_item() page = reverse('web.views.equipment_edit', kwargs={'equipment_id': str(item.id)}) @@ -64,6 +75,7 @@ class EquipmentTests(LoggedInTestCase): self.assertEqual(getattr(freshly_edited, key, ''), data[key]) def test_equipment_delete(self): + self.login_as_admin() item = create_item() page = reverse('web.views.equipment_delete', kwargs={'equipment_id': str(item.id)}) diff --git a/smash/web/tests/view/test_export.py b/smash/web/tests/view/test_export.py index d6938476f7fd4ea6e3d3b36f2441e448fb02bfad..3f6a5c99b7fc12060eb0aad8ca95a92460d424de 100644 --- a/smash/web/tests/view/test_export.py +++ b/smash/web/tests/view/test_export.py @@ -9,26 +9,47 @@ from web.views.export import subject_to_row_for_fields, DROP_OUT_FIELD class TestExportView(LoggedInTestCase): def test_export_subjects_to_csv(self): + self.login_as_admin() create_study_subject() response = self.client.get(reverse('web.views.export_to_csv', kwargs={'data_type': "subjects"})) self.assertEqual(response.status_code, 200) + def test_export_subjects_to_csv_without_permission(self): + response = self.client.get(reverse("web.views.mail_templates")) + create_study_subject() + response = self.client.get(reverse('web.views.export_to_csv', kwargs={'data_type': "subjects"})) + self.assertEqual(response.status_code, 302) + def test_render_export(self): + self.login_as_admin() create_study_subject() response = self.client.get(reverse('web.views.export')) self.assertEqual(response.status_code, 200) + def test_render_export_without_permission(self): + create_study_subject() + response = self.client.get(reverse('web.views.export')) + self.assertEqual(response.status_code, 302) + def test_export_appointments_to_csv(self): + self.login_as_admin() create_appointment() response = self.client.get(reverse('web.views.export_to_csv', kwargs={'data_type': "appointments"})) self.assertEqual(response.status_code, 200) def test_export_subjects_to_excel(self): + self.login_as_admin() create_study_subject() response = self.client.get(reverse('web.views.export_to_excel', kwargs={'data_type': "subjects"})) self.assertEqual(response.status_code, 200) + def test_export_subjects_to_excel_without_permission(self): + create_study_subject() + response = self.client.get(reverse('web.views.export_to_excel', kwargs={'data_type': "subjects"})) + self.assertEqual(response.status_code, 302) + def test_export_appointments_to_excel(self): + self.login_as_admin() appointment = create_appointment() appointment.visit = None appointment.save() diff --git a/smash/web/tests/view/test_flying_teams.py b/smash/web/tests/view/test_flying_teams.py index 32cfe22ff9d0ac7cbd073f66dd3691ff051a06b9..e282c3e9cc14987d9c4a4a7ca5b90ca26af1c907 100644 --- a/smash/web/tests/view/test_flying_teams.py +++ b/smash/web/tests/view/test_flying_teams.py @@ -17,6 +17,7 @@ class FlyingTeamTests(LoggedInTestCase): return 'Random' + ''.join(random.choice(letters) for x in range(15)) def test_flying_team_requests(self): + self.login_as_admin() pages = [ 'web.views.equipment_and_rooms.flying_teams', 'web.views.equipment_and_rooms.flying_teams_add', @@ -26,7 +27,18 @@ class FlyingTeamTests(LoggedInTestCase): response = self.client.get(reverse(page)) self.assertEqual(response.status_code, 200) + def test_flying_team_requests_without_permission(self): + pages = [ + 'web.views.equipment_and_rooms.flying_teams', + 'web.views.equipment_and_rooms.flying_teams_add', + ] + + for page in pages: + response = self.client.get(reverse(page)) + self.assertEqual(response.status_code, 302) + def test_flying_team_add(self): + self.login_as_admin() page = reverse('web.views.equipment_and_rooms.flying_teams_add') data = { 'place': self.generate_more_or_less_random_name() @@ -38,6 +50,7 @@ class FlyingTeamTests(LoggedInTestCase): self.assertEqual(len(freshly_created), 1) def test_flying_team_edit(self): + self.login_as_admin() flying_team = create_flying_team() page = reverse('web.views.equipment_and_rooms.flying_teams_edit', kwargs={'flying_team_id': str(flying_team.id)}) @@ -51,6 +64,7 @@ class FlyingTeamTests(LoggedInTestCase): self.assertEqual(freshly_edited.place, data["place"]) def test_flying_team_edit_request(self): + self.login_as_admin() flying_team = create_flying_team() page = reverse('web.views.equipment_and_rooms.flying_teams_edit', kwargs={'flying_team_id': str(flying_team.id)}) diff --git a/smash/web/tests/view/test_kit_request.py b/smash/web/tests/view/test_kit_request.py index 387001f3bd804c87fef7db3dccac4de4897dd97f..72222f5df0f4de62743ed4d3edbf388f8232b008 100644 --- a/smash/web/tests/view/test_kit_request.py +++ b/smash/web/tests/view/test_kit_request.py @@ -5,7 +5,8 @@ from django.urls import reverse from web.models import Item, Appointment, AppointmentTypeLink from web.tests import LoggedInTestCase -from web.tests.functions import create_appointment_type, create_appointment, create_visit, create_appointment_without_visit +from web.tests.functions import create_appointment_type, create_appointment, create_visit, \ + create_appointment_without_visit from web.views.kit import get_kit_requests from web.views.notifications import get_today_midnight_date @@ -13,10 +14,16 @@ from web.views.notifications import get_today_midnight_date class ViewFunctionsTests(LoggedInTestCase): def test_kit_requests(self): + self.login_as_admin() response = self.client.get(reverse('web.views.kit_requests')) self.assertEqual(response.status_code, 200) + def test_kit_requests_without_permission(self): + response = self.client.get(reverse('web.views.kit_requests')) + self.assertEqual(response.status_code, 302) + def test_kit_requests_2(self): + self.login_as_admin() item_name = "Test item to be ordered" item = Item.objects.create(disposable=True, name=item_name) appointment_type = create_appointment_type() @@ -35,6 +42,7 @@ class ViewFunctionsTests(LoggedInTestCase): self.assertTrue(item_name in response.content) def test_kit_requests_4(self): + self.login_as_admin() item_name = "Test item to be ordered" item = Item.objects.create(disposable=True, name=item_name) appointment_type = create_appointment_type() @@ -54,6 +62,7 @@ class ViewFunctionsTests(LoggedInTestCase): self.assertFalse(item_name in response.content) def test_kit_requests_3(self): + self.login_as_admin() item_name = "Test item to be ordered" item = Item.objects.create(disposable=True, name=item_name) appointment_type = create_appointment_type() @@ -72,6 +81,7 @@ class ViewFunctionsTests(LoggedInTestCase): self.assertTrue(item_name in response.content) def test_kit_requests_order(self): + self.login_as_admin() item_name = "Test item to be ordered" item = Item.objects.create(disposable=True, name=item_name) appointment_type = create_appointment_type() @@ -104,6 +114,7 @@ class ViewFunctionsTests(LoggedInTestCase): self.assertEqual(appointment2, result['appointments'][2]) def test_kit_requests_for_appointment_with_two_types(self): + self.login_as_admin() item = Item.objects.create(disposable=True, name="item 1") appointment_type = create_appointment_type() appointment_type.required_equipment.add(item) @@ -129,6 +140,7 @@ class ViewFunctionsTests(LoggedInTestCase): self.assertEqual(1, len(result["appointments"])) def test_kit_requests_send_email(self): + self.login_as_admin() item_name = "Test item to be ordered" item = Item.objects.create(disposable=True, name=item_name) appointment_type = create_appointment_type() @@ -150,6 +162,7 @@ class ViewFunctionsTests(LoggedInTestCase): self.assertEqual(1, len(mail.outbox)) def test_kit_request_send_mail_with_general_appointment(self): + self.login_as_admin() item_name = "Test item to be ordered" item = Item.objects.create(disposable=True, name=item_name) appointment_type = create_appointment_type() diff --git a/smash/web/tests/view/test_mail.py b/smash/web/tests/view/test_mail.py index 900c84a7bcadf38a37c420308d638eb4f2c74d14..1f11340a796aedc4e9c0eebf7662ddd50d3cce33 100644 --- a/smash/web/tests/view/test_mail.py +++ b/smash/web/tests/view/test_mail.py @@ -4,8 +4,8 @@ from django.urls import reverse from web.models import MailTemplate from web.models.constants import MAIL_TEMPLATE_CONTEXT_VOUCHER -from web.tests.functions import create_voucher, get_resource_path from web.tests import LoggedInTestCase +from web.tests.functions import create_voucher, get_resource_path logger = logging.getLogger(__name__) @@ -20,3 +20,12 @@ class MailTests(LoggedInTestCase): page = reverse('web.views.mail_template_generate_for_vouchers') + "?voucher_id=" + str(voucher.id) response = self.client.get(page) self.assertEqual(response.status_code, 200) + + def test_list_mail_templates(self): + self.login_as_admin() + response = self.client.get(reverse("web.views.mail_templates")) + self.assertEqual(response.status_code, 200) + + def test_list_mail_templates_without_permission(self): + response = self.client.get(reverse("web.views.mail_templates")) + self.assertEqual(response.status_code, 302) diff --git a/smash/web/tests/view/test_rooms.py b/smash/web/tests/view/test_rooms.py index ecf1161677c0a104d9861bd356c2c96416b81bf0..b607e8105b025ac6e5492ff5b53f6ec0e9aba7ea 100644 --- a/smash/web/tests/view/test_rooms.py +++ b/smash/web/tests/view/test_rooms.py @@ -2,15 +2,16 @@ import logging from django.urls import reverse -from web.tests.functions import create_room, create_item -from web.models import Item, Room +from web.models import Room from web.tests import LoggedInTestCase +from web.tests.functions import create_room, create_item logger = logging.getLogger(__name__) class RoomsTests(LoggedInTestCase): def test_rooms_requests(self): + self.login_as_admin() pages = [ 'web.views.equipment_and_rooms.rooms', 'web.views.equipment_and_rooms.rooms_add', @@ -20,7 +21,18 @@ class RoomsTests(LoggedInTestCase): response = self.client.get(reverse(page)) self.assertEqual(response.status_code, 200) + def test_rooms_requests_without_permission(self): + pages = [ + 'web.views.equipment_and_rooms.rooms', + 'web.views.equipment_and_rooms.rooms_add', + ] + + for page in pages: + response = self.client.get(reverse(page)) + self.assertEqual(response.status_code, 302) + def test_rooms_edit_request(self): + self.login_as_admin() room = create_room() page = reverse('web.views.equipment_and_rooms.rooms_edit', kwargs={'room_id': str(room.id)}) @@ -28,6 +40,7 @@ class RoomsTests(LoggedInTestCase): self.assertEqual(response.status_code, 200) def test_rooms_delete_request(self): + self.login_as_admin() room = create_room() page = reverse('web.views.equipment_and_rooms.rooms_delete', kwargs={'room_id': str(room.id)}) @@ -35,6 +48,7 @@ class RoomsTests(LoggedInTestCase): self.assertEqual(response.status_code, 302) def test_rooms_add(self): + self.login_as_admin() page = reverse('web.views.equipment_and_rooms.rooms_add') item = create_item() data = { @@ -53,6 +67,7 @@ class RoomsTests(LoggedInTestCase): self.assertEqual(len(freshly_created), 1) def test_rooms_edit(self): + self.login_as_admin() room = create_room() page = reverse('web.views.equipment_and_rooms.rooms_edit', kwargs={'room_id': str(room.id)}) @@ -72,6 +87,7 @@ class RoomsTests(LoggedInTestCase): self.assertEqual(getattr(freshly_edited, key, ''), data[key]) def test_rooms_delete(self): + self.login_as_admin() room = create_room() page = reverse('web.views.equipment_and_rooms.rooms_delete', kwargs={'room_id': str(room.id)}) diff --git a/smash/web/tests/view/test_statistics.py b/smash/web/tests/view/test_statistics.py index 737371076825bc63c5cbbe510da8a156ae5d17a5..aa7fee20b0dcffbe402185e823e885a598ec0850 100644 --- a/smash/web/tests/view/test_statistics.py +++ b/smash/web/tests/view/test_statistics.py @@ -10,6 +10,7 @@ __author__ = 'Valentin Grouès' class TestStatisticsView(LoggedInTestCase): def test_statistics_request(self): + self.login_as_admin() url = reverse('web.views.statistics') response = self.client.get(url) self.assertEqual(response.status_code, 200) @@ -19,3 +20,8 @@ class TestStatisticsView(LoggedInTestCase): response = self.client.get(url, {"month": 10, "year": 2017, "subject_type": -1, "visit": -1}) content = response.content self.assertIn('<option value="10" selected>October', content) + + def test_statistics_request_without_permission(self): + url = reverse('web.views.statistics') + response = self.client.get(url) + self.assertEqual(response.status_code, 302) diff --git a/smash/web/views/__init__.py b/smash/web/views/__init__.py index 04dd18e6f48f979fb473a0626544776129c86777..66c3b6339e4afecdf3332e273a6f03d4e1c43705 100644 --- a/smash/web/views/__init__.py +++ b/smash/web/views/__init__.py @@ -52,7 +52,7 @@ def extend_context(params, request): else: #use full name if available, username otherwise if len(request.user.get_full_name()) > 1: - person = request.user.get_full_name() + person = request.user.get_full_name() else: person = request.user.get_username() role = '<No worker information>' @@ -61,6 +61,7 @@ def extend_context(params, request): final_params.update({ 'permissions' : permissions, 'conf_perms' : permissions & PermissionDecorator.codename_groups['configuration'], + 'equipment_perms' : permissions & PermissionDecorator.codename_groups['equipment'], 'person': person, 'role': role, 'notifications': notifications, diff --git a/smash/web/views/appointment_type.py b/smash/web/views/appointment_type.py index 9aa924d1af11b85b9aa54d6a0594866325898277..2637c26ade7f9f8809b8a7ca031368f29142b579 100644 --- a/smash/web/views/appointment_type.py +++ b/smash/web/views/appointment_type.py @@ -11,7 +11,7 @@ class AppointmentTypeListView(ListView, WrappedView): template_name = 'appointment_types/index.html' context_object_name = "appointment_types" - @PermissionDecorator('change_appointmenttype', 'configuration') + @PermissionDecorator('change_appointmenttype', 'equipment') def dispatch(self, *args, **kwargs): return super(AppointmentTypeListView, self).dispatch(*args, **kwargs) @@ -22,7 +22,7 @@ class AppointmentTypeCreateView(CreateView, WrappedView): success_url = reverse_lazy('web.views.appointment_types') success_message = "Appointment type created" - @PermissionDecorator('change_appointmenttype', 'configuration') + @PermissionDecorator('change_appointmenttype', 'equipment') def dispatch(self, *args, **kwargs): return super(AppointmentTypeCreateView, self).dispatch(*args, **kwargs) @@ -34,7 +34,7 @@ class AppointmentTypeEditView(UpdateView, WrappedView): template_name = "appointment_types/edit.html" context_object_name = "appointment_types" - @PermissionDecorator('change_appointmenttype', 'configuration') + @PermissionDecorator('change_appointmenttype', 'equipment') def dispatch(self, *args, **kwargs): return super(AppointmentTypeEditView, self).dispatch(*args, **kwargs) @@ -47,6 +47,6 @@ class AppointmentTypeDeleteView(DeleteView, WrappedView): messages.success(request, "Appointment Type deleted") return super(AppointmentTypeDeleteView, self).delete(request, *args, **kwargs) - @PermissionDecorator('change_appointmenttype', 'configuration') + @PermissionDecorator('change_appointmenttype', 'equipment') def dispatch(self, *args, **kwargs): return super(AppointmentTypeDeleteView, self).dispatch(*args, **kwargs) \ No newline at end of file diff --git a/smash/web/views/daily_planning.py b/smash/web/views/daily_planning.py index 40ab776c754d5bc3c8b89e8d9e1ec70701669f53..d720816f0672bb455260b5fc3f4a9caa9fc4c2db 100644 --- a/smash/web/views/daily_planning.py +++ b/smash/web/views/daily_planning.py @@ -1,12 +1,15 @@ # coding=utf-8 -import logging from django.views.generic import TemplateView -from . import wrap_response + +from web.decorators import PermissionDecorator from web.models.worker_study_role import STUDY_ROLE_CHOICES +from . import wrap_response + class TemplateDailyPlannerView(TemplateView): + @PermissionDecorator('view_daily_planning', 'daily_planning') def get(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) context['worker_study_roles'] = STUDY_ROLE_CHOICES - return wrap_response(request, 'daily_planning.html', context) \ No newline at end of file + return wrap_response(request, 'daily_planning.html', context) diff --git a/smash/web/views/equipment.py b/smash/web/views/equipment.py index ff498134cab372d0f026c0316f89295d60b83191..4d006e41d3db59e1d8785f6e0d35ddab089cf3d1 100644 --- a/smash/web/views/equipment.py +++ b/smash/web/views/equipment.py @@ -1,11 +1,13 @@ # coding=utf-8 from django.shortcuts import redirect, get_object_or_404 +from web.decorators import PermissionDecorator from . import wrap_response -from ..models import Item from ..forms.forms import ItemForm +from ..models import Item +@PermissionDecorator('change_item', 'equipment') def equipment(request): equipment_list = Item.objects.order_by('-name') context = { @@ -15,6 +17,7 @@ def equipment(request): return wrap_response(request, "equipment_and_rooms/equipment/index.html", context) +@PermissionDecorator('change_item', 'equipment') def equipment_add(request): if request.method == 'POST': form = ItemForm(request.POST) @@ -27,6 +30,7 @@ def equipment_add(request): return wrap_response(request, 'equipment_and_rooms/equipment/add.html', {'form': form}) +@PermissionDecorator('change_item', 'equipment') def equipment_edit(request, equipment_id): the_item = get_object_or_404(Item, id=equipment_id) if request.method == 'POST': @@ -40,6 +44,7 @@ def equipment_edit(request, equipment_id): return wrap_response(request, 'equipment_and_rooms/equipment/edit.html', {'form': form}) +@PermissionDecorator('change_item', 'equipment') def equipment_delete(request, equipment_id): the_item = get_object_or_404(Item, id=equipment_id) the_item.delete() diff --git a/smash/web/views/export.py b/smash/web/views/export.py index 02fc1a82c1cb8a61341f3f1c0af9469209da0a0c..1cb6271207a31855a0199c62945cd654e336849a 100644 --- a/smash/web/views/export.py +++ b/smash/web/views/export.py @@ -5,10 +5,12 @@ import django_excel as excel from django.http import HttpResponse from notifications import get_today_midnight_date +from web.decorators import PermissionDecorator from . import e500_error, wrap_response from ..models import Subject, StudySubject, Appointment +@PermissionDecorator('export_subjects', 'subject') def export_to_csv(request, data_type="subjects"): # Create the HttpResponse object with the appropriate CSV header. selected_fields = request.GET.get('fields', None) @@ -29,6 +31,7 @@ def export_to_csv(request, data_type="subjects"): return response +@PermissionDecorator('export_subjects', 'subject') def export_to_excel(request, data_type="subjects"): selected_fields = request.GET.get('fields', None) filename = data_type + '-' + get_today_midnight_date().strftime("%Y-%m-%d") + ".xls" @@ -53,26 +56,27 @@ class CustomField: DROP_OUT_FIELD = CustomField({'verbose_name': "DROP OUT", 'name': "custom-drop-out"}) APPOINTMENT_TYPE_FIELD = CustomField({ - 'name': 'appointment_types', - 'verbose_name': 'Appointment Types' - }) + 'name': 'appointment_types', + 'verbose_name': 'Appointment Types' +}) STUDY_SUBJECT_FIELDS = [CustomField({ - 'name': 'nd_number', - 'verbose_name' : 'ND number' - })] + 'name': 'nd_number', + 'verbose_name': 'ND number' +})] SUBJECT_FIELDS = [CustomField({ - 'name': 'last_name', - 'verbose_name': 'Family name' - }), + 'name': 'last_name', + 'verbose_name': 'Family name' +}), CustomField({ 'name': 'first_name', 'verbose_name': 'Name' })] VISIT_FIELDS = [CustomField({ - 'name': 'visit_number', - 'verbose_name': 'Visit' - })] + 'name': 'visit_number', + 'verbose_name': 'Visit' +})] + def filter_fields_from_selected_fields(fields, selected_fields): if selected_fields is None: @@ -80,6 +84,7 @@ def filter_fields_from_selected_fields(fields, selected_fields): selected_fields = set(selected_fields.split(',')) return [field for field in fields if field.name in selected_fields] + def get_default_subject_fields(): subject_fields = [] for field in Subject._meta.fields: @@ -91,12 +96,13 @@ def get_default_subject_fields(): subject_fields.append(DROP_OUT_FIELD) return subject_fields + def get_subjects_as_array(selected_fields=None): result = [] - subject_fields = get_default_subject_fields() + subject_fields = get_default_subject_fields() subject_fields = filter_fields_from_selected_fields(subject_fields, selected_fields) - field_names = [field.verbose_name for field in subject_fields] #faster than loop + field_names = [field.verbose_name for field in subject_fields] # faster than loop result.append(field_names) subjects = StudySubject.objects.order_by('-subject__last_name') @@ -105,6 +111,7 @@ def get_subjects_as_array(selected_fields=None): result.append([unicode(s).replace("\n", ";").replace("\r", ";") for s in row]) return result + def subject_to_row_for_fields(study_subject, subject_fields): row = [] for field in subject_fields: @@ -128,31 +135,33 @@ def subject_to_row_for_fields(study_subject, subject_fields): row.append(cell) return row + def get_appointment_fields(): appointments_fields = [] for field in Appointment._meta.fields: if field.name.upper() != "VISIT" and field.name.upper() != "ID" and \ - field.name.upper() != "WORKER_ASSIGNED" and field.name.upper() != "APPOINTMENT_TYPES" and \ - field.name.upper() != "ROOM" and field.name.upper() != "FLYING_TEAM": + field.name.upper() != "WORKER_ASSIGNED" and field.name.upper() != "APPOINTMENT_TYPES" and \ + field.name.upper() != "ROOM" and field.name.upper() != "FLYING_TEAM": appointments_fields.append(field) all_fields = STUDY_SUBJECT_FIELDS + SUBJECT_FIELDS + VISIT_FIELDS + appointments_fields + [APPOINTMENT_TYPE_FIELD] return all_fields, appointments_fields + def get_appointments_as_array(selected_fields=None): result = [] all_fields, appointments_fields = get_appointment_fields() all_fields = filter_fields_from_selected_fields(all_fields, selected_fields) appointments_fields = filter_fields_from_selected_fields(appointments_fields, selected_fields) - field_names = [field.verbose_name for field in all_fields] #faster than loop + field_names = [field.verbose_name for field in all_fields] # faster than loop result.append(field_names) appointments = Appointment.objects.order_by('-datetime_when') for appointment in appointments: - #add field_names ['ND number', 'Family name', 'Name', 'Visit'] first + # add field_names ['ND number', 'Family name', 'Name', 'Visit'] first row = [] for field in STUDY_SUBJECT_FIELDS: if field.verbose_name in field_names: @@ -175,15 +184,16 @@ def get_appointments_as_array(selected_fields=None): for field in appointments_fields: row.append(getattr(appointment, field.name)) if APPOINTMENT_TYPE_FIELD.verbose_name in field_names: - #avoid last comma in the list of appointment types + # avoid last comma in the list of appointment types type_string = ','.join([appointment_type.code for appointment_type in appointment.appointment_types.all()]) row.append(type_string) result.append([unicode(s).replace("\n", ";").replace("\r", ";") for s in row]) return result +@PermissionDecorator('export_subjects', 'subject') def export(request): return wrap_response(request, 'export/index.html', { 'subject_fields': get_default_subject_fields(), 'appointment_fields': get_appointment_fields()[0] - }) \ No newline at end of file + }) diff --git a/smash/web/views/flying_teams.py b/smash/web/views/flying_teams.py index dc6c4752045cef881778084f6ece845aa259ef2f..b0f5c7b01f0403daf1bea1a4f5c9f69c52b4f03e 100644 --- a/smash/web/views/flying_teams.py +++ b/smash/web/views/flying_teams.py @@ -1,11 +1,13 @@ # coding=utf-8 from django.shortcuts import redirect, get_object_or_404 +from web.decorators import PermissionDecorator from . import wrap_response -from ..models import FlyingTeam from ..forms.forms import FlyingTeamAddForm, FlyingTeamEditForm +from ..models import FlyingTeam +@PermissionDecorator('change_flyingteam', 'equipment') def flying_teams(request): flying_team_list = FlyingTeam.objects.order_by('-place') context = { @@ -16,6 +18,8 @@ def flying_teams(request): "equipment_and_rooms/flying_teams/index.html", context) + +@PermissionDecorator('change_flyingteam', 'equipment') def flying_teams_add(request): if request.method == 'POST': form = FlyingTeamAddForm(request.POST) @@ -28,6 +32,7 @@ def flying_teams_add(request): return wrap_response(request, 'equipment_and_rooms/flying_teams/add.html', {'form': form}) +@PermissionDecorator('change_flyingteam', 'equipment') def flying_teams_edit(request, flying_team_id): the_flying_team = get_object_or_404(FlyingTeam, id=flying_team_id) if request.method == 'POST': diff --git a/smash/web/views/kit.py b/smash/web/views/kit.py index 79c42ef5fb89430026ceaa05c20e766f7175e7ca..c099d903f2716b474cadbcc60be70ce2f1d376db 100644 --- a/smash/web/views/kit.py +++ b/smash/web/views/kit.py @@ -13,6 +13,7 @@ from django_cron import CronJobBase, Schedule from django_cron.models import CronJobLog from notifications import get_filter_locations, get_today_midnight_date +from web.decorators import PermissionDecorator from web.models import ConfigurationItem, Language, Worker from web.models.constants import KIT_EMAIL_HOUR_CONFIGURATION_TYPE, \ KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE, CRON_JOB_TIMEOUT @@ -60,6 +61,7 @@ def get_kit_requests(user, start_date=None, end_date=None): return result +@PermissionDecorator('send_sample_mail_for_appointments', 'equipment') def get_kit_requests_data(request, start_date=None, end_date=None): form = KitRequestForm() if request.method == 'POST': @@ -76,6 +78,7 @@ def get_kit_requests_data(request, start_date=None, end_date=None): return params +@PermissionDecorator('send_sample_mail_for_appointments', 'equipment') def kit_requests(request): return wrap_response(request, 'equipment_and_rooms/kit_requests/kit_requests.html', get_kit_requests_data(request)) @@ -85,7 +88,7 @@ def send_mail(data): if data["end_date"] is not None: end_date_str = data["end_date"].strftime('%Y-%m-%d') title = "Samples between " + \ - data["start_date"].strftime('%Y-%m-%d') + " and " + end_date_str + data["start_date"].strftime('%Y-%m-%d') + " and " + end_date_str cell_style = "padding: 8px; line-height: 1.42857143; vertical-align: top; " \ "font-size: 14px; font-family: 'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif;" @@ -109,10 +112,10 @@ def send_mail(data): row_style = ' background-color: #f9f9f9;' email_body += "<tr style='" + row_style + "'>" email_body += "<td style='" + cell_style + "'>" + \ - appointment.datetime_when.strftime('%Y-%m-%d %H:%M') + "</td>" + appointment.datetime_when.strftime('%Y-%m-%d %H:%M') + "</td>" if appointment.visit is not None and appointment.visit.subject is not None: email_body += "<td style='" + cell_style + "'>" + \ - appointment.visit.subject.nd_number + "</td>" + appointment.visit.subject.nd_number + "</td>" else: email_body += "<td style='" + cell_style + "'>" + '-' + "</td>" email_body += "<td style='" + cell_style + "'>" @@ -126,7 +129,7 @@ def send_mail(data): location += " (" + unicode(appointment.flying_team) + ")" email_body += "<td style='" + cell_style + "'>" + location + "</td>" email_body += "<td style='" + cell_style + "'>" + \ - unicode(appointment.worker_assigned) + "</td>" + unicode(appointment.worker_assigned) + "</td>" email_body += "</tr>" email_body += "</tbody></table>" recipients = ConfigurationItem.objects.get( @@ -136,6 +139,7 @@ def send_mail(data): EmailSender().send_email(title, email_body, recipients, cc_recipients) +@PermissionDecorator('send_sample_mail_for_appointments', 'equipment') def kit_requests_send_mail(request, start_date, end_date=None): data = get_kit_requests_data(request, start_date, end_date) try: diff --git a/smash/web/views/mails.py b/smash/web/views/mails.py index ef4cf0b3bb6ce05c8ff4f35b0246c0f830ea5171..7b49409c99a1f1fafb6c4600bf16fbd9af6897d2 100644 --- a/smash/web/views/mails.py +++ b/smash/web/views/mails.py @@ -9,6 +9,7 @@ from django.urls import reverse_lazy from django.views.generic import DeleteView from django.views.generic import ListView +from web.decorators import PermissionDecorator from web.docx_helper import merge_files from . import WrappedView from . import wrap_response @@ -32,7 +33,11 @@ class MailTemplatesListView(ListView, WrappedView): context_object_name = "mail_templates" template_name = 'mail_templates/list.html' - def get_context_data(self, **kwargs): + @PermissionDecorator('change_mailtemplate', 'mailtemplate') + def dispatch(self, *args, **kwargs): + return super(MailTemplatesListView, self).dispatch(*args, **kwargs) + + def get_context_data(self, *args, **kwargs): context = super(MailTemplatesListView, self).get_context_data() context['explanations'] = {"generic": MailTemplate.MAILS_TEMPLATE_GENERIC_TAGS, "subject": MailTemplate.MAILS_TEMPLATE_SUBJECT_TAGS, @@ -43,6 +48,7 @@ class MailTemplatesListView(ListView, WrappedView): return context +@PermissionDecorator('change_mailtemplate', 'mailtemplate') def mail_template_add(request): if request.method == 'POST': form = MailTemplateForm(request.POST, request.FILES) @@ -59,6 +65,7 @@ def mail_template_add(request): return wrap_response(request, 'mail_templates/add.html', {'form': form}) +@PermissionDecorator('change_mailtemplate', 'mailtemplate') def mail_template_edit(request, pk): template = get_object_or_404(MailTemplate, pk=pk) if request.method == 'POST': @@ -82,6 +89,7 @@ class MailTemplatesDeleteView(DeleteView, WrappedView): success_url = reverse_lazy('web.views.mail_templates') template_name = 'mail_templates/confirm_delete.html' + @PermissionDecorator('change_mailtemplate', 'mailtemplate') def delete(self, request, *args, **kwargs): messages.success(request, "Template deleted") try: diff --git a/smash/web/views/rooms.py b/smash/web/views/rooms.py index 2ff626e95a9259a0769bcb7b8e840a40908952bc..a5ca91933a09431edc200fb0febb51cd43fe3134 100644 --- a/smash/web/views/rooms.py +++ b/smash/web/views/rooms.py @@ -1,11 +1,13 @@ # coding=utf-8 from django.shortcuts import redirect, get_object_or_404 +from web.decorators import PermissionDecorator from . import wrap_response from ..forms.forms import RoomForm from ..models import Room +@PermissionDecorator('change_room', 'equipment') def rooms(request): rooms_list = Room.objects.order_by('-city') context = { @@ -17,6 +19,7 @@ def rooms(request): context) +@PermissionDecorator('change_room', 'equipment') def rooms_add(request): if request.method == 'POST': form = RoomForm(request.POST) @@ -29,6 +32,7 @@ def rooms_add(request): return wrap_response(request, 'equipment_and_rooms/rooms/add.html', {'form': form}) +@PermissionDecorator('change_room', 'equipment') def rooms_edit(request, room_id): the_room = get_object_or_404(Room, id=room_id) if request.method == 'POST': @@ -42,6 +46,7 @@ def rooms_edit(request, room_id): return wrap_response(request, 'equipment_and_rooms/rooms/edit.html', {'form': form}) +@PermissionDecorator('change_room', 'equipment') def rooms_delete(request, room_id): the_room = get_object_or_404(Room, id=room_id) the_room.delete() diff --git a/smash/web/views/statistics.py b/smash/web/views/statistics.py index 67bdb79c8784b427bba9526b546661f266eaf10f..b8cd84cc9cb8316a434b6c8dfe438a038a00f126 100644 --- a/smash/web/views/statistics.py +++ b/smash/web/views/statistics.py @@ -1,9 +1,11 @@ # coding=utf-8 +from web.decorators import PermissionDecorator from . import wrap_response from ..forms import StatisticsForm from ..statistics import StatisticsManager, get_previous_year_and_month +@PermissionDecorator('view_statistics', 'appointment') def statistics(request): statistics_manager = StatisticsManager() visit_choices = [("-1", "all")]