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 @@
- {% 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()