Passed
Pull Request — master (#30)
by Paolo
01:17
created

DetailSubmissionView.get_context_data()   A

Complexity

Conditions 1

Size

Total Lines 15
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 15
rs 10
c 0
b 0
f 0
cc 1
nop 2
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Tue Jul 24 15:49:23 2018
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import logging
10
from django.core.exceptions import ObjectDoesNotExist
11
12
from django.contrib import messages
13
from django.contrib.auth.mixins import LoginRequiredMixin
14
from django.db.models import Q
15
from django.http import HttpResponseRedirect
16
from django.views.generic import (
17
    CreateView, DetailView, ListView, UpdateView, DeleteView)
18
from django.shortcuts import get_object_or_404, redirect
19
from django.urls import reverse_lazy
20
21
from common.constants import (
22
    WAITING, ERROR, SUBMITTED, NEED_REVISION, CRYOWEB_TYPE, CRB_ANIM_TYPE)
23
from common.helpers import get_deleted_objects
24
from common.views import OwnerMixin
25
from cryoweb.tasks import import_from_cryoweb
26
from image_app.models import Submission, Name, ValidationSummary
27
from crbanim.tasks import ImportCRBAnimTask
28
from validation.helpers import ValidationSummary as VS
29
30
from .forms import SubmissionForm, ReloadForm
31
32
# Get an instance of a logger
33
logger = logging.getLogger(__name__)
34
35
36
class CreateSubmissionView(LoginRequiredMixin, CreateView):
37
    form_class = SubmissionForm
38
    model = Submission
39
40
    # template name is derived from model position and views type.
41
    # in this case, ir will be 'image_app/submission_form.html' so
42
    # i need to clearly specify it
43
    template_name = "submissions/submission_form.html"
44
45
    def form_invalid(self, form):
46
        messages.error(
47
            self.request,
48
            message="Please correct the errors below",
49
            extra_tags="alert alert-dismissible alert-danger")
50
51
        return super(CreateSubmissionView, self).form_invalid(form)
52
53
    # add user to this object
54
    def form_valid(self, form):
55
        self.object = form.save(commit=False)
56
        self.object.owner = self.request.user
57
58
        # I will have a different loading function accordingly with data type
59
        if self.object.datasource_type == CRYOWEB_TYPE:
60
            # update object and force status
61
            self.object.message = "waiting for data loading"
62
            self.object.status = WAITING
63
            self.object.save()
64
65
            # a valid submission start a task
66
            res = import_from_cryoweb.delay(self.object.pk)
67
            logger.info(
68
                "Start cryoweb importing process with task %s" % res.task_id)
69
70
        # I will have a different loading function accordingly with data type
71
        elif self.object.datasource_type == CRB_ANIM_TYPE:
72
            # update object and force status
73
            self.object.message = "waiting for data loading"
74
            self.object.status = WAITING
75
            self.object.save()
76
77
            # create a task
78
            my_task = ImportCRBAnimTask()
79
80
            # a valid submission start a task
81
            res = my_task.delay(self.object.pk)
82
            logger.info(
83
                "Start crbanim importing process with task %s" % res.task_id)
84
85
        else:
86
            message = "{datasource} import is not implemented".format(
87
                datasource=self.object.get_datasource_type_display())
88
89
            self.object.message = message
90
            self.object.status = ERROR
91
            self.object.save()
92
93
            logger.error(message)
94
95
        # a redirect to self.object.get_absolute_url()
96
        return HttpResponseRedirect(self.get_success_url())
97
98
99
class MessagesSubmissionMixin(object):
100
    """Display messages in SubmissionViews"""
101
102
    # https://stackoverflow.com/a/45696442
103
    def get_context_data(self, **kwargs):
104
        data = super().get_context_data(**kwargs)
105
106
        # get the submission message
107
        message = self.submission.message
108
109
        # check if data are loaded or not
110
        if self.submission.status in [WAITING, SUBMITTED]:
111
            messages.warning(
112
                request=self.request,
113
                message=message,
114
                extra_tags="alert alert-dismissible alert-warning")
115
116
        elif self.submission.status in [ERROR, NEED_REVISION]:
117
            messages.error(
118
                request=self.request,
119
                message=message,
120
                extra_tags="alert alert-dismissible alert-danger")
121
122
        elif message is not None and message != '':
123
            messages.info(
124
                request=self.request,
125
                message=message,
126
                extra_tags="alert alert-dismissible alert-info")
127
128
        return data
129
130
131
class DetailSubmissionView(MessagesSubmissionMixin, OwnerMixin, DetailView):
132
    model = Submission
133
    template_name = "submissions/submission_detail.html"
134
135
    def get_context_data(self, **kwargs):
136
        # pass self.object to a new submission attribute in order to call
137
        # MessagesSubmissionMixin.get_context_data()
138
        self.submission = self.object
139
140
        # Call the base implementation first to get a context
141
        context = super(DetailSubmissionView, self).get_context_data(**kwargs)
142
143
        # add submission report to context
144
        validation_summary = VS(self.submission)
145
146
        # HINT: is this computational intensive?
147
        context["validation_summary"] = validation_summary
148
149
        return context
150
151
152
class SubmissionValidationSummaryView(OwnerMixin, DetailView):
153
    model = Submission
154
    template_name = "submissions/submission_validation_summary.html"
155
156
    def get_context_data(self, **kwargs):
157
        context = super(SubmissionValidationSummaryView, self).get_context_data(
158
            **kwargs)
159
        summary_type = ''
160
        if self.kwargs['type'] == 'animals':
161
            summary_type = 'animal'
162
        elif self.kwargs['type'] == 'samples':
163
            summary_type = 'sample'
164
        try:
165
            context['validation_summary'] = self.object.validationsummary_set\
166
                .get(type=summary_type)
167
        except ObjectDoesNotExist:
168
            context['validation_summary'] = None
169
        return context
170
171
172
# a detail view since I need to operate on a submission object
173
# HINT: rename to a more informative name?
174
class EditSubmissionView(MessagesSubmissionMixin, OwnerMixin, ListView):
175
    template_name = "submissions/submission_edit.html"
176
    paginate_by = 10
177
178
    def get_queryset(self):
179
        """Subsetting names relying submission id"""
180
        self.submission = get_object_or_404(
181
            Submission,
182
            pk=self.kwargs['pk'],
183
            owner=self.request.user)
184
185
        # unknown animals should be removed from a submission. They have no
186
        # data in animal table nor sample
187
        return Name.objects.select_related(
188
                "validationresult",
189
                "animal",
190
                "sample").filter(
191
            Q(submission=self.submission) & (
192
                Q(animal__isnull=False) | Q(sample__isnull=False))
193
            ).order_by('id')
194
195 View Code Duplication
    def dispatch(self, request, *args, **kwargs):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
196
        handler = super(EditSubmissionView, self).dispatch(
197
                request, *args, **kwargs)
198
199
        # here I've done get_queryset. Check for submission status
200
        if hasattr(self, "submission") and not self.submission.can_edit():
201
            message = "Cannot edit submission: current status is: %s" % (
202
                    self.submission.get_status_display())
203
204
            logger.warning(message)
205
            messages.warning(
206
                request=self.request,
207
                message=message,
208
                extra_tags="alert alert-dismissible alert-warning")
209
210
            return redirect(self.submission.get_absolute_url())
211
212
        return handler
213
214
    def get_context_data(self, **kwargs):
215
        # Call the base implementation first to get a context
216
        context = super(EditSubmissionView, self).get_context_data(**kwargs)
217
218
        # add submission to context
219
        context["submission"] = self.submission
220
221
        return context
222
223
224
class ListSubmissionsView(OwnerMixin, ListView):
225
    model = Submission
226
    template_name = "submissions/submission_list.html"
227
    ordering = ['-created_at']
228
    paginate_by = 10
229
230
231
class ReloadSubmissionView(OwnerMixin, UpdateView):
232
    form_class = ReloadForm
233
    model = Submission
234
    template_name = 'submissions/submission_reload.html'
235
236
    def form_invalid(self, form):
237
        messages.error(
238
            self.request,
239
            message="Please correct the errors below",
240
            extra_tags="alert alert-dismissible alert-danger")
241
242
        return super(ReloadSubmissionView, self).form_invalid(form)
243
244
    def form_valid(self, form):
245
        self.object = form.save(commit=False)
246
247
        # update object and force status
248
        self.object.message = "waiting for data loading"
249
        self.object.status = WAITING
250
        self.object.save()
251
252
        # HINT: can I change datasource type?
253
254
        # call the proper method
255
        if self.object.datasource_type == CRYOWEB_TYPE:
256
            # a valid submission start a task
257
            res = import_from_cryoweb.delay(self.object.pk)
258
            logger.info(
259
                "Start cryoweb reload process with task %s" % res.task_id)
260
261
        elif self.object.datasource_type == CRB_ANIM_TYPE:
262
            # a valid submission start a task
263
            my_task = ImportCRBAnimTask()
264
265
            # a valid submission start a task
266
            res = my_task.delay(self.object.pk)
267
            logger.info(
268
                "Start crbanim reload process with task %s" % res.task_id)
269
270
        else:
271
            # this is not an invalid form. Template is not implemented
272
            # it is a error in our application
273
            # TODO: call the proper method
274
            message = "{datasource} reload is not implemented".format(
275
                datasource=self.object.get_datasource_type_display())
276
277
            messages.error(
278
                self.request,
279
                message,
280
                extra_tags="alert alert-dismissible alert-danger")
281
282
            # set an error message to this submission
283
            self.object.message = message
284
            self.object.status = ERROR
285
            self.object.save()
286
287
        # a redirect to self.object.get_absolute_url()
288
        return HttpResponseRedirect(self.get_success_url())
289
290
291
class DeleteSubmissionView(OwnerMixin, DeleteView):
292
    model = Submission
293
    template_name = "submissions/submission_confirm_delete.html"
294
    success_url = reverse_lazy('image_app:dashboard')
295
296 View Code Duplication
    def dispatch(self, request, *args, **kwargs):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
297
        handler = super(DeleteSubmissionView, self).dispatch(
298
                request, *args, **kwargs)
299
300
        # here I've done get_queryset. Check for submission status
301
        if hasattr(self, "object") and not self.object.can_edit():
302
            message = "Cannot delete %s: submission status is: %s" % (
303
                    self.object, self.object.get_status_display())
304
305
            logger.warning(message)
306
            messages.warning(
307
                request=self.request,
308
                message=message,
309
                extra_tags="alert alert-dismissible alert-warning")
310
311
            return redirect(self.object.get_absolute_url())
312
313
        return handler
314
315
    # https://stackoverflow.com/a/39533619/4385116
316
    def get_context_data(self, **kwargs):
317
        # determining related objects
318
        # TODO: move this to a custom AJAX call
319
        context = super().get_context_data(**kwargs)
320
321
        deletable_objects, model_count, protected = get_deleted_objects(
322
            [self.object])
323
324
        # get only sample and animals from model_count
325
        info_deleted = {}
326
327
        items = ['animals', 'samples']
328
329
        for item in items:
330
            if item in model_count:
331
                info_deleted[item] = model_count[item]
332
333
        # add info to context
334
        context['info_deleted'] = dict(info_deleted).items()
335
336
        return context
337
338
    # https://ccbv.co.uk/projects/Django/1.11/django.views.generic.edit/DeleteView/#delete
339
    def delete(self, request, *args, **kwargs):
340
        """
341
        Add a message after calling base delete method
342
        """
343
344
        httpresponseredirect = super().delete(request, *args, **kwargs)
345
346
        message = "Submission %s was successfully deleted" % self.object.title
347
        logger.info(message)
348
349
        messages.info(
350
            request=self.request,
351
            message=message,
352
            extra_tags="alert alert-dismissible alert-info")
353
354
        return httpresponseredirect
355