#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 9 16:10:06 2019
@author: Paolo Cozzi <cozzi@ibba.cnr.it>
"""
import logging
from django.utils import timezone
from django.template.defaultfilters import truncatechars
from common.constants import ERROR, NEED_REVISION, EMAIL_MAX_BODY_SIZE
from common.tasks import NotifyAdminTaskMixin
from uid.models import Submission
from validation.helpers import construct_validation_message
from zooma.tasks import AnnotateAll
from .helpers import send_message
# Get an instance of a logger
logger = logging.getLogger(__name__)
# HINT: should I move all this stuff into uid module?
[docs]class SubmissionTaskMixin():
"""A mixin to extend Task to support UID Submission objects"""
action = None
max_body_size = EMAIL_MAX_BODY_SIZE
[docs] def get_uid_submission(self, submission_id):
"""Get a UID Submission instance from an id
Args:
submission_id (int): the submission id
Returns:
:py:class:`Submission`: a UID submission instance
"""
return Submission.objects.get(pk=submission_id)
# extract a generic send_message for all modules which need it
[docs] def send_message(self, submission_obj, construct_message=False):
"""
Update submission.status and submission message using django
channels
Args:
submission_obj (uid.models.Submission): an UID submission
object
construct_message (bool): construct validation message or not
"""
if construct_message is True:
send_message(
submission_obj, construct_validation_message(submission_obj)
)
else:
send_message(submission_obj)
[docs] def update_submission_status(
self, submission_obj, status, message, construct_message=False):
"""Mark submission with status, then send message
Args:
submission_obj (uid.models.Submission): an UID submission
object
status (int): a :py:class:`common.constants.STATUSES` value
message (str): the message to send
construct_message (bool): construct validation message or not
"""
submission_obj.status = status
submission_obj.message = message
submission_obj.save()
# send async message
self.send_message(submission_obj, construct_message)
[docs] def mail_to_owner(self, submission_obj, subject, body):
# truncate message body if necessary
new_body = truncatechars(body, self.max_body_size) + "[truncated]"
# send mail to user
submission_obj.owner.email_user(subject, new_body)
[docs] def on_failure(self, exc, task_id, args, kwargs, einfo):
"""Override the default on_failure method"""
# call base class
super().on_failure(exc, task_id, args, kwargs, einfo)
# get submission object
if 'uid_submission_id' in kwargs:
submission_id = kwargs['uid_submission_id']
else:
submission_id = args[0]
submission_obj = self.get_uid_submission(submission_id)
# mark submission with ERROR and send message
self.update_submission_status(
submission_obj,
ERROR,
"Error in %s: %s" % (self.action, str(exc))
)
# send a mail to the user with the stacktrace (einfo)
subject = "Error in %s for submission %s" % (
self.action, submission_id)
body = (
"Something goes wrong with %s. Please report "
"this to InjectTool team\n\n %s" % (
self.action,
str(einfo))
)
self.mail_to_owner(submission_obj, subject, body)
[docs]class ImportGenericTaskMixin(SubmissionTaskMixin, NotifyAdminTaskMixin):
"""A mixing used to import datasource into UID"""
action = None
[docs] def run(self, submission_id):
"""a function to upload data into UID"""
logger.info(
"Start %s for submission: %s" % (self.action, submission_id))
# get a submission object (from SubmissionTaskMixin)
submission_obj = self.get_uid_submission(submission_id)
# upload data into UID with the proper method (defined in child class)
status = self.import_data_from_file(submission_obj)
# if something went wrong, uploaded_cryoweb has token the exception
# ad update submission.message field
if status is False:
message = "Error in %s" % (self.action)
logger.error(message)
# this a failure in my import, not the task itself
return message
else:
message = "%s completed for submission: %s" % (
self.action, submission_id)
# debug
logger.info(message)
# calling zooma tasks
annotate_task = AnnotateAll()
res = annotate_task.delay()
logger.info(
"Start zooma annotation with task %s" % res.task_id)
# always return something
return "success"
[docs]class BatchUpdateMixin(SubmissionTaskMixin):
"""Mixin to do batch update of fields to fix validation"""
item_cls = None
[docs] def batch_update(self, submission_id, ids, attribute):
for id_, value in ids.items():
if value == '' or value == 'None':
value = None
item_object = self.item_cls.objects.get(pk=id_)
if getattr(item_object, attribute) != value:
setattr(item_object, attribute, value)
# update name object
item_object.last_changed = timezone.now()
item_object.save()
# get a submission object (from SubmissionTaskMixin)
submission_obj = self.get_uid_submission(submission_id)
# mark submission with NEED_REVISION and send message
self.update_submission_status(
submission_obj,
NEED_REVISION,
"Data updated, try to rerun validation",
construct_message=True
)