Source code for beat.web.experiments.models.block

#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

###############################################################################
#                                                                             #
# Copyright (c) 2017 Idiap Research Institute, http://www.idiap.ch/           #
# Contact: beat.support@idiap.ch                                              #
#                                                                             #
# This file is part of the beat.web module of the BEAT platform.              #
#                                                                             #
# Commercial License Usage                                                    #
# Licensees holding valid commercial BEAT licenses may use this file in       #
# accordance with the terms contained in a written agreement between you      #
# and Idiap. For further information contact tto@idiap.ch                     #
#                                                                             #
# Alternatively, this file may be used under the terms of the GNU Affero      #
# Public License version 3 as published by the Free Software and appearing    #
# in the file LICENSE.AGPL included in the packaging of this file.            #
# The BEAT platform is distributed in the hope that it will be useful, but    #
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
#                                                                             #
# You should have received a copy of the GNU Affero Public License along      #
# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
#                                                                             #
###############################################################################


from datetime import datetime

from django.db import models

from ...algorithms.models import Algorithm
from ...backend.models import Environment
from ...backend.models import Queue

# ----------------------------------------------------------


[docs]class BlockManager(models.Manager):
[docs] def get_by_natural_key( self, name, experiment_author, toolchain_author, toolchain_name, toolchain_version, experiment_name, ): return self.get( name=name, experiment__author__username=experiment_author, experiment__toolchain__author__username=toolchain_author, experiment__toolchain__name=toolchain_name, experiment__toolchain__version=toolchain_version, experiment__name=experiment_name, )
# ----------------------------------------------------------
[docs]class Block(models.Model): PENDING = "N" PROCESSING = "P" DONE = "C" FAILED = "F" CANCELLED = "L" STATUS = ( (PENDING, "Pending"), (PROCESSING, "Processing"), (DONE, "Done"), (FAILED, "Failed"), (CANCELLED, "Cancelled"), ) experiment = models.ForeignKey( "Experiment", related_name="blocks", on_delete=models.CASCADE ) name = models.CharField(max_length=200) command = models.TextField(null=True, blank=True) status = models.CharField(max_length=1, choices=STATUS, default=PENDING) analyzer = models.BooleanField(default=False) algorithm = models.ForeignKey( Algorithm, related_name="blocks", on_delete=models.CASCADE ) creation_date = models.DateTimeField(null=True, blank=True, auto_now_add=True) start_date = models.DateTimeField(null=True, blank=True) end_date = models.DateTimeField(null=True, blank=True) environment = models.ForeignKey( Environment, related_name="blocks", null=True, on_delete=models.SET_NULL ) queue = models.ForeignKey( Queue, related_name="blocks", null=True, on_delete=models.SET_NULL ) required_slots = models.PositiveIntegerField(default=1) channel = models.CharField( max_length=200, default="", blank=True, help_text="Synchronization channel within the toolchain", ) # relationship to blocks to which this block depends on dependencies = models.ManyToManyField( "self", related_name="dependents", blank=True, symmetrical=False, ) # order of this block within the experiment - useful for the `backup' # command, so we can dump the blocks in the right dependence order execution_order = models.PositiveIntegerField(null=True, blank=True) objects = BlockManager() class Meta: unique_together = ("experiment", "name") # setup ordering so that the dump order respects self dependencies ordering = ["experiment_id", "execution_order"] def __str__(self): return ( self.experiment.fullname() + ", " + self.name + " (%s)" % self.get_status_display() )
[docs] def natural_key(self): return ( self.name, self.experiment.author.username, self.experiment.toolchain.author.username, self.experiment.toolchain.name, self.experiment.toolchain.version, self.experiment.name, )
natural_key.dependencies = ["experiments.experiment"]
[docs] def save(self, *args, **kwargs): # Ensure that the state of the block is consistent, just in case, but # we expect the caller to do it properly if self.status == Block.PENDING: try: self.results.all().delete() except Exception: # nosec pass self.start_date = None self.end_date = None elif self.status == Block.PROCESSING: if self.start_date is None: self.start_date = datetime.now() self.end_date = None else: if self.end_date is None: self.end_date = datetime.now() super(Block, self).save(*args, **kwargs)
# Accessors for statistics def __return_first__(self, field, default=None): return getattr(self.outputs.first(), field, default)
[docs] def first_cache(self): return self.outputs.first()
[docs] def error_report(self): return self.__return_first__("error_report")
[docs] def stdout(self): return self.__return_first__("stdout")
[docs] def stderr(self): return self.__return_first__("stderr")
[docs] def speed_up_real(self): return self.__return_first__("speed_up_real")
[docs] def speed_up_maximal(self): return self.__return_first__("speed_up_maximal")
[docs] def linear_execution_time(self): return self.__return_first__("linear_execution_time")
[docs] def queuing_time(self): return self.__return_first__("queuing_time")
[docs] def cpu_time(self): return self.__return_first__("cpu_time")
[docs] def max_memory(self): return self.__return_first__("max_memory")
[docs] def data_read_size(self): return self.__return_first__("data_read_size")
[docs] def data_read_nb_blocks(self): return self.__return_first__("data_read_nb_blocks")
[docs] def data_read_time(self): return self.__return_first__("data_read_time")
[docs] def data_written_size(self): return self.__return_first__("data_written_size")
[docs] def data_written_nb_blocks(self): return self.__return_first__("data_written_nb_blocks")
[docs] def data_written_time(self): return self.__return_first__("data_written_time")
# Accessor for results results = property(lambda self: self.__return_first__("results"))
[docs] def done(self): """Says whether the block has finished or not""" return self.status not in (Block.PENDING, Block.PROCESSING)
[docs] def is_runnable(self): """Checks if a block is runnable presently""" return all([k.status == Block.DONE for k in self.dependencies.all()])
[docs] def set_canceled(self, end_date=None): """Update the block state to canceled Parameters: end_date (datetime): If provided sets the end_date otherwise datetime.now() will be used. """ self.status = Block.CANCELLED if end_date is None: end_date = datetime.now() self.end_date = end_date if self.start_date is None: self.start_date = self.end_date self.save()
[docs] def set_failed(self, end_date): """Update the block state to failed Parameters: end_date (datetime): end date on failure """ self.status = Block.FAILED self.end_date = end_date self.save()