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