diff --git a/castellum/execution/api.py b/castellum/execution/api.py index b31d637883337a58d76d27db4afb411304dada7f..225678cced2577c2f9387c9e3061fbcfc964afec 100644 --- a/castellum/execution/api.py +++ b/castellum/execution/api.py @@ -148,7 +148,10 @@ class APIAttributesView(StrongholdPublicMixin, StudyMixin, APIAuthMixin, View): get_object_or_404( Participation, subject=subject, study=self.study, status=Participation.INVITED ) - if not self.request.user.has_privacy_level(subject.privacy_level): + if not self.request.user.has_privacy_level(max( + subject.privacy_level, + self.study.get_exportable_attributes_max_privacy_level(), + )): raise PermissionDenied monitoring_logger.info('Attribute export: study {} subject {} by {}'.format( diff --git a/castellum/execution/templates/execution/study_base.html b/castellum/execution/templates/execution/study_base.html index 386b799091f5cb64addb7af296475c417bcb9060..4a32d3937f1694709728b8f618fc881fb121037e 100644 --- a/castellum/execution/templates/execution/study_base.html +++ b/castellum/execution/templates/execution/study_base.html @@ -8,8 +8,9 @@

{{ study }}

- {% has_privacy_level study.get_invited_max_privacy_level user as has_privacy_level %} - {% if has_privacy_level %} + {% has_privacy_level study.get_invited_max_privacy_level user as has_subject_privacy_level %} + {% has_privacy_level study.get_exportable_attributes_max_privacy_level user as has_attribute_privacy_level %} + {% if has_subject_privacy_level and has_attribute_privacy_level %} {% trans 'Export recruitment attributes' %} {% endif %} {% if study.consent %} diff --git a/castellum/execution/views.py b/castellum/execution/views.py index b3e1e684f5526bb3e9ce0dedb4d3035962418d83..44ba07f659ce73854166ef79e1d7e0f467715e14 100644 --- a/castellum/execution/views.py +++ b/castellum/execution/views.py @@ -142,7 +142,10 @@ class ExportView(StudyMixin, PermissionRequiredMixin, DetailView): return response def get(self, request, **kwargs): - if not request.user.has_privacy_level(self.study.get_invited_max_privacy_level()): + if not request.user.has_privacy_level(max( + self.study.get_invited_max_privacy_level(), + self.study.get_exportable_attributes_max_privacy_level(), + )): raise PermissionDenied if 'domain' in self.request.GET: domain = get_object_or_404(self.study.domains.all(), key=self.request.GET['domain']) diff --git a/castellum/studies/migrations/0035_alter_study_exportable_attributes.py b/castellum/studies/migrations/0035_alter_study_exportable_attributes.py new file mode 100644 index 0000000000000000000000000000000000000000..a6d1638b54cb37e9a4a4266dbeae6d2140ae858b --- /dev/null +++ b/castellum/studies/migrations/0035_alter_study_exportable_attributes.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.6 on 2021-09-01 12:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('recruitment', '0037_participation_excluded_by_cleanup'), + ('studies', '0034_study_name_unique'), + ] + + operations = [ + migrations.AlterField( + model_name='study', + name='exportable_attributes', + field=models.ManyToManyField(blank=True, related_name='_studies_study_exportable_attributes_+', to='recruitment.AttributeDescription', verbose_name='Exportable recruitment attributes'), + ), + ] diff --git a/castellum/studies/models.py b/castellum/studies/models.py index 09387c8a838a5aa7e7937c5e1c8f8da14c020048..f2ce526da0d38128a84eb8c4d95fd73b9f7bd96c 100644 --- a/castellum/studies/models.py +++ b/castellum/studies/models.py @@ -212,7 +212,6 @@ class Study(models.Model): verbose_name=_('Exportable recruitment attributes'), related_name='+', blank=True, - limit_choices_to={'privacy_level_read': 0}, ) mail_subject = models.CharField(_('E-mail subject'), max_length=254, blank=True) @@ -281,6 +280,11 @@ class Study(models.Model): ) return result[key + '__max'] or 0 + def get_exportable_attributes_max_privacy_level(self): + key = 'privacy_level_read' + result = self.exportable_attributes.aggregate(models.Max(key)) + return result[key + '__max'] or 0 + @property def study_type(self): return StudyType.objects.filter(studysession__study=self).distinct()