From 57ca637e891291608b0c2255f2c28b73aa2e336f Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Tue, 4 Aug 2020 16:30:53 +0200 Subject: [PATCH 1/3] add study.stat_attributes --- castellum/studies/forms.py | 1 + .../0026_study_exportable_attributes.py | 19 +++++++++++++++++++ castellum/studies/models.py | 8 ++++++++ .../studies/templates/studies/study_form.html | 1 + castellum/studies/views/studies.py | 1 + 5 files changed, 30 insertions(+) create mode 100644 castellum/studies/migrations/0026_study_exportable_attributes.py diff --git a/castellum/studies/forms.py b/castellum/studies/forms.py index c01c8f037..89d36b6de 100644 --- a/castellum/studies/forms.py +++ b/castellum/studies/forms.py @@ -53,6 +53,7 @@ class StudyForm(ReadonlyModelForm): 'data_sensitivity', 'consent', 'min_subject_count', + 'exportable_attributes', ] help_texts = { 'announce_status_changes': ( diff --git a/castellum/studies/migrations/0026_study_exportable_attributes.py b/castellum/studies/migrations/0026_study_exportable_attributes.py new file mode 100644 index 000000000..f6523bb33 --- /dev/null +++ b/castellum/studies/migrations/0026_study_exportable_attributes.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.3 on 2020-11-17 11:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('recruitment', '0022_delete_attributeset'), + ('studies', '0025_studysession_schedule_id'), + ] + + operations = [ + migrations.AddField( + model_name='study', + name='exportable_attributes', + field=models.ManyToManyField(blank=True, limit_choices_to={'privacy_level_read': 0}, related_name='_study_exportable_attributes_+', to='recruitment.AttributeDescription', verbose_name='Exportable attributes'), + ), + ] diff --git a/castellum/studies/models.py b/castellum/studies/models.py index 12f4eb679..d2fc472f0 100644 --- a/castellum/studies/models.py +++ b/castellum/studies/models.py @@ -191,6 +191,14 @@ class Study(models.Model): max_upload_size=settings.CASTELLUM_FILE_UPLOAD_MAX_SIZE, ) + exportable_attributes = models.ManyToManyField( + 'recruitment.AttributeDescription', + verbose_name=_('Exportable attributes'), + related_name='+', + blank=True, + limit_choices_to={'privacy_level_read': 0}, + ) + mail_subject = models.CharField(_('E-mail subject'), max_length=254, blank=True) mail_body = models.TextField( _('E-mail body'), diff --git a/castellum/studies/templates/studies/study_form.html b/castellum/studies/templates/studies/study_form.html index 293c25dcc..f66be0aa2 100644 --- a/castellum/studies/templates/studies/study_form.html +++ b/castellum/studies/templates/studies/study_form.html @@ -32,6 +32,7 @@ {% bootstrap_field form.data_sensitivity %} {% bootstrap_field form.consent %} {% bootstrap_field form.min_subject_count %} + {% bootstrap_field form.exportable_attributes %} {% if form.is_onetime_invitation %} {% bootstrap_field form.is_onetime_invitation %} {% endif %} diff --git a/castellum/studies/views/studies.py b/castellum/studies/views/studies.py index 2cd157dd1..135143219 100644 --- a/castellum/studies/views/studies.py +++ b/castellum/studies/views/studies.py @@ -192,6 +192,7 @@ class StudyCreateView(PermissionRequiredMixin, CreateView): 'data_sensitivity', 'consent', 'min_subject_count', + 'exportable_attributes', 'is_onetime_invitation', ] permission_required = 'studies.change_study' -- GitLab From 9f26220b0710cf98552a77258b66f89504b7cd44 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Tue, 4 Aug 2020 18:02:19 +0200 Subject: [PATCH 2/3] allow to export execution list --- .../templates/execution/study_detail.html | 1 + castellum/execution/views.py | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/castellum/execution/templates/execution/study_detail.html b/castellum/execution/templates/execution/study_detail.html index 7d19f4113..1f3ffd627 100644 --- a/castellum/execution/templates/execution/study_detail.html +++ b/castellum/execution/templates/execution/study_detail.html @@ -46,5 +46,6 @@ {% translate 'Resolve pseudonym' %} {% translate 'Calendar' %} {% translate 'News mail' %} + {% trans 'Export attributes' %} {% endblock %} diff --git a/castellum/execution/views.py b/castellum/execution/views.py index c23b13b54..66c4583d4 100644 --- a/castellum/execution/views.py +++ b/castellum/execution/views.py @@ -26,6 +26,7 @@ from django.conf import settings from django.contrib import messages from django.core.exceptions import ObjectDoesNotExist from django.db import models +from django.http import HttpResponse from django.shortcuts import get_object_or_404 from django.shortcuts import redirect from django.urls import reverse @@ -43,6 +44,7 @@ from castellum.castellum_auth.mixins import PermissionRequiredMixin from castellum.contacts.mixins import BaseContactUpdateView from castellum.pseudonyms.forms import PseudonymForm from castellum.pseudonyms.helpers import get_subject +from castellum.recruitment.attribute_exporters import get_exporter from castellum.recruitment.mixins import BaseAttributesUpdateView from castellum.recruitment.mixins import ParticipationMixin from castellum.recruitment.models import NewsMailBatch @@ -84,6 +86,25 @@ class StudyDetailView(StudyMixin, PermissionRequiredMixin, DetailView): return context + def export(self): + exporter = get_exporter() + descriptions = self.study.exportable_attributes.all() + + subjects = [] + for participation in self.study.participation_set.filter(status=Participation.INVITED): + has_access = self.request.user.has_privacy_level(participation.subject.privacy_level) + if has_access: + subjects.append((participation.pseudonym, participation.subject)) + + data = exporter.get_data(descriptions, subjects) + return HttpResponse(data) + + def get(self, request, *args, **kwargs): + if 'export' in request.GET: + return self.export() + else: + return super().get(request, *args, **kwargs) + class ParticipationDetailMixin(ParticipationMixin, PermissionRequiredMixin): model = Participation -- GitLab From ccdab543e0ccba8f9bd3f1a4a587ef4236532069 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Wed, 25 Nov 2020 12:34:47 +0100 Subject: [PATCH 3/3] wrap data and schema in zip file --- castellum/execution/views.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/castellum/execution/views.py b/castellum/execution/views.py index 66c4583d4..1f2983833 100644 --- a/castellum/execution/views.py +++ b/castellum/execution/views.py @@ -20,6 +20,7 @@ # . import datetime +import zipfile from django import forms from django.conf import settings @@ -89,6 +90,8 @@ class StudyDetailView(StudyMixin, PermissionRequiredMixin, DetailView): def export(self): exporter = get_exporter() descriptions = self.study.exportable_attributes.all() + response = HttpResponse(content_type='application/zip') + zresponse = zipfile.ZipFile(response, 'w') subjects = [] for participation in self.study.participation_set.filter(status=Participation.INVITED): @@ -96,8 +99,17 @@ class StudyDetailView(StudyMixin, PermissionRequiredMixin, DetailView): if has_access: subjects.append((participation.pseudonym, participation.subject)) - data = exporter.get_data(descriptions, subjects) - return HttpResponse(data) + filename = exporter.get_schema_filename() + content = exporter.get_schema(descriptions) + with zresponse.open(filename, 'w') as fh: + fh.write(content.encode('UTF-8')) + + filename = exporter.get_data_filename() + content = exporter.get_data(descriptions, subjects) + with zresponse.open(filename, 'w') as fh: + fh.write(content.encode('UTF-8')) + + return response def get(self, request, *args, **kwargs): if 'export' in request.GET: -- GitLab