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 

22from django import forms 

23from django.conf import settings 

24from django.core.exceptions import PermissionDenied 

25from django.core.mail import send_mail 

26from django.urls import reverse 

27from django.utils.functional import cached_property 

28from django.views.generic import UpdateView 

29 

30from castellum.castellum_auth.mixins import PermissionRequiredMixin 

31 

32from .forms import DataProtectionForm 

33from .models import Subject 

34from .models import SubjectNote 

35 

36 

37class SubjectMixin: 

38 """Use this on every view that represents a subject. 

39 

40 - set ``self.subject`` 

41 - check privacy level 

42 """ 

43 

44 @cached_property 

45 def subject(self): 

46 obj = self.get_object() 

47 if isinstance(obj, Subject): 

48 return obj 

49 else: 

50 return obj.subject 

51 

52 def dispatch(self, request, *args, **kwargs): 

53 if request.user.is_authenticated: 53 ↛ 56line 53 didn't jump to line 56, because the condition on line 53 was never false

54 if not request.user.has_privacy_level(self.subject.privacy_level): 54 ↛ 55line 54 didn't jump to line 55, because the condition on line 54 was never true

55 raise PermissionDenied 

56 return super().dispatch(request, *args, **kwargs) 

57 

58 def get_context_data(self, **kwargs): 

59 context = super().get_context_data(**kwargs) 

60 context['subject'] = self.subject 

61 return context 

62 

63 

64class BaseDataProtectionUpdateView(PermissionRequiredMixin, UpdateView): 

65 model = Subject 

66 form_class = DataProtectionForm 

67 permission_required = 'subjects.change_subject' 

68 template_name = 'subjects/subject_data_protection_form.html' 

69 

70 def get_form_kwargs(self): 

71 kwargs = super().get_form_kwargs() 

72 kwargs['user'] = self.request.user 

73 return kwargs 

74 

75 def form_valid(self, form): 

76 if settings.CASTELLUM_DELETE_NOTIFICATION_TO: 76 ↛ 77line 76 didn't jump to line 77, because the condition on line 76 was never true

77 old = Subject.objects.get(pk=self.object.pk) 

78 if self.object.export_requested and not old.export_requested: 

79 rel = reverse('subjects:detail', args=[self.object.pk]) 

80 url = self.request.build_absolute_uri(rel) 

81 

82 self.object.to_be_deleted_notified = send_mail( 

83 settings.CASTELLUM_EXPORT_SUBJECT_NOTIFICATION_SUBJECT, 

84 settings.CASTELLUM_EXPORT_SUBJECT_NOTIFICATION_BODY.format(url=url), 

85 settings.DEFAULT_FROM_EMAIL, 

86 [settings.CASTELLUM_DELETE_NOTIFICATION_TO], 

87 ) 

88 

89 if self.object.to_be_deleted and not self.object.to_be_deleted_notified: 

90 rel = reverse('subjects:detail', args=[self.object.pk]) 

91 url = self.request.build_absolute_uri(rel) 

92 

93 self.object.to_be_deleted_notified = send_mail( 

94 settings.CASTELLUM_DELETE_SUBJECT_NOTIFICATION_SUBJECT, 

95 settings.CASTELLUM_DELETE_SUBJECT_NOTIFICATION_BODY.format(url=url), 

96 settings.DEFAULT_FROM_EMAIL, 

97 [settings.CASTELLUM_DELETE_NOTIFICATION_TO], 

98 ) 

99 

100 # If a user explicitly removes the ``to_be_deleted`` they 

101 # expect that this task is done. If there are still reasons 

102 # for deletion there should be a new notificaition. 

103 if not self.object.to_be_deleted and self.object.to_be_deleted_notified: 

104 old = Subject.objects.get(pk=self.object.pk) 

105 if old.to_be_deleted: 

106 self.object.to_be_deleted_notified = False 

107 

108 return super().form_valid(form) 

109 

110 

111class BaseAdditionalInfoUpdateView(PermissionRequiredMixin, UpdateView): 

112 model = Subject 

113 fields = ( 

114 'note_hard_of_hearing', 

115 'note_difficult_to_understand', 

116 'note_abusive_language', 

117 'availability_monday', 

118 'availability_tuesday', 

119 'availability_wednesday', 

120 'availability_thursday', 

121 'availability_friday', 

122 'not_available_until', 

123 'source', 

124 ) 

125 permission_required = 'subjects.change_subject' 

126 template_name = 'subjects/subject_additional_info_form.html' 

127 

128 def get_notes_form(self): 

129 form_class = forms.modelform_factory(SubjectNote, fields=('content',)) 

130 return form_class(prefix='{prefix}') 

131 

132 def get_notes_formset(self): 

133 formset_class = forms.modelformset_factory( 

134 SubjectNote, fields=('content',), extra=0, can_delete=True 

135 ) 

136 kwargs = self.get_form_kwargs() 

137 kwargs.pop('instance') 

138 kwargs.pop('initial') 

139 return formset_class(queryset=SubjectNote.objects.filter(subject=self.object), **kwargs) 

140 

141 def get_context_data(self, **kwargs): 

142 context = super().get_context_data(**kwargs) 

143 if 'notes_formset' not in context: 143 ↛ 145line 143 didn't jump to line 145, because the condition on line 143 was never false

144 context['notes_formset'] = self.get_notes_formset() 

145 context['template_form'] = self.get_notes_form() 

146 return context 

147 

148 def post(self, request, *args, **kwargs): 

149 self.object = self.get_object() 

150 form = self.get_form() 

151 notes_formset = self.get_notes_formset() 

152 

153 if form.is_valid() and notes_formset.is_valid(): 

154 return self.form_valid(form, notes_formset) 

155 else: 

156 return self.form_invalid(form, notes_formset) 

157 

158 def form_invalid(self, form, notes_formset): 

159 return self.render_to_response( 

160 self.get_context_data(form=form, notes_formset=notes_formset) 

161 ) 

162 

163 def form_valid(self, form, notes_formset): 

164 response = super().form_valid(form) 

165 

166 for subform in notes_formset: 

167 subform.instance.subject = self.object 

168 notes_formset.save() 

169 

170 return response