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/>.
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
30from castellum.castellum_auth.mixins import PermissionRequiredMixin
32from .forms import DataProtectionForm
33from .models import Subject
34from .models import SubjectNote
37class SubjectMixin:
38 """Use this on every view that represents a subject.
40 - set ``self.subject``
41 - check privacy level
42 """
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
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)
58 def get_context_data(self, **kwargs):
59 context = super().get_context_data(**kwargs)
60 context['subject'] = self.subject
61 return context
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'
70 def get_form_kwargs(self):
71 kwargs = super().get_form_kwargs()
72 kwargs['user'] = self.request.user
73 return kwargs
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)
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 )
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)
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 )
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
108 return super().form_valid(form)
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'
128 def get_notes_form(self):
129 form_class = forms.modelform_factory(SubjectNote, fields=('content',))
130 return form_class(prefix='{prefix}')
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)
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
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()
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)
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 )
163 def form_valid(self, form, notes_formset):
164 response = super().form_valid(form)
166 for subform in notes_formset:
167 subform.instance.subject = self.object
168 notes_formset.save()
170 return response