Hide keyboard shortcuts

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/>. 

21 

22import datetime 

23 

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 _ 

29 

30from castellum.utils.forms import DatalistWidget 

31 

32from .models import Consent 

33from .models import ConsentDocument 

34from .models import Subject 

35 

36 

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 '' 

42 

43 acceptable_datetime = timezone.now() - settings.CASTELLUM_CONSENT_REVIEW_PERIOD 

44 if consent.status == Consent.WAITING and consent.updated_at <= acceptable_datetime: 

45 return '' 

46 

47 return consent.status 

48 

49 

50class DataProtectionForm(forms.ModelForm): 

51 

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 } 

61 

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 ) 

81 

82 def __init__(self, user, *args, **kwargs): 

83 self.user = user 

84 super().__init__(*args, **kwargs) 

85 

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) 

89 

90 def clean(self): 

91 cleaned_data = super().clean() 

92 

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 ) 

98 

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 ) 

107 

108 return cleaned_data 

109 

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() 

115 

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() 

120 

121 subject = super().save(commit) 

122 

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() 

144 

145 return subject 

146 

147 

148class SubjectPrivacyLevelForm(forms.ModelForm): 

149 

150 class Meta: 

151 model = Subject 

152 fields = ['privacy_level'] 

153 widgets = { 

154 'privacy_level': forms.RadioSelect, 

155 } 

156 

157 def __init__(self, user, *args, **kwargs): 

158 super().__init__(*args, **kwargs) 

159 self.user = user 

160 

161 def clean(self): 

162 cleaned_data = super().clean() 

163 

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