From cd02637c7b89a4fbfdf90ae35cb81d04d7b82dab Mon Sep 17 00:00:00 2001 From: Piotr Gawron <piotr.gawron@uni.lu> Date: Thu, 19 Mar 2020 16:29:07 +0100 Subject: [PATCH] export_subjects permission implemented --- .../web/migrations/0144_auto_20200319_1518.py | 19 +++++++ smash/web/models/subject.py | 19 ++++--- smash/web/templates/sidebar.html | 14 +++--- smash/web/tests/view/test_export.py | 21 ++++++++ smash/web/views/export.py | 50 +++++++++++-------- 5 files changed, 90 insertions(+), 33 deletions(-) create mode 100644 smash/web/migrations/0144_auto_20200319_1518.py diff --git a/smash/web/migrations/0144_auto_20200319_1518.py b/smash/web/migrations/0144_auto_20200319_1518.py new file mode 100644 index 00000000..bdb5b3ee --- /dev/null +++ b/smash/web/migrations/0144_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', '0143_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/models/subject.py b/smash/web/models/subject.py index b762bfe3..0d0eb57f 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 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, @@ -103,7 +108,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,8 +135,8 @@ 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: - instance.mark_as_dead() \ No newline at end of file + if instance.dead: + instance.mark_as_dead() diff --git a/smash/web/templates/sidebar.html b/smash/web/templates/sidebar.html index 5aceea8b..9d215925 100644 --- a/smash/web/templates/sidebar.html +++ b/smash/web/templates/sidebar.html @@ -83,12 +83,14 @@ </li> {% endif %} - <li data-desc="export"> - <a href="{% url 'web.views.export' %}"> - <i class="fa fa-file-excel-o"></i> - <span>Export</span> - </a> - </li> + {% 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/tests/view/test_export.py b/smash/web/tests/view/test_export.py index d6938476..3f6a5c99 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/views/export.py b/smash/web/views/export.py index 02fc1a82..1cb62712 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 + }) -- GitLab