Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# (c) 2018-2020
2# MPIB <https://www.mpib-berlin.mpg.de/>,
3# MPI-CBS <https://www.cbs.mpg.de/>,
4# MPIP <http://www.psych.mpg.de/>
5#
6# This file is part of Castellum.
7#
8# Castellum is free software; you can redistribute it and/or modify it
9# under the terms of the GNU Affero General Public License as published
10# by the Free Software Foundation; either version 3 of the License, or
11# (at your option) any later version.
12#
13# Castellum is distributed in the hope that it will be useful, but
14# WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16# Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public
19# License along with Castellum. If not, see
20# <http://www.gnu.org/licenses/>.
22import datetime
24from django import forms
25from django.conf import settings
26from django.forms import ValidationError
27from django.utils import timezone
28from django.utils.translation import gettext_lazy as _
30from castellum.utils.forms import DatalistWidget
32from .models import Consent
33from .models import ConsentDocument
34from .models import Subject
37def get_initial_consent_status(subject):
38 try:
39 consent = Consent.objects.get(subject=subject, document__is_valid=True)
40 except Consent.DoesNotExist:
41 return ''
43 acceptable_datetime = timezone.now() - settings.CASTELLUM_CONSENT_REVIEW_PERIOD
44 if consent.status == Consent.WAITING and consent.updated_at <= acceptable_datetime:
45 return ''
47 return consent.status
50class DataProtectionForm(forms.ModelForm):
52 class Meta:
53 model = Subject
54 fields = ('privacy_level',)
55 widgets = {
56 'privacy_level': forms.RadioSelect,
57 'source': DatalistWidget(datalist=(
58 Subject.objects.order_by('source').values_list('source', flat=True).distinct
59 )),
60 }
62 consent_status = Consent._meta.get_field('status').formfield(
63 label=_('Recruitment consent'),
64 help_text=_(
65 'Without recruitment consent, this subject will not be taken into account for future '
66 'recruitment. Withdrawing recruitment consent has no effect on study consent. '
67 'Therefore no data related to existing studies is deleted.'
68 ),
69 required=False,
70 )
71 to_be_deleted = forms.BooleanField(
72 label=_("To be deleted"),
73 required=False,
74 help_text=Subject._meta.get_field('to_be_deleted').help_text,
75 )
76 export_requested = forms.BooleanField(
77 label=_('Export requested'),
78 required=False,
79 help_text=Subject._meta.get_field('export_requested').help_text,
80 )
82 def __init__(self, user, *args, **kwargs):
83 self.user = user
84 super().__init__(*args, **kwargs)
86 self.fields['to_be_deleted'].initial = self.instance.to_be_deleted
87 self.fields['export_requested'].initial = self.instance.export_requested
88 self.fields['consent_status'].initial = get_initial_consent_status(self.instance)
90 def clean(self):
91 cleaned_data = super().clean()
93 privacy_level = cleaned_data.get('privacy_level', 0)
94 if not self.user.has_privacy_level(privacy_level):
95 raise ValidationError(
96 _("Please choose lower privacy level for the subject."), code='invalid'
97 )
99 if cleaned_data.get('to_be_deleted') and cleaned_data.get('consent_status'):
100 raise ValidationError(
101 _(
102 'Subjects can either be marked for deletion or have recruitment consent, '
103 'not both.'
104 ),
105 code='invalid',
106 )
108 return cleaned_data
110 def save(self, commit=True):
111 if not self.cleaned_data.get('to_be_deleted'): 111 ↛ 113line 111 didn't jump to line 113, because the condition on line 111 was never false
112 self.instance.to_be_deleted = None
113 elif not self.instance.to_be_deleted:
114 self.instance.to_be_deleted = datetime.date.today()
116 if not self.cleaned_data.get('export_requested'): 116 ↛ 118line 116 didn't jump to line 118, because the condition on line 116 was never false
117 self.instance.export_requested = None
118 elif not self.instance.export_requested:
119 self.instance.export_requested = datetime.date.today()
121 subject = super().save(commit)
123 new_status = self.cleaned_data.get('consent_status')
124 if new_status:
125 try:
126 document = ConsentDocument.objects.filter(is_valid=True).latest()
127 except ConsentDocument.DoesNotExist:
128 document = ConsentDocument.objects.create()
129 try:
130 initial_status = get_initial_consent_status(subject)
131 if initial_status != new_status:
132 if initial_status != Consent.WAITING:
133 subject.consent.document = document
134 subject.consent.status = new_status
135 subject.consent.save()
136 except Consent.DoesNotExist:
137 Consent.objects.create(
138 subject=subject,
139 status=new_status,
140 document=document,
141 )
142 else:
143 Consent.objects.filter(subject=subject).delete()
145 return subject
148class SubjectPrivacyLevelForm(forms.ModelForm):
150 class Meta:
151 model = Subject
152 fields = ['privacy_level']
153 widgets = {
154 'privacy_level': forms.RadioSelect,
155 }
157 def __init__(self, user, *args, **kwargs):
158 super().__init__(*args, **kwargs)
159 self.user = user
161 def clean(self):
162 cleaned_data = super().clean()
164 privacy_level = cleaned_data.get('privacy_level', 0)
165 if not self.user.has_privacy_level(privacy_level): 165 ↛ 166line 165 didn't jump to line 166, because the condition on line 165 was never true
166 raise ValidationError(
167 _("Please choose lower privacy level for the subject."),
168 code='invalid',
169 )
170 return cleaned_data