Completed
Pull Request — master (#41)
by
unknown
05:58
created

SubmissionValidationSummaryFixErrorsView.get_context_data()   A

Complexity

Conditions 2

Size

Total Lines 26
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 26
rs 9.4
c 0
b 0
f 0
cc 2
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
import ast
11
import re
12
from django.core.exceptions import ObjectDoesNotExist
13
14
from django.contrib import messages
15
from django.contrib.auth.mixins import LoginRequiredMixin
16
from django.db.models import Q
17
from django.http import HttpResponseRedirect
18
from django.views.generic import (
19
    CreateView, DetailView, ListView, UpdateView, DeleteView)
20
from django.shortcuts import get_object_or_404, redirect
21
from django.urls import reverse_lazy, reverse
22
23
from common.constants import (
24
    WAITING, ERROR, SUBMITTED, NEED_REVISION, CRYOWEB_TYPE, CRB_ANIM_TYPE,
25
    VALIDATION_MESSAGES, VALIDATION_MESSAGES_ATTRIBUTES)
26
from common.helpers import get_deleted_objects
27
from common.views import OwnerMixin
28
from crbanim.tasks import ImportCRBAnimTask
29
from cryoweb.tasks import import_from_cryoweb
30
31
from image_app.models import Submission, Name, Animal, Sample
32
from excel.tasks import ImportTemplateTask
33
34
from validation.helpers import construct_validation_message
35
from validation.models import ValidationSummary
36
from animals.tasks import BatchUpdateAnimals
37
from samples.tasks import BatchUpdateSamples
38
39
from .forms import SubmissionForm, ReloadForm
40
41
# Get an instance of a logger
42
logger = logging.getLogger(__name__)
43
44
45
class CreateSubmissionView(LoginRequiredMixin, CreateView):
46
    form_class = SubmissionForm
47
    model = Submission
48
49
    # template name is derived from model position and views type.
50
    # in this case, ir will be 'image_app/submission_form.html' so
51
    # i need to clearly specify it
52
    template_name = "submissions/submission_form.html"
53
54
    def form_invalid(self, form):
55
        messages.error(
56
            self.request,
57
            message="Please correct the errors below",
58
            extra_tags="alert alert-dismissible alert-danger")
59
60
        return super(CreateSubmissionView, self).form_invalid(form)
61
62
    # add user to this object
63
    def form_valid(self, form):
64
        self.object = form.save(commit=False)
65
        self.object.owner = self.request.user
66
67
        # I will have a different loading function accordingly with data type
68
        if self.object.datasource_type == CRYOWEB_TYPE:
69
            # update object and force status
70
            self.object.message = "waiting for data loading"
71
            self.object.status = WAITING
72
            self.object.save()
73
74
            # a valid submission start a task
75
            res = import_from_cryoweb.delay(self.object.pk)
76
            logger.info(
77
                "Start cryoweb importing process with task %s" % res.task_id)
78
79
        # I will have a different loading function accordingly with data type
80
        elif self.object.datasource_type == CRB_ANIM_TYPE:
81
            # update object and force status
82
            self.object.message = "waiting for data loading"
83
            self.object.status = WAITING
84
            self.object.save()
85
86
            # create a task
87
            my_task = ImportCRBAnimTask()
88
89
            # a valid submission start a task
90
            res = my_task.delay(self.object.pk)
91
            logger.info(
92
                "Start crbanim importing process with task %s" % res.task_id)
93
94
        else:
95
            # update object and force status
96
            self.object.message = "waiting for data loading"
97
            self.object.status = WAITING
98
            self.object.save()
99
100
            # create a task
101
            my_task = ImportTemplateTask()
102
103
            # a valid submission start a task
104
            res = my_task.delay(self.object.pk)
105
            logger.info(
106
                "Start template importing process with task %s" % res.task_id)
107
108
        # a redirect to self.object.get_absolute_url()
109
        return HttpResponseRedirect(self.get_success_url())
110
111
112
class MessagesSubmissionMixin(object):
113
    """Display messages in SubmissionViews"""
114
115
    # https://stackoverflow.com/a/45696442
116
    def get_context_data(self, **kwargs):
117
        data = super().get_context_data(**kwargs)
118
119
        # get the submission message
120
        message = self.submission.message
121
122
        # check if data are loaded or not
123
        if self.submission.status in [WAITING, SUBMITTED]:
124
            messages.warning(
125
                request=self.request,
126
                message=message,
127
                extra_tags="alert alert-dismissible alert-warning")
128
129
        elif self.submission.status in [ERROR, NEED_REVISION]:
130
            messages.error(
131
                request=self.request,
132
                message=message,
133
                extra_tags="alert alert-dismissible alert-danger")
134
135
        elif message is not None and message != '':
136
            messages.info(
137
                request=self.request,
138
                message=message,
139
                extra_tags="alert alert-dismissible alert-info")
140
141
        return data
142
143
144
class DetailSubmissionView(MessagesSubmissionMixin, OwnerMixin, DetailView):
145
    model = Submission
146
    template_name = "submissions/submission_detail.html"
147
148
    def get_context_data(self, **kwargs):
149
        # pass self.object to a new submission attribute in order to call
150
        # MessagesSubmissionMixin.get_context_data()
151
        self.submission = self.object
152
153
        # Call the base implementation first to get a context
154
        context = super(DetailSubmissionView, self).get_context_data(**kwargs)
155
156
        # add submission report to context
157
        validation_summary = construct_validation_message(self.submission)
158
159
        # HINT: is this computational intensive?
160
        context["validation_summary"] = validation_summary
161
162
        return context
163
164
165
class SubmissionValidationSummaryView(OwnerMixin, DetailView):
166
    model = Submission
167
    template_name = "submissions/submission_validation_summary.html"
168
169
    def get_context_data(self, **kwargs):
170
        context = super().get_context_data(**kwargs)
171
        summary_type = self.kwargs['type']
172
        try:
173
            context['validation_summary'] = self.object.validationsummary_set\
174
                .get(type=summary_type)
175
        except ObjectDoesNotExist:
176
            context['validation_summary'] = None
177
        context['submission'] = Submission.objects.get(pk=self.kwargs['pk'])
178
        return context
179
180
181
class SubmissionValidationSummaryFixErrorsView(OwnerMixin, ListView):
182
    template_name = "submissions/submission_validation_summary_fix_errors.html"
183
184
    def get_queryset(self):
185
        ids = list()
186
        self.summary_type = self.kwargs['type']
187
        self.submission = Submission.objects.get(pk=self.kwargs['pk'])
188
        self.validation_summary = ValidationSummary.objects.get(
189
            submission=self.submission, type=self.summary_type)
190
        self.request_message = self.kwargs['msg']
191
        for message in self.validation_summary.messages:
192
            message = ast.literal_eval(message)
193
            if message['message'] == self.request_message:
194
                ids = message['ids']
195
                self.offending_column = message['offending_column']
196
        if self.summary_type == 'animal':
197
            return Animal.objects.filter(id__in=ids)
198
        elif self.summary_type == 'sample':
199
            return Sample.objects.filter(id__in=ids)
200
201
    def get_context_data(self, **kwargs):
202
        # Call the base implementation first to get a context
203
        context = super(
204
            SubmissionValidationSummaryFixErrorsView, self
205
        ).get_context_data(**kwargs)
206
207
        # add submission to context
208
        context["message"] = self.request_message
209
        context["type"] = self.summary_type
210
        context["offending_column"] = self.offending_column
211
        if re.search(VALIDATION_MESSAGES['coordinate_check'],
212
                     self.request_message):
213
            type_of_check = f"coordinate_check_{self.summary_type}"
214
            context['attributes_to_show'] = VALIDATION_MESSAGES_ATTRIBUTES[
215
                type_of_check]['attributes_to_show']
216
            context['attributes_to_edit'] = VALIDATION_MESSAGES_ATTRIBUTES[
217
                type_of_check]['attributes_to_edit']
218
            context['error_type'] = 'coordinate_check'
219
        # TODO add checks for other messages
220
        else:
221
            context['attributes_to_show'] = []
222
            context['attributes_to_edit'] = []
223
            context['error_type'] = 'error'
224
        context['submission'] = self.submission
225
226
        return context
227
228
229
# a detail view since I need to operate on a submission object
230
# HINT: rename to a more informative name?
231
class EditSubmissionView(MessagesSubmissionMixin, OwnerMixin, ListView):
232
    template_name = "submissions/submission_edit.html"
233
    paginate_by = 10
234
235
    def get_queryset(self):
236
        """Subsetting names relying submission id"""
237
        self.submission = get_object_or_404(
238
            Submission,
239
            pk=self.kwargs['pk'],
240
            owner=self.request.user)
241
242
        # unknown animals should be removed from a submission. They have no
243
        # data in animal table nor sample
244
        return Name.objects.select_related(
245
                "validationresult",
246
                "animal",
247
                "sample").filter(
248
            Q(submission=self.submission) & (
249
                Q(animal__isnull=False) | Q(sample__isnull=False))
250
            ).order_by('id')
251
252 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...
253
        handler = super(EditSubmissionView, self).dispatch(
254
                request, *args, **kwargs)
255
256
        # here I've done get_queryset. Check for submission status
257
        if hasattr(self, "submission") and not self.submission.can_edit():
258
            message = "Cannot edit submission: current status is: %s" % (
259
                    self.submission.get_status_display())
260
261
            logger.warning(message)
262
            messages.warning(
263
                request=self.request,
264
                message=message,
265
                extra_tags="alert alert-dismissible alert-warning")
266
267
            return redirect(self.submission.get_absolute_url())
268
269
        return handler
270
271
    def get_context_data(self, **kwargs):
272
        # Call the base implementation first to get a context
273
        context = super(EditSubmissionView, self).get_context_data(**kwargs)
274
275
        # add submission to context
276
        context["submission"] = self.submission
277
278
        return context
279
280
281
class ListSubmissionsView(OwnerMixin, ListView):
282
    model = Submission
283
    template_name = "submissions/submission_list.html"
284
    ordering = ['-created_at']
285
    paginate_by = 10
286
287
288
class ReloadSubmissionView(OwnerMixin, UpdateView):
289
    form_class = ReloadForm
290
    model = Submission
291
    template_name = 'submissions/submission_reload.html'
292
293
    def form_invalid(self, form):
294
        messages.error(
295
            self.request,
296
            message="Please correct the errors below",
297
            extra_tags="alert alert-dismissible alert-danger")
298
299
        return super(ReloadSubmissionView, self).form_invalid(form)
300
301
    def form_valid(self, form):
302
        self.object = form.save(commit=False)
303
304
        # update object and force status
305
        self.object.message = "waiting for data loading"
306
        self.object.status = WAITING
307
        self.object.save()
308
309
        # HINT: can I change datasource type?
310
311
        # call the proper method
312
        if self.object.datasource_type == CRYOWEB_TYPE:
313
            # a valid submission start a task
314
            res = import_from_cryoweb.delay(self.object.pk)
315
            logger.info(
316
                "Start cryoweb reload process with task %s" % res.task_id)
317
318
        elif self.object.datasource_type == CRB_ANIM_TYPE:
319
            # a valid submission start a task
320
            my_task = ImportCRBAnimTask()
321
322
            # a valid submission start a task
323
            res = my_task.delay(self.object.pk)
324
            logger.info(
325
                "Start crbanim reload process with task %s" % res.task_id)
326
327
        else:
328
            # a valid submission start a task
329
            my_task = ImportTemplateTask()
330
331
            # a valid submission start a task
332
            res = my_task.delay(self.object.pk)
333
            logger.info(
334
                "Start template reload process with task %s" % res.task_id)
335
336
        # a redirect to self.object.get_absolute_url()
337
        return HttpResponseRedirect(self.get_success_url())
338
339
340
class DeleteSubmissionView(OwnerMixin, DeleteView):
341
    model = Submission
342
    template_name = "submissions/submission_confirm_delete.html"
343
    success_url = reverse_lazy('image_app:dashboard')
344
345 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...
346
        handler = super(DeleteSubmissionView, self).dispatch(
347
                request, *args, **kwargs)
348
349
        # here I've done get_queryset. Check for submission status
350
        if hasattr(self, "object") and not self.object.can_edit():
351
            message = "Cannot delete %s: submission status is: %s" % (
352
                    self.object, self.object.get_status_display())
353
354
            logger.warning(message)
355
            messages.warning(
356
                request=self.request,
357
                message=message,
358
                extra_tags="alert alert-dismissible alert-warning")
359
360
            return redirect(self.object.get_absolute_url())
361
362
        return handler
363
364
    # https://stackoverflow.com/a/39533619/4385116
365
    def get_context_data(self, **kwargs):
366
        # determining related objects
367
        # TODO: move this to a custom AJAX call
368
        context = super().get_context_data(**kwargs)
369
370
        deletable_objects, model_count, protected = get_deleted_objects(
371
            [self.object])
372
373
        # get only sample and animals from model_count
374
        info_deleted = {}
375
376
        items = ['animals', 'samples']
377
378
        for item in items:
379
            if item in model_count:
380
                info_deleted[item] = model_count[item]
381
382
        # add info to context
383
        context['info_deleted'] = dict(info_deleted).items()
384
385
        return context
386
387
    # https://ccbv.co.uk/projects/Django/1.11/django.views.generic.edit/DeleteView/#delete
388
    def delete(self, request, *args, **kwargs):
389
        """
390
        Add a message after calling base delete method
391
        """
392
393
        httpresponseredirect = super().delete(request, *args, **kwargs)
394
395
        message = "Submission %s was successfully deleted" % self.object.title
396
        logger.info(message)
397
398
        messages.info(
399
            request=self.request,
400
            message=message,
401
            extra_tags="alert alert-dismissible alert-info")
402
403
        return httpresponseredirect
404
405
406
def fix_validation(request, pk, record_type, error):
407
    # Fetch all required ids from input names and use it as keys
408
    keys_to_fix = dict()
409
    for key_to_fix in request.POST:
410
        if 'to_edit' in key_to_fix:
411
            keys_to_fix[int(re.search('to_edit(.*)', key_to_fix).groups()[0])] \
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable int does not seem to be defined.
Loading history...
412
                = request.POST[key_to_fix]
413
414
    submission = Submission.objects.get(pk=pk)
415
    submission.message = "waiting for data updating"
416
    submission.status = WAITING
417
    submission.save()
418
419
    # Update validation summary
420
    summary_obj, created = ValidationSummary.objects.get_or_create(
421
        submission=submission, type=record_type)
422
    summary_obj.submission = submission
423
    summary_obj.pass_count = 0
424
    summary_obj.warning_count = 0
425
    summary_obj.error_count = 0
426
    summary_obj.issues_count = 0
427
    summary_obj.validation_known_count = 0
428
    summary_obj.messages = list()
429
    summary_obj.save()
430
431
    # create a task
432
    if record_type == 'animal':
433
        my_task = BatchUpdateAnimals()
434
        attribute_name = VALIDATION_MESSAGES_ATTRIBUTES[f"{error}_animal"][
435
            'attributes_to_edit'][0]
436
    elif record_type == 'sample':
437
        my_task = BatchUpdateSamples()
438
        attribute_name = VALIDATION_MESSAGES_ATTRIBUTES[f"{error}_sample"][
439
            'attributes_to_edit'][0]
440
    else:
441
        return HttpResponseRedirect(reverse('submissions:detail', args=(pk,)))
442
443
    # a valid submission start a task
444
    res = my_task.delay(pk, keys_to_fix, attribute_name)
445
    logger.info(
446
        "Start fix validation process with task %s" % res.task_id)
447
    return HttpResponseRedirect(reverse('submissions:detail', args=(pk,)))
448