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

30 

31from dateutil.relativedelta import relativedelta 

32 

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 

39 

40from ..forms import ExcludedStudiesForm 

41from ..forms import SessionForm 

42from ..mixins import StudyMixin 

43from ..models import Resource 

44from ..models import Study 

45from ..models import StudySession 

46 

47 

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

52 

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] 

64 

65 

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' 

80 

81 def get_readonly(self): 

82 return self.object.status == Study.FINISHED 

83 

84 def get_success_url(self): 

85 return reverse('studies:recruitmentsettings', kwargs={'pk': self.object.pk}) 

86 

87 def form_valid(self, form): 

88 messages.success(self.request, _('Data has been saved.')) 

89 return super().form_valid(form) 

90 

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

95 

96 

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' 

106 

107 def get_readonly(self): 

108 return self.object.status == Study.FINISHED 

109 

110 def get_success_url(self): 

111 return reverse('studies:excluded-studies', args=[self.object.pk]) 

112 

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 

124 

125 def form_valid(self, form): 

126 messages.success(self.request, _('Data has been saved.')) 

127 return super().form_valid(form) 

128 

129 

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' 

139 

140 def get_readonly(self): 

141 return self.object.status == Study.FINISHED 

142 

143 def get_success_url(self): 

144 return reverse('studies:exclusioncriteria', args=[self.object.pk]) 

145 

146 def form_valid(self, form): 

147 messages.success(self.request, _('Data has been saved.')) 

148 return super().form_valid(form) 

149 

150 

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' 

164 

165 def get_session_form_readonly(self, form): 

166 return self.get_readonly() or ( 

167 form.instance.appointment_set.exists() 

168 ) 

169 

170 def get_session_form(self): 

171 return SessionForm(prefix='{prefix}') 

172 

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 ) 

185 

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 

193 

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) 

202 

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 ) 

207 

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 

215 

216 def get_readonly(self): 

217 return self.object.status == Study.FINISHED 

218 

219 def get_success_url(self): 

220 return reverse('studies:sessions', args=[self.object.pk]) 

221 

222 

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' 

236 

237 def get_readonly(self): 

238 return self.object.status == Study.FINISHED 

239 

240 def get_success_url(self): 

241 return reverse('studies:mailsettings', args=[self.object.pk]) 

242 

243 def form_valid(self, form): 

244 messages.success(self.request, _('Data has been saved.')) 

245 return super().form_valid(form)