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.contrib import messages
25from django.db import models
26from django.urls import reverse
27from django.utils import timezone
28from django.utils.translation import gettext_lazy as _
29from django.views.generic import UpdateView
31from dateutil.relativedelta import relativedelta
33from castellum.castellum_auth.mixins import PermissionRequiredMixin
34from castellum.recruitment import filter_queries
35from castellum.recruitment.models import ParticipationRequest
36from castellum.recruitment.models import SubjectFilter
37from castellum.subjects.models import Subject
38from castellum.utils.views import ReadonlyMixin
40from ..forms import ExcludedStudiesForm
41from ..forms import SessionForm
42from ..mixins import StudyMixin
43from ..models import Resource
44from ..models import Study
45from ..models import StudySession
48def get_related_studies(study):
49 # if no filters exist, all active studies would be related
50 if not SubjectFilter.objects.filter(group__study=study).exists(): 50 ↛ 53line 50 didn't jump to line 53, because the condition on line 50 was never false
51 return Study.objects.none()
53 dt = timezone.now() - relativedelta(years=2)
54 related_prs = ParticipationRequest.objects.filter(
55 updated_at__gte=dt,
56 status=ParticipationRequest.INVITED,
57 subject__in=Subject.objects.filter(filter_queries.study(study))
58 )
59 return Study.objects\
60 .filter(participationrequest__in=related_prs)\
61 .annotate(count=models.Count('pk'))\
62 .exclude(pk=study.pk)\
63 .order_by('-count')[:5]
66class StudyRecruitmentSettingsUpdateView(
67 StudyMixin, PermissionRequiredMixin, ReadonlyMixin, UpdateView
68):
69 model = Study
70 fields = (
71 'advanced_filtering',
72 'recruitment_text',
73 'is_exclusive',
74 'complete_matches_only',
75 )
76 permission_required = 'studies.change_study'
77 template_name = 'studies/study_recruitmentsettings_form.html'
78 tab = 'recruitmentsettings'
79 subtab = 'update'
81 def get_readonly(self):
82 return self.object.status == Study.FINISHED
84 def get_success_url(self):
85 return reverse('studies:recruitmentsettings', kwargs={'pk': self.object.pk})
87 def form_valid(self, form):
88 messages.success(self.request, _('Data has been saved.'))
89 return super().form_valid(form)
91 def get_form_class(self):
92 if 'castellum.geofilters' in settings.INSTALLED_APPS: 92 ↛ 94line 92 didn't jump to line 94, because the condition on line 92 was never false
93 self.fields = (*self.fields, 'geo_filter')
94 return super().get_form_class()
97class StudyExcludedStudiesView(
98 StudyMixin, PermissionRequiredMixin, ReadonlyMixin, UpdateView
99):
100 model = Study
101 template_name = 'studies/study_excluded_studies.html'
102 form_class = ExcludedStudiesForm
103 permission_required = 'studies.change_study'
104 tab = 'recruitmentsettings'
105 subtab = 'excluded-studies'
107 def get_readonly(self):
108 return self.object.status == Study.FINISHED
110 def get_success_url(self):
111 return reverse('studies:excluded-studies', args=[self.object.pk])
113 def get_context_data(self, **kwargs):
114 context = super().get_context_data(**kwargs)
115 context['count'] = (
116 Subject.objects.filter(
117 filter_queries.study(self.object),
118 filter_queries.has_consent()
119 ).count()
120 )
121 context['total_count'] = Subject.objects.count()
122 context['related_studies'] = list(get_related_studies(self.object))
123 return context
125 def form_valid(self, form):
126 messages.success(self.request, _('Data has been saved.'))
127 return super().form_valid(form)
130class StudyExclusionCriteriaView(
131 StudyMixin, PermissionRequiredMixin, ReadonlyMixin, UpdateView
132):
133 model = Study
134 template_name = 'studies/study_exclusioncriteria.html'
135 fields = ('exclusion_criteria',)
136 permission_required = 'studies.change_study'
137 tab = 'recruitmentsettings'
138 subtab = 'exclusioncriteria'
140 def get_readonly(self):
141 return self.object.status == Study.FINISHED
143 def get_success_url(self):
144 return reverse('studies:exclusioncriteria', args=[self.object.pk])
146 def form_valid(self, form):
147 messages.success(self.request, _('Data has been saved.'))
148 return super().form_valid(form)
151class StudySessionsView(
152 StudyMixin, PermissionRequiredMixin, ReadonlyMixin, UpdateView
153):
154 model = Study
155 template_name = 'studies/study_sessions.html'
156 fields = (
157 'session_instructions',
158 'sessions_start',
159 'sessions_end',
160 )
161 permission_required = 'studies.change_study'
162 tab = 'recruitmentsettings'
163 subtab = 'sessions'
165 def get_session_form_readonly(self, form):
166 return self.get_readonly() or (
167 form.instance.appointment_set.exists()
168 )
170 def get_session_form(self):
171 return SessionForm(prefix='{prefix}')
173 def get_session_formset(self):
174 formset_class = forms.modelformset_factory(
175 StudySession, form=SessionForm, extra=0, can_delete=True
176 )
177 kwargs = self.get_form_kwargs()
178 study = kwargs.pop('instance', None)
179 kwargs.pop('readonly', False)
180 return formset_class(
181 queryset=StudySession.objects.filter(study=study),
182 form_kwargs={'readonly': self.get_session_form_readonly},
183 **kwargs
184 )
186 def get_context_data(self, **kwargs):
187 context = super().get_context_data(**kwargs)
188 if 'session_formset' not in context: 188 ↛ 190line 188 didn't jump to line 190, because the condition on line 188 was never false
189 context['session_formset'] = self.get_session_formset()
190 context["template_form"] = self.get_session_form()
191 context['has_resources'] = Resource.objects.exists()
192 return context
194 def post(self, request, *args, **kwargs):
195 self.object = self.get_object()
196 form = self.get_form()
197 session_formset = self.get_session_formset()
198 if form.is_valid() and session_formset.is_valid():
199 return self.form_valid(form, session_formset)
200 else:
201 return self.form_invalid(form, session_formset)
203 def form_invalid(self, form, session_formset):
204 return self.render_to_response(
205 self.get_context_data(form=form, session_formset=session_formset)
206 )
208 def form_valid(self, form, session_formset):
209 messages.success(self.request, _('Data has been saved.'))
210 response = super().form_valid(form)
211 for subform in session_formset:
212 subform.instance.study = form.instance
213 session_formset.save()
214 return response
216 def get_readonly(self):
217 return self.object.status == Study.FINISHED
219 def get_success_url(self):
220 return reverse('studies:sessions', args=[self.object.pk])
223class StudyMailSettingsView(
224 StudyMixin, PermissionRequiredMixin, ReadonlyMixin, UpdateView
225):
226 model = Study
227 template_name = 'studies/study_mailsettings.html'
228 fields = (
229 'mail_subject',
230 'mail_body',
231 'mail_reply_address',
232 )
233 permission_required = 'studies.change_study'
234 tab = 'recruitmentsettings'
235 subtab = 'mailsettings'
237 def get_readonly(self):
238 return self.object.status == Study.FINISHED
240 def get_success_url(self):
241 return reverse('studies:mailsettings', args=[self.object.pk])
243 def form_valid(self, form):
244 messages.success(self.request, _('Data has been saved.'))
245 return super().form_valid(form)