Source code for beat.web.libraries.models

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

###############################################################################
#                                                                             #
# Copyright (c) 2016 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 django.db import models
from django.conf import settings
from django.core.urlresolvers import reverse

import beat.core.library

from ..common.storage import OverwriteStorage
from ..common.models import get_contribution_declaration_filename
from ..common.models import get_contribution_description_filename

from ..code.models import Code
from ..code.models import CodeManager
from ..code.models import get_contribution_source_code_filename

import simplejson
import collections


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


[docs]def validate_library(declaration): """Validates the declaration of a library code, returns wrapper""" if not declaration: raise SyntaxError('Library declaration cannot be empty') if not(isinstance(declaration, dict)): try: declaration_dict = simplejson.loads(declaration, object_pairs_hook=collections.OrderedDict) except Exception as e: raise SyntaxError(str(e)) else: declaration_dict = collections.OrderedDict(declaration) library = beat.core.library.Library(settings.PREFIX, declaration_dict) if not library.valid: raise SyntaxError('\n * %s' % '\n * '.join(library.errors)) return library
#----------------------------------------------------------
[docs]class LibraryStorage(OverwriteStorage): def __init__(self, *args, **kwargs): kwargs['location'] = settings.LIBRARIES_ROOT super(LibraryStorage, self).__init__(*args, **kwargs)
#----------------------------------------------------------
[docs]class LibraryManager(CodeManager):
[docs] def create_library(self, author, name, short_description='', description='', declaration=None, code=None, version=1, previous_version=None, fork_of=None): default = beat.core.library.Library(settings.PREFIX, data=None) return self.create_code(author, name, default, short_description, description, declaration, code, version, previous_version, fork_of)
#----------------------------------------------------------
[docs]class Library(Code): #_____ Fields __________ declaration_file = models.FileField( storage=LibraryStorage(), upload_to=get_contribution_declaration_filename, blank=True, null=True, max_length=200, db_column='declaration' ) description_file = models.FileField( storage=LibraryStorage(), upload_to=get_contribution_description_filename, blank=True, null=True, max_length=200, db_column='description' ) source_code_file = models.FileField( storage=LibraryStorage(), upload_to=get_contribution_source_code_filename, blank=True, null=True, max_length=200, db_column='source_code' ) # Read-only parameters that are updated at every save(), if required referenced_libraries = models.ManyToManyField('self', blank=True, related_name='referencing', symmetrical=False) objects = LibraryManager() #_____ Meta parameters __________ class Meta(Code.Meta): verbose_name_plural = 'libraries' #_____ Utilities __________
[docs] def get_absolute_url(self): return reverse( 'libraries:view', args=(self.author.username, self.name, self.version,), )
[docs] def get_api_update_url(self): '''Returns the endpoint to update this object''' return reverse( 'api_libraries:object', args=(self.author.username, self.name, self.version,), )
[docs] def get_api_share_url(self): '''Returns the endpoint to share this object''' return reverse( 'api_libraries:share', args=(self.author.username, self.name, self.version,), )
#_____ Overrides __________
[docs] def save(self, *args, **kwargs): wrapper = self._save_preprocessing() super(Library, self).save(*args, **kwargs) # reset referenced libraries self.referenced_libraries.clear() if wrapper.uses is not None: for l in set(wrapper.uses.values()): s = beat.core.library.Storage(settings.PREFIX, l) library = Library.objects.get(author__username=s.username, name=s.name, version=s.version, ) self.referenced_libraries.add(library)
#_____ Methods __________
[docs] def validate(self, declaration): return validate_library(declaration)
[docs] def modifiable(self): """Can modify if nobody points at me""" return super(Library, self).modifiable() and ((self.referencing.count() + self.used_by_algorithms.count()) == 0)
[docs] def deletable(self): """Can delete if nobody points at me""" return super(Library, self).deletable() and ((self.referencing.count() + self.used_by_algorithms.count()) == 0)
[docs] def valid(self): return True # A library (at least for now) is always implemented in Python,
# thus always valid
[docs] def core(self): return validate_library(self.declaration)
[docs] def uses(self): return self.core().uses
[docs] def environments(self): '''Calculates environment usage for this library Returns: list: mapping environment to usage counts, determining how many times a given algorithm has been successfuly used on that environment ''' from django.db.models import Count, Q from ..experiments.models import Block from ..experiments.models import Experiment from ..backend.models import Environment # Tries to figure through a maximum if using algorithms have been # successfuly used inside an environment. # Case 1) The block is part of an experiment that was successful # Case 2) The block is part of an experiment that is not successful # (failed or other), but it is CACHED (if not cached, then we can't # attest anything about the algorithm/environment relationship!) envs = Environment.objects.filter(blocks__in=Block.objects.filter( \ algorithm__in=self.used_by_algorithms.all()).filter( \ Q(experiment__status=Experiment.DONE) | \ ((~Q(experiment__status=Experiment.DONE)) & Q(status=Block.DONE)) )).annotate(itemcount=Count('id')).order_by('-creation_date' \ ).distinct() return [(k, k.itemcount) for k in envs]