1 | #!/usr/bin/env python3 |
||
2 | # -*- coding: utf-8 -*- |
||
3 | """ |
||
4 | Created on Mon Oct 29 15:33:34 2018 |
||
5 | |||
6 | @author: Paolo Cozzi <[email protected]> |
||
7 | """ |
||
8 | |||
9 | import logging |
||
10 | |||
11 | from functools import wraps |
||
12 | |||
13 | from django.contrib.auth.mixins import LoginRequiredMixin |
||
14 | from django.contrib import messages |
||
15 | from django.http import HttpResponseBadRequest |
||
16 | from django.utils import timezone |
||
17 | from django.utils.decorators import method_decorator |
||
18 | from django.shortcuts import redirect |
||
19 | from django.urls import reverse |
||
20 | from django.views.generic.base import TemplateView |
||
21 | |||
22 | from .constants import NEED_REVISION |
||
23 | |||
24 | # Get an instance of a logger |
||
25 | logger = logging.getLogger(__name__) |
||
26 | |||
27 | |||
28 | # a mixin to isolate user data |
||
29 | class OwnerMixin(LoginRequiredMixin): |
||
30 | def get_queryset(self): |
||
31 | """ |
||
32 | Filter base queryset relying on django authenticated sessions:: |
||
33 | |||
34 | from common.views import OwnerMixin |
||
35 | from django.views.generic import DetailView |
||
36 | |||
37 | class MyDetailView(OwnerMixin, DetailView): |
||
38 | def get_queryset(self): |
||
39 | # call OwnerMixin and DetailView super methods |
||
40 | qs = super(MyDetailView, self).get_queryset() |
||
41 | |||
42 | # add custom filter to queryset |
||
43 | |||
44 | # remeber to return the updated queryset to the caller |
||
45 | return qs |
||
46 | """ |
||
47 | |||
48 | qs = super(OwnerMixin, self).get_queryset() |
||
49 | logger.debug("Checking '%s' ownership for user '%s'" % ( |
||
50 | self.request.path, self.request.user)) |
||
51 | return qs.filter(owner=self.request.user) |
||
52 | |||
53 | |||
54 | class DetailMaterialMixin(OwnerMixin): |
||
55 | """A common DetailMixin for Material classes (Sample/Animal)""" |
||
56 | |||
57 | def get_context_data(self, **kwargs): |
||
58 | data = super().get_context_data(**kwargs) |
||
59 | |||
60 | # get a validationresult obj |
||
61 | if self.object.validationresult: |
||
62 | validation = self.object.validationresult |
||
63 | |||
64 | logger.debug( |
||
65 | "Found validationresult: %s->%s" % ( |
||
66 | validation, validation.messages)) |
||
67 | |||
68 | # I could have more messages in validation message. They could |
||
69 | # be werning or errors, validation.status (overall status) |
||
70 | # has no meaning here |
||
71 | for message in validation.messages: |
||
72 | if "Info:" in message: |
||
73 | messages.info( |
||
74 | request=self.request, |
||
75 | message=message, |
||
76 | extra_tags="alert alert-dismissible alert-info") |
||
77 | |||
78 | elif "Warning:" in message: |
||
79 | messages.warning( |
||
80 | request=self.request, |
||
81 | message=message, |
||
82 | extra_tags="alert alert-dismissible alert-warning") |
||
83 | |||
84 | # the other validation messages are threated like errors |
||
85 | else: |
||
86 | messages.error( |
||
87 | request=self.request, |
||
88 | message=message, |
||
89 | extra_tags="alert alert-dismissible alert-danger") |
||
90 | |||
91 | # cicle for a message in validation.messages |
||
92 | |||
93 | # condition: I have validation result |
||
94 | return data |
||
95 | |||
96 | |||
97 | class UpdateMaterialMixin(OwnerMixin): |
||
98 | """A common UpdateMixin for Material classes (Sample/Animal)""" |
||
99 | |||
100 | # override this attribute with a real validation class |
||
101 | validationresult_class = None |
||
102 | |||
103 | View Code Duplication | def dispatch(self, request, *args, **kwargs): |
|
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
104 | handler = super(UpdateMaterialMixin, self).dispatch( |
||
105 | request, *args, **kwargs) |
||
106 | |||
107 | # here I've done get_queryset. Check for submission status |
||
108 | if hasattr(self, "object") and not self.object.can_edit(): |
||
109 | message = "Cannot edit %s: submission status is: %s" % ( |
||
110 | self.object, self.object.submission.get_status_display()) |
||
111 | |||
112 | logger.warning(message) |
||
113 | messages.warning( |
||
114 | request=self.request, |
||
115 | message=message, |
||
116 | extra_tags="alert alert-dismissible alert-warning") |
||
117 | |||
118 | return redirect(self.object.get_absolute_url()) |
||
119 | |||
120 | return handler |
||
121 | |||
122 | # add the request to the kwargs |
||
123 | # https://chriskief.com/2012/12/18/django-modelform-formview-and-the-request-object/ |
||
124 | def get_form_kwargs(self): |
||
125 | kwargs = super(UpdateMaterialMixin, self).get_form_kwargs() |
||
126 | kwargs['request'] = self.request |
||
127 | return kwargs |
||
128 | |||
129 | # override UpdateView.get_form() method. Initialize a form object |
||
130 | # and pass submission into it. Self object is the animal object I |
||
131 | # want to update |
||
132 | def get_form(self): |
||
133 | return self.form_class( |
||
134 | self.object, **self.get_form_kwargs()) |
||
135 | |||
136 | # override form valid istance |
||
137 | def form_valid(self, form): |
||
138 | self.object = form.save(commit=False) |
||
139 | |||
140 | # HINT: validate object? |
||
141 | |||
142 | # setting statuses and messages |
||
143 | self.object.status = NEED_REVISION |
||
144 | self.object.last_changed = timezone.now() |
||
145 | self.object.save() |
||
146 | |||
147 | if self.object.validationresult: |
||
148 | validationresult = self.object.validationresult |
||
149 | else: |
||
150 | validationresult = self.validationresult_class() |
||
151 | self.object.validationresult = validationresult |
||
152 | |||
153 | validationresult.messages = [ |
||
154 | 'Info: Data has changed, validation has to be called'] |
||
155 | validationresult.status = "Info" |
||
156 | validationresult.save() |
||
157 | |||
158 | # Override submission status |
||
159 | self.object.submission.status = NEED_REVISION |
||
160 | self.object.submission.message = ( |
||
161 | "Data has changed, validation has to be called") |
||
162 | self.object.submission.save() |
||
163 | |||
164 | # save object and return HttpResponseRedirect(self.get_success_url()) |
||
165 | return super().form_valid(form) |
||
166 | |||
167 | |||
168 | class DeleteMaterialMixin(OwnerMixin): |
||
169 | """A common DeleteMixin for Material classes (Sample/Animal)""" |
||
170 | |||
171 | View Code Duplication | def dispatch(self, request, *args, **kwargs): |
|
0 ignored issues
–
show
|
|||
172 | handler = super(DeleteMaterialMixin, self).dispatch( |
||
173 | request, *args, **kwargs) |
||
174 | |||
175 | # here I've done get_queryset. Check for submission status |
||
176 | if hasattr(self, "object") and not self.object.can_delete(): |
||
177 | message = "Cannot delete %s: submission status is: %s" % ( |
||
178 | self.object, self.object.submission.get_status_display()) |
||
179 | |||
180 | logger.warning(message) |
||
181 | messages.warning( |
||
182 | request=self.request, |
||
183 | message=message, |
||
184 | extra_tags="alert alert-dismissible alert-warning") |
||
185 | |||
186 | return redirect(self.object.get_absolute_url()) |
||
187 | |||
188 | return handler |
||
189 | |||
190 | def get_success_url(self): |
||
191 | return reverse( |
||
192 | 'submissions:edit', |
||
193 | kwargs={'pk': self.object.submission.pk} |
||
194 | ) |
||
195 | |||
196 | |||
197 | class ListMaterialMixin(OwnerMixin): |
||
198 | """A common ListMixin for Material classes (Sample/Animal)""" |
||
199 | |||
200 | pass |
||
201 | |||
202 | |||
203 | def ajax_required(f): |
||
204 | # https://stackoverflow.com/a/52271410/4385116 |
||
205 | # use a decorator to wrap a function and avoid |
||
206 | # 'functools.partial' object has no attribute '__name__'. |
||
207 | @wraps(f) |
||
208 | def wrap(request, *args, **kwargs): |
||
209 | if not request.is_ajax(): |
||
210 | return HttpResponseBadRequest() |
||
211 | |||
212 | return f(request, *args, **kwargs) |
||
213 | |||
214 | return wrap |
||
215 | |||
216 | |||
217 | # https://stackoverflow.com/a/13409704/4385116 |
||
218 | class AjaxTemplateView(TemplateView): |
||
219 | @method_decorator(ajax_required) |
||
220 | def dispatch(self, *args, **kwargs): |
||
221 | return super(AjaxTemplateView, self).dispatch(*args, **kwargs) |
||
222 | |||
223 | |||
224 | class FormInvalidMixin(): |
||
225 | def get_form_kwargs(self): |
||
226 | kwargs = super(FormInvalidMixin, self).get_form_kwargs() |
||
227 | kwargs['request'] = self.request |
||
228 | return kwargs |
||
229 | |||
230 | def form_invalid(self, form): |
||
231 | messages.error( |
||
232 | self.request, |
||
233 | message="Please correct the errors below", |
||
234 | extra_tags="alert alert-dismissible alert-danger") |
||
235 | |||
236 | return super(FormInvalidMixin, self).form_invalid(form) |
||
237 |