From 38c9b2dcf7fbc12d209b0fce73a378c6fd25bb26 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Wed, 8 Apr 2020 14:36:58 +0200 Subject: [PATCH 01/17] [common][serializers] Pre-commit cleanup --- beat/web/common/serializers.py | 245 +++++++++++++++++++-------------- 1 file changed, 142 insertions(+), 103 deletions(-) diff --git a/beat/web/common/serializers.py b/beat/web/common/serializers.py index d3e6b3bc..a829de03 100644 --- a/beat/web/common/serializers.py +++ b/beat/web/common/serializers.py @@ -27,15 +27,13 @@ from django.conf import settings -from django.core.exceptions import ValidationError from django.contrib.auth.models import User from django.utils import six from rest_framework import serializers from ..team.models import Team -from ..ui.templatetags.gravatar import gravatar_url -from ..common.utils import validate_restructuredtext, ensure_html +from ..common.utils import ensure_html from .models import Shareable, Versionable, Contribution from .exceptions import ContributionCreationError @@ -44,24 +42,22 @@ from .fields import JSONSerializerField, StringListField import simplejson as json import difflib -import ast - -#---------------------------------------------------------- +# ---------------------------------------------------------- class DiffSerializer(serializers.Serializer): diff = serializers.SerializerMethodField() def get_diff(self, obj): - source1 = json.dumps(obj['object1'].declaration, indent=4) - source2 = json.dumps(obj['object2'].declaration, indent=4) + source1 = json.dumps(obj["object1"].declaration, indent=4) + source2 = json.dumps(obj["object2"].declaration, indent=4) diff = difflib.ndiff(source1.splitlines(), source2.splitlines()) - return '\n'.join(filter(lambda x: x[0] != '?', list(diff))) + return "\n".join(filter(lambda x: x[0] != "?", list(diff))) -#---------------------------------------------------------- +# ---------------------------------------------------------- class CheckNameSerializer(serializers.Serializer): @@ -72,16 +68,16 @@ class CheckNameSerializer(serializers.Serializer): return Contribution.sanitize_name(name) def get_used(self, obj): - name = obj.get('name') - model = self.context.get('model') - user = self.context.get('user') + name = obj.get("name") + model = self.context.get("model") + user = self.context.get("user") return model.objects.filter(author=user, name=name).exists() def create(self, validated_data): return validated_data -#---------------------------------------------------------- +# ---------------------------------------------------------- class SharingSerializer(serializers.Serializer): @@ -89,22 +85,28 @@ class SharingSerializer(serializers.Serializer): teams = StringListField(required=False) def validate_users(self, users): - user_accounts = User.objects.filter(username__in=users).values_list('username', flat=True) + user_accounts = User.objects.filter(username__in=users).values_list( + "username", flat=True + ) if len(users) != user_accounts.count(): unknown_users = [user for user in users if user not in user_accounts] if len(unknown_users) > 1: - raise serializers.ValidationError(['Unknown usernames: ' + ', '.join(unknown_users)]) + raise serializers.ValidationError( + ["Unknown usernames: " + ", ".join(unknown_users)] + ) else: - raise serializers.ValidationError(['Unknown username: ' + unknown_users[0]]) + raise serializers.ValidationError( + ["Unknown username: " + unknown_users[0]] + ) return users def validate_teams(self, teams): unknown_teams = [] - user = self.context.get('user') + user = self.context.get("user") for team_name in teams: - parts = team_name.split('/') + parts = team_name.split("/") if len(parts) > 2: unknown_teams.append(team_name) @@ -118,14 +120,16 @@ class SharingSerializer(serializers.Serializer): unknown_teams.append(team_name) if len(unknown_teams) > 1: - raise serializers.ValidationError('Unknown teams: ' + ', '.join(unknown_teams)) + raise serializers.ValidationError( + "Unknown teams: " + ", ".join(unknown_teams) + ) elif len(unknown_teams) == 1: - raise serializers.ValidationError('Unknown team: ' + unknown_teams[0]) + raise serializers.ValidationError("Unknown team: " + unknown_teams[0]) return teams -#---------------------------------------------------------- +# ---------------------------------------------------------- class DynamicFieldsSerializer(serializers.ModelSerializer): @@ -134,7 +138,7 @@ class DynamicFieldsSerializer(serializers.ModelSerializer): def __init__(self, *args, **kwargs): # Don't pass the 'fields' arg up to the superclass - fields = kwargs.pop('fields', self.Meta.default_fields) + fields = kwargs.pop("fields", self.Meta.default_fields) # Instantiate the superclass normally super(DynamicFieldsSerializer, self).__init__(*args, **kwargs) @@ -146,7 +150,7 @@ class DynamicFieldsSerializer(serializers.ModelSerializer): self.fields.pop(field_name) -#---------------------------------------------------------- +# ---------------------------------------------------------- class ShareableSerializer(DynamicFieldsSerializer): @@ -158,43 +162,50 @@ class ShareableSerializer(DynamicFieldsSerializer): class Meta(DynamicFieldsSerializer.Meta): model = Shareable - default_fields = DynamicFieldsSerializer.Meta.default_fields + \ - ['is_owner', 'modifiable', 'deletable', 'sharing'] + default_fields = DynamicFieldsSerializer.Meta.default_fields + [ + "is_owner", + "modifiable", + "deletable", + "sharing", + ] def get_accessibility(self, obj): if obj.sharing == Versionable.PUBLIC: - return 'public' + return "public" elif obj.sharing == Versionable.SHARED or obj.sharing == Versionable.USABLE: - return 'confidential' + return "confidential" else: - return 'private' - + return "private" def get_sharing(self, obj): - user = self.context.get('user') + user = self.context.get("user") sharing = None - if hasattr(obj, 'author') and user == obj.author: - sharing = {'status': obj.get_sharing_display().lower()} + if hasattr(obj, "author") and user == obj.author: + sharing = {"status": obj.get_sharing_display().lower()} if obj.shared_with.count() > 0: - sharing['shared_with'] = [user.username for user in obj.shared_with.all()] + sharing["shared_with"] = [ + user.username for user in obj.shared_with.all() + ] if obj.shared_with_team.count() > 0: - sharing['shared_with_team'] = [team.fullname() for team in obj.shared_with_team.all()] + sharing["shared_with_team"] = [ + team.fullname() for team in obj.shared_with_team.all() + ] return sharing def get_is_owner(self, obj): - if hasattr(obj, 'author'): - return obj.author == self.context.get('user') + if hasattr(obj, "author"): + return obj.author == self.context.get("user") return False -#---------------------------------------------------------- +# ---------------------------------------------------------- class VersionableSerializer(ShareableSerializer): - name = serializers.CharField(source='fullname') + name = serializers.CharField(source="fullname") fork_of = serializers.SerializerMethodField() last_version = serializers.SerializerMethodField() previous_version = serializers.SerializerMethodField() @@ -205,28 +216,33 @@ class VersionableSerializer(ShareableSerializer): class Meta(ShareableSerializer.Meta): model = Versionable - default_fields = ShareableSerializer.Meta.default_fields + ['name', 'version', 'last_version', - 'short_description', - 'fork_of', 'previous_version', - 'accessibility', - 'hash', - 'creation_date'] + default_fields = ShareableSerializer.Meta.default_fields + [ + "name", + "version", + "last_version", + "short_description", + "fork_of", + "previous_version", + "accessibility", + "hash", + "creation_date", + ] def get_fork_of(self, obj): - if not(obj.fork_of): + if not (obj.fork_of): return None - accessibility_infos = obj.fork_of.accessibility_for(self.context.get('user')) + accessibility_infos = obj.fork_of.accessibility_for(self.context.get("user")) return obj.fork_of.fullname() if accessibility_infos[0] else None def get_last_version(self, obj): return self.Meta.model.objects.is_last_version(obj) def get_previous_version(self, obj): - if not(obj.previous_version): + if not (obj.previous_version): return None - user = self.context.get('user') + user = self.context.get("user") previous_version = obj.previous_version while previous_version is not None: @@ -240,10 +256,10 @@ class VersionableSerializer(ShareableSerializer): def get_history(self, obj): - return obj.api_history(self.context.get('user')) + return obj.api_history(self.context.get("user")) -#---------------------------------------------------------- +# ---------------------------------------------------------- class ContributionSerializer(VersionableSerializer): @@ -253,18 +269,18 @@ class ContributionSerializer(VersionableSerializer): class Meta(VersionableSerializer.Meta): model = Contribution - extra_fields = ['description', 'declaration'] - exclude = ['description_file', 'declaration_file'] + extra_fields = ["description", "declaration"] + exclude = ["description_file", "declaration_file"] def get_description(self, obj): result = obj.description if six.PY2: - result = result.decode('utf-8') + result = result.decode("utf-8") return result def get_declaration(self, obj): - object_format = self.context.get('object_format') - if object_format == 'string': + object_format = self.context.get("object_format") + if object_format == "string": return json.dumps(obj.declaration, indent=4) else: return obj.declaration @@ -273,10 +289,11 @@ class ContributionSerializer(VersionableSerializer): description = obj.description if len(description) > 0: return ensure_html(description) - return '' + return "" + +# ---------------------------------------------------------- -#---------------------------------------------------------- class MapDot(dict): def __init__(self, *args, **kwargs): @@ -315,50 +332,57 @@ class ContributionCreationSerializer(serializers.ModelSerializer): previous_version = serializers.CharField(required=False) class Meta: - fields = ['name', 'short_description', 'description', - 'declaration', 'previous_version', - 'fork_of'] + fields = [ + "name", + "short_description", + "description", + "declaration", + "previous_version", + "fork_of", + ] beat_core_class = None def validate_description(self, description): - if description.find('\\') >= 0: #was escaped, unescape - description = description.decode('string_escape') + if description.find("\\") >= 0: # was escaped, unescape + description = description.decode("string_escape") return description def validate(self, data): - user = self.context.get('user') - name = self.Meta.model.sanitize_name(data['name']) - data['name'] = name + user = self.context.get("user") + name = self.Meta.model.sanitize_name(data["name"]) + data["name"] = name - if 'previous_version' in data: + if "previous_version" in data: if self.Meta.beat_core_class is not None: - previous_version_id = self.Meta.beat_core_class.Storage(settings.PREFIX, - data['previous_version']) + previous_version_id = self.Meta.beat_core_class.Storage( + settings.PREFIX, data["previous_version"] + ) if previous_version_id.username is None: previous_version_id.username = user.username else: - previous_version_id = MapDot() - previous_version_id["username"] = user.username - previous_version_id["name"] = name - previous_version_id["version"] = data['previous_version'] - data['data'] = json.dumps(data['data']) + previous_version_id = MapDot() + previous_version_id["username"] = user.username + previous_version_id["name"] = name + previous_version_id["version"] = data["previous_version"] + data["data"] = json.dumps(data["data"]) else: previous_version_id = None - if 'fork_of' in data: + if "fork_of" in data: if self.Meta.beat_core_class is not None: - fork_of_id = self.Meta.beat_core_class.Storage(settings.PREFIX, - data['fork_of']) + fork_of_id = self.Meta.beat_core_class.Storage( + settings.PREFIX, data["fork_of"] + ) if fork_of_id.username is None: fork_of_id.username = user.username else: - fork_of_id = MapDot() - fork_elem = data['fork_of'] - fork_of_id["username"] = fork_elem['username'] - fork_of_id["name"] = fork_elem['name'] - fork_of_id["version"] = fork_elem['version'] - data['data'] = json.dumps(data['data']) + fork_of_id = MapDot() + fork_elem = data["fork_of"] + fork_of_id["username"] = fork_elem["username"] + fork_of_id["name"] = fork_elem["name"] + fork_of_id["version"] = fork_elem["version"] + data["data"] = json.dumps(data["data"]) else: fork_of_id = None @@ -369,45 +393,60 @@ class ContributionCreationSerializer(serializers.ModelSerializer): previous_version = self.Meta.model.objects.get( author__username__iexact=previous_version_id.username, name=previous_version_id.name, - version=previous_version_id.version) - except: - raise serializers.ValidationError("{} '{}' not found".format( - self.Meta.model.__name__, - previous_version_id.fullname) - ) + version=previous_version_id.version, + ) + except self.Meta.model.DoesNotExist: + raise serializers.ValidationError( + "{} '{}' not found".format( + self.Meta.model.__name__, previous_version_id.fullname + ) + ) is_accessible = previous_version.accessibility_for(user) if not is_accessible[0]: - raise serializers.ValidationError('No access allowed') - data['previous_version'] = previous_version + raise serializers.ValidationError("No access allowed") + data["previous_version"] = previous_version # Retrieve the forked algorithm (if applicable) if fork_of_id is not None: try: - fork_of = self.Meta.model.objects.get(author__username__iexact=fork_of_id.username, - name=fork_of_id.name, - version=fork_of_id.version) - except: - raise serializers.ValidationError("{} '{}' not found".format(self.Meta.model.__name__, fork_of_id.fullname)) + fork_of = self.Meta.model.objects.get( + author__username__iexact=fork_of_id.username, + name=fork_of_id.name, + version=fork_of_id.version, + ) + except self.Meta.model.DoesNotExist: + raise serializers.ValidationError( + "{} '{}' not found".format( + self.Meta.model.__name__, fork_of_id.fullname + ) + ) is_accessible = fork_of.accessibility_for(user) if not is_accessible[0]: - raise serializers.ValidationError('No access allowed') - data['fork_of'] = fork_of + raise serializers.ValidationError("No access allowed") + data["fork_of"] = fork_of # Determine the version number last_version = None if previous_version_id is not None: - if (previous_version_id.username == user.username) and \ - (previous_version_id.name == name): - last_version = self.Meta.model.objects.filter(author=user, name=name).order_by('-version')[0] + if (previous_version_id.username == user.username) and ( + previous_version_id.name == name + ): + last_version = self.Meta.model.objects.filter( + author=user, name=name + ).order_by("-version")[0] if last_version is None: if self.Meta.model.objects.filter(author=user, name=name).count() > 0: - raise serializers.ValidationError('This {} name already exists on this account'.format(self.Meta.model.__name__.lower())) + raise serializers.ValidationError( + "This {} name already exists on this account".format( + self.Meta.model.__name__.lower() + ) + ) - data['version'] = (last_version.version + 1 if last_version is not None else 1) + data["version"] = last_version.version + 1 if last_version is not None else 1 return data -- GitLab From 3e6e617751338be45527de19a55e8f7a8da1fc78 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Wed, 8 Apr 2020 15:24:48 +0200 Subject: [PATCH 02/17] [common][serializers] Improve create validate readability --- beat/web/common/serializers.py | 66 +++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/beat/web/common/serializers.py b/beat/web/common/serializers.py index a829de03..a404df16 100644 --- a/beat/web/common/serializers.py +++ b/beat/web/common/serializers.py @@ -354,85 +354,85 @@ class ContributionCreationSerializer(serializers.ModelSerializer): if "previous_version" in data: if self.Meta.beat_core_class is not None: - previous_version_id = self.Meta.beat_core_class.Storage( + previous_version_asset = self.Meta.beat_core_class.Storage( settings.PREFIX, data["previous_version"] ) - if previous_version_id.username is None: - previous_version_id.username = user.username + if previous_version_asset.username is None: + previous_version_asset.username = user.username else: - previous_version_id = MapDot() - previous_version_id["username"] = user.username - previous_version_id["name"] = name - previous_version_id["version"] = data["previous_version"] + previous_version_asset = MapDot() + previous_version_asset["username"] = user.username + previous_version_asset["name"] = name + previous_version_asset["version"] = data["previous_version"] data["data"] = json.dumps(data["data"]) else: - previous_version_id = None + previous_version_asset = None if "fork_of" in data: if self.Meta.beat_core_class is not None: - fork_of_id = self.Meta.beat_core_class.Storage( + fork_of_asset = self.Meta.beat_core_class.Storage( settings.PREFIX, data["fork_of"] ) - if fork_of_id.username is None: - fork_of_id.username = user.username + if fork_of_asset.username is None: + fork_of_asset.username = user.username else: - fork_of_id = MapDot() + fork_of_asset = MapDot() fork_elem = data["fork_of"] - fork_of_id["username"] = fork_elem["username"] - fork_of_id["name"] = fork_elem["name"] - fork_of_id["version"] = fork_elem["version"] + fork_of_asset["username"] = fork_elem["username"] + fork_of_asset["name"] = fork_elem["name"] + fork_of_asset["version"] = fork_elem["version"] data["data"] = json.dumps(data["data"]) else: - fork_of_id = None + fork_of_asset = None # Retrieve the previous version (if applicable) - if previous_version_id is not None: + if previous_version_asset is not None: try: previous_version = self.Meta.model.objects.get( - author__username__iexact=previous_version_id.username, - name=previous_version_id.name, - version=previous_version_id.version, + author__username__iexact=previous_version_asset.username, + name=previous_version_asset.name, + version=previous_version_asset.version, ) except self.Meta.model.DoesNotExist: raise serializers.ValidationError( "{} '{}' not found".format( - self.Meta.model.__name__, previous_version_id.fullname + self.Meta.model.__name__, previous_version_asset.fullname ) ) - is_accessible = previous_version.accessibility_for(user) - if not is_accessible[0]: + has_access, _, _ = previous_version.accessibility_for(user) + if not has_access: raise serializers.ValidationError("No access allowed") data["previous_version"] = previous_version # Retrieve the forked algorithm (if applicable) - if fork_of_id is not None: + if fork_of_asset is not None: try: fork_of = self.Meta.model.objects.get( - author__username__iexact=fork_of_id.username, - name=fork_of_id.name, - version=fork_of_id.version, + author__username__iexact=fork_of_asset.username, + name=fork_of_asset.name, + version=fork_of_asset.version, ) except self.Meta.model.DoesNotExist: raise serializers.ValidationError( "{} '{}' not found".format( - self.Meta.model.__name__, fork_of_id.fullname + self.Meta.model.__name__, fork_of_asset.fullname ) ) - is_accessible = fork_of.accessibility_for(user) - if not is_accessible[0]: + has_access, _, _ = fork_of.accessibility_for(user) + if not has_access: raise serializers.ValidationError("No access allowed") data["fork_of"] = fork_of # Determine the version number last_version = None - if previous_version_id is not None: - if (previous_version_id.username == user.username) and ( - previous_version_id.name == name + if previous_version_asset is not None: + if (previous_version_asset.username == user.username) and ( + previous_version_asset.name == name ): last_version = self.Meta.model.objects.filter( author=user, name=name -- GitLab From cf9c08a219f7dc21c884e4dd3e0b68e6b4c2d9e5 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 16:12:28 +0200 Subject: [PATCH 03/17] [code][models] Pre-commit cleanup --- beat/web/code/models.py | 452 +++++++++++++++++++++++++--------------- 1 file changed, 288 insertions(+), 164 deletions(-) diff --git a/beat/web/code/models.py b/beat/web/code/models.py index 2ff0f51e..ac9453fa 100755 --- a/beat/web/code/models.py +++ b/beat/web/code/models.py @@ -33,7 +33,6 @@ from ..common.models import Contribution from ..common.models import StoredContribution from ..common.models import StoredContributionManager from ..common.exceptions import ShareError -from ..common.signals import shared from ..common import storage from ..team.models import Team @@ -45,34 +44,53 @@ import beat.core.hash import simplejson -#---------------------------------------------------------- +# ---------------------------------------------------------- class CodeManager(StoredContributionManager): - - def create_object(self, author, name, short_description='', description='', - declaration=None, code=None, version=1, previous_version=None, - fork_of=None): - - create = getattr(self, 'create_{}'.format(self.model.__name__.lower())) - return create(author=author, name=name, short_description=short_description, - description=description, declaration=declaration, code=code, - version=version, previous_version=previous_version, - fork_of=fork_of) + def create_object( + self, + author, + name, + short_description="", + description="", + declaration=None, + code=None, + version=1, + previous_version=None, + fork_of=None, + ): + + create = getattr(self, "create_{}".format(self.model.__name__.lower())) + return create( + author=author, + name=name, + short_description=short_description, + description=description, + declaration=declaration, + code=code, + version=version, + previous_version=previous_version, + fork_of=fork_of, + ) def for_user(self, user, add_public=False): if user.is_anonymous(): - query = Q(sharing=Code.PUBLIC) |\ - (Q(sharing=Code.USABLE) & (Q(shared_with=None) & Q(shared_with_team=None))) + query = Q(sharing=Code.PUBLIC) | ( + Q(sharing=Code.USABLE) + & (Q(shared_with=None) & Q(shared_with_team=None)) + ) return self.filter(query).distinct() teams = Team.objects.filter(members=user) - query = Q(author=user) |\ - Q(sharing=Code.USABLE) |\ - Q(usable_by=user) |\ - Q(usable_by_team__in=teams) |\ - Q(shared_with=user) |\ - Q(shared_with_team__in=teams) + query = ( + Q(author=user) + | Q(sharing=Code.USABLE) + | Q(usable_by=user) + | Q(usable_by_team__in=teams) + | Q(shared_with=user) + | Q(shared_with_team__in=teams) + ) if add_public: query |= Q(sharing=Code.PUBLIC) @@ -80,8 +98,7 @@ class CodeManager(StoredContributionManager): return self.filter(query).distinct() def for_team(self, team): - return self.filter(Q(usable_by_team=team) | - Q(shared_with_team=team)) + return self.filter(Q(usable_by_team=team) | Q(shared_with_team=team)) def from_author(self, user, author_name, add_public=False): if user.is_anonymous(): @@ -92,28 +109,44 @@ class CodeManager(StoredContributionManager): objects_for_user = self.for_user(asked_user, add_public) else: teams = Team.objects.filter(members=user) - objects_for_user = self.filter(Q(author__username=author_name) & - (Q(sharing=Code.PUBLIC) | - Q(sharing=Code.USABLE) | - Q(usable_by=user) | - Q(usable_by_team__in=teams) | - Q(shared_with=user) | - Q(shared_with_team__in=teams))).distinct() - - return objects_for_user.order_by('author__username', 'name', '-version').select_related() - - def create_code(self, author, name, default, short_description='', - description='', declaration=None, code=None, version=1, - previous_version=None, fork_of=None): + objects_for_user = self.filter( + Q(author__username=author_name) + & ( + Q(sharing=Code.PUBLIC) + | Q(sharing=Code.USABLE) + | Q(usable_by=user) + | Q(usable_by_team__in=teams) + | Q(shared_with=user) + | Q(shared_with_team__in=teams) + ) + ).distinct() + + return objects_for_user.order_by( + "author__username", "name", "-version" + ).select_related() + + def create_code( + self, + author, + name, + default, + short_description="", + description="", + declaration=None, + code=None, + version=1, + previous_version=None, + fork_of=None, + ): # Create the database representation code_db = self.model( - author = author, - name = self.model.sanitize_name(name), - version = version, - sharing = Code.PRIVATE, - previous_version = previous_version, - fork_of = fork_of, + author=author, + name=self.model.sanitize_name(name), + version=version, + sharing=Code.PRIVATE, + previous_version=previous_version, + fork_of=fork_of, ) # Check the provided declaration @@ -124,16 +157,16 @@ class CodeManager(StoredContributionManager): declaration = fork_of.declaration else: declaration = default.data - elif not(isinstance(declaration, dict)): + elif not (isinstance(declaration, dict)): declaration = simplejson.loads(declaration) if len(short_description) > 0: - declaration['description'] = short_description + declaration["description"] = short_description code_db.declaration = declaration # Figure out the language - language = declaration.get('language', 'unknown') + language = declaration.get("language", "unknown") code_db.language = getattr(Code, language.upper()) # Check the provided source code @@ -154,7 +187,7 @@ class CodeManager(StoredContributionManager): elif fork_of is not None: description = fork_of.description else: - description = '' + description = "" code_db.description = description @@ -162,61 +195,62 @@ class CodeManager(StoredContributionManager): code_db.save() except Exception: import traceback + return (None, traceback.format_exc()) return (code_db, None) -#---------------------------------------------------------- +# ---------------------------------------------------------- def get_contribution_source_code_filename(obj, path): return obj.source_code_filename() -#---------------------------------------------------------- +# ---------------------------------------------------------- # Use those function to add a 'source_code' property to a model, by doing: # source_code = property(beat.web.code.models.get_source_code, # beat.web.code.models.set_source_code) + def set_source_code(instance, value): - storage.set_file_content(instance, 'source_code_file', instance.source_code_filename(), value) + storage.set_file_content( + instance, "source_code_file", instance.source_code_filename(), value + ) def get_source_code(instance): - return storage.get_file_content(instance, 'source_code_file') + return storage.get_file_content(instance, "source_code_file") -#---------------------------------------------------------- +# ---------------------------------------------------------- class Code(StoredContribution): - #_____ Constants __________ + # _____ Constants __________ # All possible values should be in sync with beat.core.utils - UNKNOWN = 'U' - CXX = 'C' - MATLAB = 'M' - PYTHON = 'P' - R = 'R' + UNKNOWN = "U" + CXX = "C" + MATLAB = "M" + PYTHON = "P" + R = "R" CODE_LANGUAGE = ( - (UNKNOWN, 'Unknown'), - (CXX, 'Cxx'), - (MATLAB, 'Matlab'), - (PYTHON, 'Python'), - (R, 'R'), + (UNKNOWN, "Unknown"), + (CXX, "Cxx"), + (MATLAB, "Matlab"), + (PYTHON, "Python"), + (R, "R"), ) - CODE_NAMES = { - CXX: 'C++', - } + CODE_NAMES = {CXX: "C++"} - - #_____ Fields __________ + # _____ Fields __________ # For technical reason, it is not possible to declare the required field here. It # must be declared in each subclass of Code, like this: @@ -229,35 +263,34 @@ class Code(StoredContribution): # db_column='source_code' # ) - usable_by = models.ManyToManyField(User, related_name='usable_%(class)ss', - blank=True) + usable_by = models.ManyToManyField( + User, related_name="usable_%(class)ss", blank=True + ) - usable_by_team = models.ManyToManyField(Team, - related_name='usable_%(class)ss', blank=True) + usable_by_team = models.ManyToManyField( + Team, related_name="usable_%(class)ss", blank=True + ) - language = models.CharField(max_length=1, choices=CODE_LANGUAGE, - default=PYTHON) + language = models.CharField(max_length=1, choices=CODE_LANGUAGE, default=PYTHON) objects = CodeManager() - - #_____ Meta parameters __________ + # _____ Meta parameters __________ class Meta(StoredContribution.Meta): abstract = True - - #_____ Methods to implement __________ + # _____ Methods to implement __________ def validate(self, declaration): raise NotImplementedError() - - #_____ Methods __________ + # _____ Methods __________ def source_code_filename(self): - return self.hashed_path(extension_for_language(self.get_language_display().lower())) - + return self.hashed_path( + extension_for_language(self.get_language_display().lower()) + ) def share(self, public, users=None, teams=None): self._share_libraries(public=public, users=users, teams=teams) @@ -271,9 +304,13 @@ class Code(StoredContribution): if users is not None: # Filter out the users that already have this sharing access or better users_with_better_access = [self.author.username] - users_with_better_access.extend(self.shared_with.all().values_list('username', flat=True)) + users_with_better_access.extend( + self.shared_with.all().values_list("username", flat=True) + ) - users = [user for user in users if user not in users_with_better_access] + users = [ + user for user in users if user not in users_with_better_access + ] # Remove the sharing preferences of users that already have a lower sharing access db_users = self.usable_by.filter(username__in=users) @@ -295,7 +332,11 @@ class Code(StoredContribution): # Filter out the teams that already have this sharing access or better teams_with_better_access = self.shared_with_team.all() - db_teams = [team for team in teams if team not in teams_with_better_access] + db_teams = [ + team + for team in teams + if team not in teams_with_better_access + ] # Update the code entry in the database for db_team in db_teams: @@ -303,18 +344,26 @@ class Code(StoredContribution): self.shared_with_team.add(db_team) else: # Filter out the teams that already have this sharing access or better - teams_with_better_access = [team.fullname() for team in self.shared_with_team.all()] + teams_with_better_access = [ + team.fullname() for team in self.shared_with_team.all() + ] - teams = [team for team in teams if team not in teams_with_better_access] + teams = [ + team + for team in teams + if team not in teams_with_better_access + ] # Update the algorithm entry in the database for team_name in teams: - parts = team_name.split('/') + parts = team_name.split("/") if len(parts) == 1: parts = [self.author.username, team_name] - db_team = Team.objects.filter(owner__username=parts[0], name=parts[1]) + db_team = Team.objects.filter( + owner__username=parts[0], name=parts[1] + ) if len(db_team) == 1: self.usable_by_team.remove(db_team[0]) self.shared_with_team.add(db_team[0]) @@ -329,16 +378,22 @@ class Code(StoredContribution): else: # Check that the request make sense - if not self.sharing in [Contribution.PUBLIC, Contribution.USABLE]: + if self.sharing not in [Contribution.PUBLIC, Contribution.USABLE]: # Usable by some users sharing = None if users is not None: # Filter out the users that already have this sharing access or better users_with_better_access = [self.author.username] - users_with_better_access.extend(self.shared_with.all().values_list('username', flat=True)) - users_with_better_access.extend(self.usable_by.all().values_list('username', flat=True)) + users_with_better_access.extend( + self.shared_with.all().values_list("username", flat=True) + ) + users_with_better_access.extend( + self.usable_by.all().values_list("username", flat=True) + ) - users = [user for user in users if user not in users_with_better_access] + users = [ + user for user in users if user not in users_with_better_access + ] # Update the algorithm entry in the database db_users = User.objects.filter(username__in=users) @@ -354,26 +409,40 @@ class Code(StoredContribution): teams_with_better_access = list(self.shared_with_team.all()) teams_with_better_access.extend(list(self.usable_by_team.all())) - db_teams = [team for team in teams if team not in teams_with_better_access] + db_teams = [ + team + for team in teams + if team not in teams_with_better_access + ] # Update the code entry in the database for db_team in db_teams: self.usable_by_team.add(db_team) else: # Filter out the teams that already have this sharing access or better - teams_with_better_access = [team.fullname() for team in self.shared_with_team.all()] - teams_with_better_access.extend([team.fullname for team in self.usable_by_team.all()]) - - new_teams = [team for team in teams if team not in teams_with_better_access] + teams_with_better_access = [ + team.fullname() for team in self.shared_with_team.all() + ] + teams_with_better_access.extend( + [team.fullname for team in self.usable_by_team.all()] + ) + + new_teams = [ + team + for team in teams + if team not in teams_with_better_access + ] # Update the algorithm entry in the database for team_name in new_teams: - parts = team_name.split('/') + parts = team_name.split("/") if len(parts) == 1: parts = [self.author.username, team_name] - db_team = Team.objects.filter(owner__username=parts[0], name=parts[1]) + db_team = Team.objects.filter( + owner__username=parts[0], name=parts[1] + ) if len(db_team) == 1: self.usable_by_team.add(db_team[0]) @@ -385,7 +454,6 @@ class Code(StoredContribution): self._update_sharing(sharing, users, teams) - def is_accessible(self, public, users=None, teams=None): errors = [] @@ -393,116 +461,168 @@ class Code(StoredContribution): if self.sharing == Contribution.PUBLIC: return errors - if (users is None and teams is None): - errors.append("The {} '{}' isn't accessible to all".format(self.model_name(), self.fullname())) + if users is None and teams is None: + errors.append( + "The {} '{}' isn't accessible to all".format( + self.model_name(), self.fullname() + ) + ) # Public some teams if users is not None: # Retrieve the users that don't have enough access to the algorithm users_with_better_access = [self.author.username] - users_with_better_access.extend(self.shared_with.all().values_list('username', flat=True)) + users_with_better_access.extend( + self.shared_with.all().values_list("username", flat=True) + ) - missing_users = [user for user in users if user not in users_with_better_access] + missing_users = [ + user for user in users if user not in users_with_better_access + ] if len(missing_users) > 0: - errors.append("The {} '{}' isn't accessible to the following users: {}".format(self.model_name(), self.fullname(), ', '.join(missing_users))) + errors.append( + "The {} '{}' isn't accessible to the following users: {}".format( + self.model_name(), self.fullname(), ", ".join(missing_users) + ) + ) # Accessible to some teams if teams is not None: # Retrieve the teams that don't have enough access to the algorithm - teams_with_better_access = [team.fullname() for team in self.shared_with_team.all()] + teams_with_better_access = [ + team.fullname() for team in self.shared_with_team.all() + ] - missing_teams = [team for team in teams if team not in teams_with_better_access] + missing_teams = [ + team for team in teams if team not in teams_with_better_access + ] if len(missing_teams) > 0: - errors.append("The {} '{}' isn't accessible to the following teams: {}".format(self.model_name(), self.fullname(), ', '.join(missing_teams))) + errors.append( + "The {} '{}' isn't accessible to the following teams: {}".format( + self.model_name(), self.fullname(), ", ".join(missing_teams) + ) + ) else: if self.sharing in [Contribution.PUBLIC, Contribution.USABLE]: return errors - if (users is None and teams is None): - errors.append("The {} '{}' isn't usable by all".format(self.model_name(), self.fullname())) + if users is None and teams is None: + errors.append( + "The {} '{}' isn't usable by all".format( + self.model_name(), self.fullname() + ) + ) # Usable by some users if users is not None: # Retrieve the users that don't have enough access to the algorithm users_with_better_access = [self.author.username] - users_with_better_access.extend(self.shared_with.all().values_list('username', flat=True)) - users_with_better_access.extend(self.usable_by.all().values_list('username', flat=True)) + users_with_better_access.extend( + self.shared_with.all().values_list("username", flat=True) + ) + users_with_better_access.extend( + self.usable_by.all().values_list("username", flat=True) + ) - missing_users = [user for user in users if user not in users_with_better_access] + missing_users = [ + user for user in users if user not in users_with_better_access + ] if len(missing_users) > 0: - errors.append("The {} '{}' isn't usable by the following users: {}".format(self.model_name(), self.fullname(), ', '.join(missing_users))) + errors.append( + "The {} '{}' isn't usable by the following users: {}".format( + self.model_name(), self.fullname(), ", ".join(missing_users) + ) + ) # Usable by some teams if teams is not None: # Retrieve the teams that don't have enough access to the algorithm - teams_with_better_access = [team.fullname() for team in self.shared_with_team.all()] - teams_with_better_access.extend([team.fullname() for team in self.usable_by_team.all()]) + teams_with_better_access = [ + team.fullname() for team in self.shared_with_team.all() + ] + teams_with_better_access.extend( + [team.fullname() for team in self.usable_by_team.all()] + ) - missing_teams = [team for team in teams if team not in teams_with_better_access] + missing_teams = [ + team for team in teams if team not in teams_with_better_access + ] if len(missing_teams) > 0: - errors.append("The {} '{}' isn't usable by the following teams: {}".format(self.model_name(), self.fullname(), ', '.join(missing_teams))) + errors.append( + "The {} '{}' isn't usable by the following teams: {}".format( + self.model_name(), self.fullname(), ", ".join(missing_teams) + ) + ) return errors - def sharing_preferences(self): - result = { - 'status': self.get_sharing_display().lower() - } + result = {"status": self.get_sharing_display().lower()} if self.shared_with.count() > 0: - result['shared_with'] = self.shared_with.all().values_list('username', flat=True) + result["shared_with"] = self.shared_with.all().values_list( + "username", flat=True + ) if self.shared_with_team.count() > 0: - result['shared_with_team'] = [team.fullname() for team in self.shared_with_team.all()] + result["shared_with_team"] = [ + team.fullname() for team in self.shared_with_team.all() + ] if self.usable_by.count() > 0: - result['usable_by'] = self.usable_by.all().values_list('username', flat=True) + result["usable_by"] = self.usable_by.all().values_list( + "username", flat=True + ) if self.usable_by_team.count() > 0: - result['usable_by_team'] = [team.fullname() for team in self.usable_by_team.all()] + result["usable_by_team"] = [ + team.fullname() for team in self.usable_by_team.all() + ] return result - def open_source(self, user): - '''Tells if a given user can view the code of this contribution''' + """Tells if a given user can view the code of this contribution""" - if self.author == user: return True + if self.author == user: + return True accessible, open_source, level = self.accessibility_for(user) return open_source - def is_binary(self): return self.language in [Code.CXX] - def language_fullname(self): if self.language in Code.CODE_NAMES: return Code.CODE_NAMES[self.language] - return [language for language in Code.CODE_LANGUAGE if language[0] == self.language][0][1] - + return [ + language for language in Code.CODE_LANGUAGE if language[0] == self.language + ][0][1] def json_language(self): return Code.language_identifier(self.language) - @staticmethod def language_identifier(db_language): - return [language for language in Code.CODE_LANGUAGE if language[0] == db_language][0][1].lower() + return [ + language for language in Code.CODE_LANGUAGE if language[0] == db_language + ][0][1].lower() @staticmethod def language_db(language_identifier): - return [language for language in Code.CODE_LANGUAGE if language[1].lower() == language_identifier][0][0] - + return [ + language + for language in Code.CODE_LANGUAGE + if language[1].lower() == language_identifier + ][0][0] - #_____ Overrides __________ + # _____ Overrides __________ def save(self, *args, **kwargs): @@ -516,77 +636,79 @@ class Code(StoredContribution): elif self.sharing == Code.USABLE: self.usable_by.clear() - - #_____ Properties __________ + # _____ Properties __________ source_code = property(get_source_code, set_source_code) - - #_____ Protected methods __________ + # _____ Protected methods __________ def _save_preprocessing(self): # Make sure the declaration is valid, raises SyntaxError if a problem occurs wrapper = self.validate(self.declaration) # Update the DB entry using the validated declaration - declaration_hash = beat.core.hash.hashJSON(self.declaration, 'description') + declaration_hash = beat.core.hash.hashJSON(self.declaration, "description") code_hash = beat.core.hash.hash(self.source_code) - self.hash = beat.core.hash.hash(dict( - declaration=declaration_hash, - code=code_hash, - )) + self.hash = beat.core.hash.hash( + dict(declaration=declaration_hash, code=code_hash) + ) - self.short_description = wrapper.description if wrapper.description is not None else '' + self.short_description = ( + wrapper.description if wrapper.description is not None else "" + ) return wrapper - def _accessibility_for_user(self, user, without_usable=False): """Returns a tuple (, , ), with being either 'public', 'private', 'confidential' """ if self.author == user: if self.sharing == Contribution.PRIVATE: - return (True, False, 'private') + return (True, False, "private") elif self.sharing == Contribution.PUBLIC: - return (True, True, 'public') + return (True, True, "public") else: - return (True, False, 'confidential') + return (True, False, "confidential") else: if self.sharing == Contribution.PRIVATE: return (False, False, None) elif self.sharing == Contribution.PUBLIC: - return (True, True, 'public') + return (True, True, "public") elif not without_usable and (self.sharing == Contribution.USABLE): - return (True, False, 'confidential') + return (True, False, "confidential") elif not user.is_anonymous(): - if self.shared_with.filter(id=user.id).exists() or (self.shared_with_team.filter(members=user).count() > 0): - return (True, True, 'confidential') - elif not without_usable and (self.usable_by.filter(id=user.id).exists() or (self.usable_by_team.filter(members=user).count() > 0)): - return (True, False, 'confidential') + if self.shared_with.filter(id=user.id).exists() or ( + self.shared_with_team.filter(members=user).count() > 0 + ): + return (True, True, "confidential") + elif not without_usable and ( + self.usable_by.filter(id=user.id).exists() + or (self.usable_by_team.filter(members=user).count() > 0) + ): + return (True, False, "confidential") return (False, False, None) - def _accessibility_for_team(self, team, without_usable=False): """Team specific accessibility check """ if self.sharing == Contribution.PRIVATE: return (False, False, None) elif self.sharing == Contribution.PUBLIC: - return (True, True, 'public') + return (True, True, "public") elif not without_usable and (self.sharing == Contribution.USABLE): - return (True, False, 'confidential') + return (True, False, "confidential") elif self.shared_with_team.filter(id=team.id).exists(): - return (True, True, 'confidential') + return (True, True, "confidential") elif not without_usable and self.usable_by_team.filter(id=team.id).exists(): - return (True, False, 'confidential') + return (True, False, "confidential") return (False, False, None) - #_____ Protected methods __________ + # _____ Protected methods __________ def _share_libraries(self, public, users, teams): # Retrieve and process the list of referenced libraries @@ -598,7 +720,9 @@ class Code(StoredContribution): # preferences errors = [] for needed_library in other_needed_libraries: - errors.extend(needed_library.is_accessible(public=public, users=users, teams=teams)) + errors.extend( + needed_library.is_accessible(public=public, users=users, teams=teams) + ) if len(errors) > 0: raise ShareError(errors) -- GitLab From b4067adf3295735aca2d4ce6d8f74346bcc3f03a Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 16:12:46 +0200 Subject: [PATCH 04/17] [common][models] Pre-commit cleanup --- beat/web/common/models.py | 454 ++++++++++++++++++++++---------------- 1 file changed, 261 insertions(+), 193 deletions(-) diff --git a/beat/web/common/models.py b/beat/web/common/models.py index 324a2f72..ffc0a5a5 100755 --- a/beat/web/common/models.py +++ b/beat/web/common/models.py @@ -49,7 +49,7 @@ import simplejson from collections import OrderedDict -#---------------------------------------------------------- +# ---------------------------------------------------------- class ShareableManager(models.Manager): @@ -57,11 +57,12 @@ class ShareableManager(models.Manager): if user.is_anonymous(): return self.public() - query = Q(sharing=Shareable.SHARED) &\ - (Q(shared_with=user) |\ - Q(shared_with_team__in=Team.objects.filter(members=user))) + query = Q(sharing=Shareable.SHARED) & ( + Q(shared_with=user) + | Q(shared_with_team__in=Team.objects.filter(members=user)) + ) - if hasattr(self.model, 'author'): + if hasattr(self.model, "author"): query |= Q(author=user) if add_public: @@ -73,57 +74,54 @@ class ShareableManager(models.Manager): return self.filter(sharing=Shareable.PUBLIC) -#---------------------------------------------------------- +# ---------------------------------------------------------- class Shareable(models.Model): - #_____ Constants __________ + # _____ Constants __________ - PRIVATE = 'P' - SHARED = 'S' - PUBLIC = 'A' - USABLE = 'U' + PRIVATE = "P" + SHARED = "S" + PUBLIC = "A" + USABLE = "U" SHARING_STATUS = ( - (PRIVATE, 'Private'), - (SHARED, 'Shared'), - (PUBLIC, 'Public'), - (USABLE, 'Usable'), # Not applicable for all contribution types, must be - # checked for each case! + (PRIVATE, "Private"), + (SHARED, "Shared"), + (PUBLIC, "Public"), + (USABLE, "Usable"), # Not applicable for all contribution types, must be + # checked for each case! ) + # _____ Fields __________ - #_____ Fields __________ - - sharing = models.CharField(max_length=1, choices=SHARING_STATUS, default=PRIVATE) - shared_with = models.ManyToManyField(User, related_name='shared_%(class)ss', - blank=True) + sharing = models.CharField(max_length=1, choices=SHARING_STATUS, default=PRIVATE) + shared_with = models.ManyToManyField( + User, related_name="shared_%(class)ss", blank=True + ) - shared_with_team = models.ManyToManyField(Team, - related_name='shared_%(class)ss', blank=True) + shared_with_team = models.ManyToManyField( + Team, related_name="shared_%(class)ss", blank=True + ) objects = ShareableManager() - - #_____ Meta parameters __________ + # _____ Meta parameters __________ class Meta: abstract = True - - #_____ Utilities __________ + # _____ Utilities __________ def model_name(self): return type(self).__name__.lower() - - #_____ Overrides __________ + # _____ Overrides __________ def __str__(self): return self.fullname() - def save(self, *args, **kwargs): super(Shareable, self).save(*args, **kwargs) @@ -132,9 +130,7 @@ class Shareable(models.Model): if self.sharing == Shareable.PUBLIC: self.shared_with.clear() - - #_____ Methods __________ - + # _____ Methods __________ def get_verbose_name(self): return self._meta.verbose_name @@ -143,13 +139,13 @@ class Shareable(models.Model): return self._meta.verbose_name_plural def modifiable(self): - if hasattr(self, 'attestations'): - return (self.attestations.count() == 0) + if hasattr(self, "attestations"): + return self.attestations.count() == 0 return True def deletable(self): - if hasattr(self, 'attestations'): - return (self.attestations.count() == 0) + if hasattr(self, "attestations"): + return self.attestations.count() == 0 return True def accessibility_for(self, user_or_team, without_usable=False): @@ -173,30 +169,55 @@ class Shareable(models.Model): if users is not None: # Retrieve the users that don't have enough access to the algorithm users_with_better_access = [] - if hasattr(self, 'author'): + if hasattr(self, "author"): users_with_better_access.append(self.author.username) - users_with_better_access.extend(self.shared_with.all().values_list('username', flat=True)) - users_with_better_access.extend([user for user in users if self.shared_with_team.filter(members__username=user).exists()]) - - missing_users = [user for user in users if user not in users_with_better_access] + users_with_better_access.extend( + self.shared_with.all().values_list("username", flat=True) + ) + users_with_better_access.extend( + [ + user + for user in users + if self.shared_with_team.filter(members__username=user).exists() + ] + ) + + missing_users = [ + user for user in users if user not in users_with_better_access + ] if len(missing_users) > 0: - errors.append("The {0} '{1}' isn't accessible to the following users: {2}".format(self.model_name(), self.fullname(), ', '.join(missing_users))) - + errors.append( + "The {0} '{1}' isn't accessible to the following users: {2}".format( + self.model_name(), self.fullname(), ", ".join(missing_users) + ) + ) if teams is not None: # Retrieve the teams that don't have enough access to the algorithm - teams_with_better_access = [team.fullname() for team in self.shared_with_team.all()] + teams_with_better_access = [ + team.fullname() for team in self.shared_with_team.all() + ] - missing_teams = [team for team in teams if team not in teams_with_better_access] + missing_teams = [ + team for team in teams if team not in teams_with_better_access + ] if len(missing_teams) > 0: - errors.append("The {0} '{1}' isn't accessible to the following teams: {2}".format(self.model_name(), self.fullname(), ', '.join(missing_teams))) + errors.append( + "The {0} '{1}' isn't accessible to the following teams: {2}".format( + self.model_name(), self.fullname(), ", ".join(missing_teams) + ) + ) # Accessible to everybody if users is None and teams is None: if self.sharing != Shareable.PUBLIC: - errors.append("The {0} '{1}' isn't Accessible to all".format(self.model_name(), self.fullname())) + errors.append( + "The {0} '{1}' isn't Accessible to all".format( + self.model_name(), self.fullname() + ) + ) return errors @@ -210,9 +231,11 @@ class Shareable(models.Model): if users is not None: # Filter out the users that already have this sharing access or better users_with_better_access = [] - if hasattr(self, 'author'): + if hasattr(self, "author"): users_with_better_access.append(self.author.username) - users_with_better_access.extend(self.shared_with.all().values_list('username', flat=True)) + users_with_better_access.extend( + self.shared_with.all().values_list("username", flat=True) + ) users = [user for user in users if user not in users_with_better_access] @@ -230,25 +253,31 @@ class Shareable(models.Model): # Filter out the teams that already have this sharing access or better teams_with_better_access = self.shared_with_team.all() - db_teams = [team for team in teams if team not in teams_with_better_access] + db_teams = [ + team for team in teams if team not in teams_with_better_access + ] # Update the database entry for db_team in db_teams: self.shared_with_team.add(db_team) else: # Filter out the teams that already have this sharing access or better - teams_with_better_access = [team.fullname() for team in self.shared_with_team.all()] + teams_with_better_access = [ + team.fullname() for team in self.shared_with_team.all() + ] teams = [team for team in teams if team not in teams_with_better_access] # Update the database entry for team_name in teams: - parts = team_name.split('/') + parts = team_name.split("/") if len(parts) == 1: parts = [self.author.username, team_name] - db_team = Team.objects.filter(owner__username=parts[0], name=parts[1]) + db_team = Team.objects.filter( + owner__username=parts[0], name=parts[1] + ) if len(db_team) == 1: self.shared_with_team.add(db_team[0]) @@ -263,58 +292,73 @@ class Shareable(models.Model): self._update_sharing(sharing, users, teams) def sharing_preferences(self): - result = { - 'status': self.get_sharing_display().lower() - } + result = {"status": self.get_sharing_display().lower()} if self.shared_with.count() > 0: - result['shared_with'] = self.shared_with.all().values_list('username', flat=True) + result["shared_with"] = self.shared_with.all().values_list( + "username", flat=True + ) if self.shared_with_team.count() > 0: - result['shared_with_team'] = [team.fullname() for team in self.shared_with_team.all()] + result["shared_with_team"] = [ + team.fullname() for team in self.shared_with_team.all() + ] return result def all_shared_with_users(self): - '''Returns a list of users this object is shared with''' + """Returns a list of users this object is shared with""" - users = set(self.shared_with.exclude(username__in=settings.ACCOUNTS_TO_EXCLUDE_FROM_TEAMS).distinct()) - teams = set(User.objects.filter(teams__in=self.shared_with_team.all()).exclude(username__in=settings.ACCOUNTS_TO_EXCLUDE_FROM_TEAMS)) + users = set( + self.shared_with.exclude( + username__in=settings.ACCOUNTS_TO_EXCLUDE_FROM_TEAMS + ).distinct() + ) + teams = set( + User.objects.filter(teams__in=self.shared_with_team.all()).exclude( + username__in=settings.ACCOUNTS_TO_EXCLUDE_FROM_TEAMS + ) + ) return users | teams def users_with_access(self): - '''Returns a set of users that have access to this environment''' + """Returns a set of users that have access to this environment""" if self.sharing == Shareable.PUBLIC: - return set(User.objects.filter(is_active=True).exclude(username__in=settings.ACCOUNTS_TO_EXCLUDE_FROM_TEAMS).distinct()) + return set( + User.objects.filter(is_active=True) + .exclude(username__in=settings.ACCOUNTS_TO_EXCLUDE_FROM_TEAMS) + .distinct() + ) elif self.sharing == Shareable.SHARED: return self.all_shared_with_users() return set() - - #_____ Protected Methods __________ + # _____ Protected Methods __________ def _accessibility_for_user(self, user, without_usable=False): """User specific accessibility check """ - if hasattr(self, 'author') and self.author == user: + if hasattr(self, "author") and self.author == user: if self.sharing == Shareable.PRIVATE: - return (True, 'private') + return (True, "private") elif self.sharing == Shareable.PUBLIC: - return (True, 'public') + return (True, "public") else: - return (True, 'confidential') + return (True, "confidential") else: if self.sharing == Shareable.PRIVATE: return (False, None) elif self.sharing == Shareable.PUBLIC: - return (True, 'public') + return (True, "public") elif not user.is_anonymous(): - if self.shared_with.filter(id=user.id).exists() or (self.shared_with_team.filter(members=user).count() > 0): - return (True, 'confidential') + if self.shared_with.filter(id=user.id).exists() or ( + self.shared_with_team.filter(members=user).count() > 0 + ): + return (True, "confidential") return (False, None) @@ -324,13 +368,12 @@ class Shareable(models.Model): if self.sharing == Shareable.PRIVATE: return (False, None) elif self.sharing == Shareable.PUBLIC: - return (True, 'public') + return (True, "public") elif self.shared_with_team.filter(id=team.id).exists(): - return (True, 'confidential') + return (True, "confidential") return (False, None) - def _update_sharing(self, sharing, users, teams): if sharing != self.sharing: self.sharing = sharing @@ -339,81 +382,73 @@ class Shareable(models.Model): shared.send(sender=self, users=users, teams=teams) -#---------------------------------------------------------- +# ---------------------------------------------------------- class VersionableManager(ShareableManager): - def is_last_version(self, object): - return not self.filter(name=object.name, - version__gt=object.version).exists() + return not self.filter(name=object.name, version__gt=object.version).exists() -#---------------------------------------------------------- +# ---------------------------------------------------------- class Versionable(Shareable): - #_____ Fields __________ - - name = models.CharField(max_length=200, help_text=Messages['name'], blank=False) - version = models.PositiveIntegerField(default=1, help_text=Messages['version']) - short_description = models.CharField(max_length=100, default='', blank=True, help_text=Messages['short_description']) - creation_date = models.DateTimeField('Creation date', auto_now_add=True) - hash = models.CharField(max_length=64, editable=False, help_text=Messages['hash']) + # _____ Fields __________ - previous_version = models.ForeignKey('self', - related_name='next_versions', - null=True, - blank=True, - ) + name = models.CharField(max_length=200, help_text=Messages["name"], blank=False) + version = models.PositiveIntegerField(default=1, help_text=Messages["version"]) + short_description = models.CharField( + max_length=100, default="", blank=True, help_text=Messages["short_description"] + ) + creation_date = models.DateTimeField("Creation date", auto_now_add=True) + hash = models.CharField(max_length=64, editable=False, help_text=Messages["hash"]) - fork_of = models.ForeignKey('self', - related_name='forks', - null=True, - blank=True, - ) + previous_version = models.ForeignKey( + "self", related_name="next_versions", null=True, blank=True + ) + fork_of = models.ForeignKey("self", related_name="forks", null=True, blank=True) objects = VersionableManager() - - #_____ Meta parameters __________ + # _____ Meta parameters __________ class Meta(Shareable.Meta): abstract = True - ordering = ['name', '-version'] - + ordering = ["name", "-version"] - #_____ Static Methods __________ + # _____ Static Methods __________ @staticmethod def sanitize_name(name): """Makes sure that the name is valid""" - return re.sub(r'[^\x00-\x7f]|\W', r'-', name) - - + return re.sub(r"[^\x00-\x7f]|\W", r"-", name) @staticmethod def filter_latest_versions(versionables): result = [] for versionable in versionables: try: - entry = [item for item in result if item.fullname().startswith('%s' % versionable.name)][0] + entry = [ + item + for item in result + if item.fullname().startswith("%s" % versionable.name) + ][0] if entry.version >= versionable.version: continue else: result.remove(entry) - except: + except IndexError: pass result.append(versionable) return result - - #_____ Overrides __________ + # _____ Overrides __________ def delete(self, *args, **kwargs): for next_version in self.next_versions.iterator(): @@ -427,7 +462,9 @@ class Versionable(Shareable): super(Versionable, self).delete(*args, **kwargs) def modifiable(self): - return super(Versionable, self).modifiable() and (self.next_versions.count() == 0) + return super(Versionable, self).modifiable() and ( + self.next_versions.count() == 0 + ) def history(self, for_user): """Calculates its own history and returns it in a dictionary. @@ -441,81 +478,88 @@ class Versionable(Shareable): """Recursive function to build the history starting from a leaf""" # First retrieve all accessible versions of the versionable - if hasattr(obj, 'author'): - versions = self.__class__.objects.for_user(for_user, True).filter(author=obj.author, name=obj.name).order_by('version') + if hasattr(obj, "author"): + versions = ( + self.__class__.objects.for_user(for_user, True) + .filter(author=obj.author, name=obj.name) + .order_by("version") + ) else: - versions = self.__class__.objects.for_user(for_user, True).filter(name=obj.name).order_by('version') + versions = ( + self.__class__.objects.for_user(for_user, True) + .filter(name=obj.name) + .order_by("version") + ) if versions.count() == 0: return None # Next search for the very first version of the history first_version = versions[0] - if not(forward_only) and (first_version.fork_of is not None): + if not (forward_only) and (first_version.fork_of is not None): history = _process(first_version.fork_of, forward_only=False) if history is not None: return history # Construct a tree from the first version - history = {'object': first_version, 'next': []} + history = {"object": first_version, "next": []} for fork in first_version.forks.iterator(): fork_history = _process(fork) if fork_history is not None: - history['next'].append(fork_history) + history["next"].append(fork_history) previous_version = history for version in versions[1:]: - version_dict = {'object': version, 'next': []} + version_dict = {"object": version, "next": []} for fork in version.forks.iterator(): fork_history = _process(fork) if fork_history is not None: - version_dict['next'].append(fork_history) + version_dict["next"].append(fork_history) - previous_version['next'].append(version_dict) + previous_version["next"].append(version_dict) previous_version = version_dict return history return _process(self, forward_only=False) - def api_history(self, for_user): """The same as history(), but providing an implementation compatible with our V1 API""" def _recurse(d): - o = d['object'] - d['name'] = o.fullname() - d['creation_date'] = o.creation_date.isoformat(' ') - d['author_gravatar'] = gravatar_url(o.author.email) if hasattr(o, 'author') else None - del d['object'] - d['next'] = [_recurse(k) for k in d['next']] + o = d["object"] + d["name"] = o.fullname() + d["creation_date"] = o.creation_date.isoformat(" ") + d["author_gravatar"] = ( + gravatar_url(o.author.email) if hasattr(o, "author") else None + ) + del d["object"] + d["next"] = [_recurse(k) for k in d["next"]] return d return _recurse(self.history(for_user)) - def json_history(self, for_user): """The same as API history, but serializes the result into a JSON""" return simplejson.dumps(self.api_history(for_user)) -#---------------------------------------------------------- +# ---------------------------------------------------------- class ContributionManager(VersionableManager): - def get_by_natural_key(self, username, name, version): return self.get(author__username=username, name=name, version=version) def is_last_version(self, object): - return not self.filter(author=object.author, - name=object.name, - version__gt=object.version).exists() + return not self.filter( + author=object.author, name=object.name, version__gt=object.version + ).exists() def from_author(self, user, author_name, add_public=False): if user.is_anonymous(): @@ -525,65 +569,74 @@ class ContributionManager(VersionableManager): objects_for_user = self.for_user(user, add_public) else: teams = Team.objects.filter(members=user) - objects_for_user = self.filter(Q(author__username=author_name) & - (Q(sharing=Contribution.PUBLIC)| - Q(shared_with=user) | - Q(shared_with_team__in=teams))).distinct() - - return objects_for_user.order_by('author__username', 'name', '-version').select_related() + objects_for_user = self.filter( + Q(author__username=author_name) + & ( + Q(sharing=Contribution.PUBLIC) + | Q(shared_with=user) + | Q(shared_with_team__in=teams) + ) + ).distinct() + + return objects_for_user.order_by( + "author__username", "name", "-version" + ).select_related() def from_author_and_public(self, user, author_name): return self.from_author(user, author_name, True) -#---------------------------------------------------------- +# ---------------------------------------------------------- class Contribution(Versionable): - #_____ Fields __________ + # _____ Fields __________ - author = models.ForeignKey(User, related_name='%(class)ss', - on_delete=models.CASCADE) + author = models.ForeignKey( + User, related_name="%(class)ss", on_delete=models.CASCADE + ) objects = ContributionManager() - - #_____ Meta parameters __________ + # _____ Meta parameters __________ class Meta(Versionable.Meta): abstract = True - ordering = ['author__username', 'name', 'version'] - unique_together = ('author', 'name', 'version') + ordering = ["author__username", "name", "version"] + unique_together = ("author", "name", "version") - - #_____ Utilities __________ + # _____ Utilities __________ def natural_key(self): return (self.author.username, self.name, self.version) - - #_____ Methods __________ + # _____ Methods __________ def fullname(self): - return '%s/%s/%d' % (self.author.username, self.name, self.version) - + return "%s/%s/%d" % (self.author.username, self.name, self.version) - #_____ Static Methods __________ + # _____ Static Methods __________ @staticmethod def filter_latest_versions(contributions): result = [] for contribution in contributions: try: - entry = [item for item in result if item.fullname().startswith('%s/%s/' % (contribution.author.username, contribution.name))][0] + entry = [ + item + for item in result + if item.fullname().startswith( + "%s/%s/" % (contribution.author.username, contribution.name) + ) + ][0] if entry.version >= contribution.version: continue else: result.remove(entry) - except: + except IndexError: pass result.append(contribution) @@ -591,23 +644,37 @@ class Contribution(Versionable): return result -#---------------------------------------------------------- +# ---------------------------------------------------------- class StoredContributionManager(ContributionManager): - - def create_object(self, author, name, short_description='', description='', - declaration=None, version=1, previous_version=None, - fork_of=None): - - create = getattr(self, 'create_{}'.format(self.model.__name__.lower())) - - return create(author=author, name=name, short_description=short_description, - description=description, declaration=declaration, version=version, - previous_version=previous_version, fork_of=fork_of) + def create_object( + self, + author, + name, + short_description="", + description="", + declaration=None, + version=1, + previous_version=None, + fork_of=None, + ): + + create = getattr(self, "create_{}".format(self.model.__name__.lower())) + + return create( + author=author, + name=name, + short_description=short_description, + description=description, + declaration=declaration, + version=version, + previous_version=previous_version, + fork_of=fork_of, + ) -#---------------------------------------------------------- +# ---------------------------------------------------------- def get_contribution_declaration_filename(obj, path): @@ -618,7 +685,7 @@ def get_contribution_description_filename(obj, path): return obj.description_filename() -#---------------------------------------------------------- +# ---------------------------------------------------------- # Use those function to add a 'declaration' and a 'declaration_string' property to a @@ -627,45 +694,52 @@ def get_contribution_description_filename(obj, path): # beat.web.common.models.set_declaration) # declaration_string = property(beat.web.common.models.get_declaration_string) + def set_declaration(instance, value): if isinstance(value, dict): - value = simplejson.dumps(value, - indent=4, - cls=NumpyJSONEncoder) + value = simplejson.dumps(value, indent=4, cls=NumpyJSONEncoder) - storage.set_file_content(instance, 'declaration_file', instance.declaration_filename(), value) + storage.set_file_content( + instance, "declaration_file", instance.declaration_filename(), value + ) def get_declaration(instance): - return simplejson.loads(storage.get_file_content(instance, 'declaration_file'), object_pairs_hook=OrderedDict) + return simplejson.loads( + storage.get_file_content(instance, "declaration_file"), + object_pairs_hook=OrderedDict, + ) def get_declaration_string(instance): - data = storage.get_file_content(instance, 'declaration_file') + data = storage.get_file_content(instance, "declaration_file") return ensure_string(data) -#---------------------------------------------------------- +# ---------------------------------------------------------- # Use those function to add a 'description' property to a model, by doing: # description = property(beat.web.common.models.get_description, # beat.web.common.models.set_description) + def set_description(instance, value): - storage.set_file_content(instance, 'description_file', instance.description_filename(), value) + storage.set_file_content( + instance, "description_file", instance.description_filename(), value + ) def get_description(instance): - return storage.get_file_content(instance, 'description_file') + return storage.get_file_content(instance, "description_file") -#---------------------------------------------------------- +# ---------------------------------------------------------- class StoredContribution(Contribution): - #_____ Fields __________ + # _____ Fields __________ # For technical reason, it is not possible to declare the required fields here. They # must be declared in each subclass of StoredContribution, like this: @@ -688,16 +762,14 @@ class StoredContribution(Contribution): objects = StoredContributionManager() - - #_____ Meta parameters __________ + # _____ Meta parameters __________ class Meta(Contribution.Meta): abstract = True + # _____ Methods __________ - #_____ Methods __________ - - def hashed_path(self, extension=''): + def hashed_path(self, extension=""): """Relative path of a file belonging to the object on the respective storage""" @@ -707,18 +779,15 @@ class StoredContribution(Contribution): str(self.version) + extension, ) - def declaration_filename(self): """Relative path of the declaration file on the storage""" - return self.hashed_path('.json') - + return self.hashed_path(".json") def description_filename(self): """Relative path of the description file on the storage""" - return self.hashed_path('.rst') - + return self.hashed_path(".rst") - #_____ Overrides __________ + # _____ Overrides __________ def save(self, *args, **kwargs): @@ -728,8 +797,7 @@ class StoredContribution(Contribution): # Invoke the base implementation super(StoredContribution, self).save(*args, **kwargs) - - #_____ Properties __________ + # _____ Properties __________ description = property(get_description, set_description) declaration = property(get_declaration, set_declaration) -- GitLab From 8d0131defa2804fe03269b5f637c9270bc9e56a4 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 16:20:26 +0200 Subject: [PATCH 05/17] [code/common][models] Use a named tuple to return accessibility information This makes things clearer and easier to understand. Current code using these methods can be use as is while new code can make use of the named fields. --- beat/web/code/models.py | 37 +++++++++++++++++++++---------------- beat/web/common/models.py | 28 ++++++++++++++++------------ 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/beat/web/code/models.py b/beat/web/code/models.py index ac9453fa..e42994ad 100755 --- a/beat/web/code/models.py +++ b/beat/web/code/models.py @@ -25,6 +25,8 @@ # # ############################################################################### +from collections import namedtuple + from django.db import models from django.db.models import Q from django.contrib.auth.models import User @@ -228,6 +230,10 @@ def get_source_code(instance): # ---------------------------------------------------------- +AccessibilityInfo = namedtuple( + "Accessibility", ["has_access", "is_opensource", "accessibility"] +) + class Code(StoredContribution): @@ -666,47 +672,46 @@ class Code(StoredContribution): if self.author == user: if self.sharing == Contribution.PRIVATE: - return (True, False, "private") + return AccessibilityInfo(True, False, "private") elif self.sharing == Contribution.PUBLIC: - return (True, True, "public") + return AccessibilityInfo(True, True, "public") else: - return (True, False, "confidential") - + return AccessibilityInfo(True, False, "confidential") else: if self.sharing == Contribution.PRIVATE: - return (False, False, None) + return AccessibilityInfo(False, False, None) elif self.sharing == Contribution.PUBLIC: - return (True, True, "public") + return AccessibilityInfo(True, True, "public") elif not without_usable and (self.sharing == Contribution.USABLE): - return (True, False, "confidential") + return AccessibilityInfo(True, False, "confidential") elif not user.is_anonymous(): if self.shared_with.filter(id=user.id).exists() or ( self.shared_with_team.filter(members=user).count() > 0 ): - return (True, True, "confidential") + return AccessibilityInfo(True, True, "confidential") elif not without_usable and ( self.usable_by.filter(id=user.id).exists() or (self.usable_by_team.filter(members=user).count() > 0) ): - return (True, False, "confidential") + return AccessibilityInfo(True, False, "confidential") - return (False, False, None) + return AccessibilityInfo(False, False, None) def _accessibility_for_team(self, team, without_usable=False): """Team specific accessibility check """ if self.sharing == Contribution.PRIVATE: - return (False, False, None) + return AccessibilityInfo(False, False, None) elif self.sharing == Contribution.PUBLIC: - return (True, True, "public") + return AccessibilityInfo(True, True, "public") elif not without_usable and (self.sharing == Contribution.USABLE): - return (True, False, "confidential") + return AccessibilityInfo(True, False, "confidential") elif self.shared_with_team.filter(id=team.id).exists(): - return (True, True, "confidential") + return AccessibilityInfo(True, True, "confidential") elif not without_usable and self.usable_by_team.filter(id=team.id).exists(): - return (True, False, "confidential") + return AccessibilityInfo(True, False, "confidential") - return (False, False, None) + return AccessibilityInfo(False, False, None) # _____ Protected methods __________ diff --git a/beat/web/common/models.py b/beat/web/common/models.py index ffc0a5a5..590670c6 100755 --- a/beat/web/common/models.py +++ b/beat/web/common/models.py @@ -25,6 +25,8 @@ # # ############################################################################### +from collections import namedtuple + from django.db import models from django.conf import settings from django.db.models import Q @@ -77,6 +79,9 @@ class ShareableManager(models.Manager): # ---------------------------------------------------------- +AccessibilityInfo = namedtuple("Accessibility", ["has_access", "accessibility"]) + + class Shareable(models.Model): # _____ Constants __________ @@ -343,36 +348,35 @@ class Shareable(models.Model): if hasattr(self, "author") and self.author == user: if self.sharing == Shareable.PRIVATE: - return (True, "private") + return AccessibilityInfo(True, "private") elif self.sharing == Shareable.PUBLIC: - return (True, "public") + return AccessibilityInfo(True, "public") else: - return (True, "confidential") - + return AccessibilityInfo(True, "confidential") else: if self.sharing == Shareable.PRIVATE: - return (False, None) + return AccessibilityInfo(False, None) elif self.sharing == Shareable.PUBLIC: - return (True, "public") + return AccessibilityInfo(True, "public") elif not user.is_anonymous(): if self.shared_with.filter(id=user.id).exists() or ( self.shared_with_team.filter(members=user).count() > 0 ): - return (True, "confidential") + return AccessibilityInfo(True, "confidential") - return (False, None) + return AccessibilityInfo(False, None) def _accessibility_for_team(self, team, without_usable=False): """Team specific accessibility check """ if self.sharing == Shareable.PRIVATE: - return (False, None) + return AccessibilityInfo(False, None) elif self.sharing == Shareable.PUBLIC: - return (True, "public") + return AccessibilityInfo(True, "public") elif self.shared_with_team.filter(id=team.id).exists(): - return (True, "confidential") + return AccessibilityInfo(True, "confidential") - return (False, None) + return AccessibilityInfo(False, None) def _update_sharing(self, sharing, users, teams): if sharing != self.sharing: -- GitLab From 951a186f144d98e8de549ab9cee7ac529e35fd22 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 17:52:13 +0200 Subject: [PATCH 06/17] [common][serializers] Refactor creation serializer and a version as a mandatory field The original code would automatically generate a number based on the previous version if any or set it to one. This has the drawback of generating cryptic errors if for example someone sends an asset with a version number higher than the one available on the platform without providing historical data. To avoid that, version is now a mandatory field. When doing local development, it's already assigned. This allows for better handling of posted data. The refactored code makes only use of the database rather than mixing prefix and database usage. --- beat/web/common/serializers.py | 181 ++++++++++++++------------------- 1 file changed, 76 insertions(+), 105 deletions(-) diff --git a/beat/web/common/serializers.py b/beat/web/common/serializers.py index a404df16..05344e4d 100644 --- a/beat/web/common/serializers.py +++ b/beat/web/common/serializers.py @@ -25,11 +25,13 @@ # # ############################################################################### -from django.conf import settings from django.contrib.auth.models import User from django.utils import six +from django.db.models import CharField, Value as V +from django.db.models.functions import Concat + from rest_framework import serializers from ..team.models import Team @@ -295,41 +297,12 @@ class ContributionSerializer(VersionableSerializer): # ---------------------------------------------------------- -class MapDot(dict): - def __init__(self, *args, **kwargs): - super(MapDot, self).__init__(*args, **kwargs) - for arg in args: - if isinstance(arg, dict): - for k, v in arg.items(): - self[k] = v - - if kwargs: - for k, v in kwargs.items(): - self[k] = v - - def __getattr__(self, attr): - return self.get(attr) - - def __setattr__(self, key, value): - self.__setitem__(key, value) - - def __setitem__(self, key, value): - super(MapDot, self).__setitem__(key, value) - self.__dict__.update({key: value}) - - def __delattr__(self, item): - self.__delitem__(item) - - def __delitem__(self, key): - super(MapDot, self).__delitem__(key) - del self.__dict__[key] - - class ContributionCreationSerializer(serializers.ModelSerializer): declaration = JSONSerializerField(required=False) description = serializers.CharField(required=False, allow_blank=True) fork_of = serializers.JSONField(required=False) previous_version = serializers.CharField(required=False) + version = serializers.IntegerField(min_value=1) class Meta: fields = [ @@ -339,9 +312,24 @@ class ContributionCreationSerializer(serializers.ModelSerializer): "declaration", "previous_version", "fork_of", + "version", ] beat_core_class = None + def validate_fork_of(self, fork_of): + if "previous_version" in self.initial_data: + raise serializers.ValidationError( + "fork_of and previous_version cannot appear together" + ) + return fork_of + + def validate_previous_version(self, previous_version): + if "fork_of" in self.initial_data: + raise serializers.ValidationError( + "previous_version and fork_of cannot appear together" + ) + return previous_version + def validate_description(self, description): if description.find("\\") >= 0: # was escaped, unescape description = description.decode("string_escape") @@ -351,102 +339,85 @@ class ContributionCreationSerializer(serializers.ModelSerializer): user = self.context.get("user") name = self.Meta.model.sanitize_name(data["name"]) data["name"] = name + version = data.get("version") - if "previous_version" in data: - if self.Meta.beat_core_class is not None: - previous_version_asset = self.Meta.beat_core_class.Storage( - settings.PREFIX, data["previous_version"] + # If version is not one then it's necessarily a new version + # forks start at one + if version > 1 and "previous_version" not in data: + raise serializers.ValidationError( + "{} {} version {} incomplete history data posted".format( + self.Meta.model.__name__.lower(), name, version ) - if previous_version_asset.username is None: - previous_version_asset.username = user.username - else: - previous_version_asset = MapDot() - previous_version_asset["username"] = user.username - previous_version_asset["name"] = name - previous_version_asset["version"] = data["previous_version"] - data["data"] = json.dumps(data["data"]) - - else: - previous_version_asset = None + ) - if "fork_of" in data: - if self.Meta.beat_core_class is not None: - fork_of_asset = self.Meta.beat_core_class.Storage( - settings.PREFIX, data["fork_of"] + if self.Meta.model.objects.filter( + author__username__iexact=user, name=name, version=version + ).exists(): + raise serializers.ValidationError( + "{} {} version {} already exists on this account".format( + self.Meta.model.__name__.lower(), name, version ) - if fork_of_asset.username is None: - fork_of_asset.username = user.username - else: - fork_of_asset = MapDot() - fork_elem = data["fork_of"] - fork_of_asset["username"] = fork_elem["username"] - fork_of_asset["name"] = fork_elem["name"] - fork_of_asset["version"] = fork_elem["version"] - data["data"] = json.dumps(data["data"]) + ) - else: - fork_of_asset = None + previous_version = data.get("previous_version") + fork_of = data.get("fork_of") - # Retrieve the previous version (if applicable) - if previous_version_asset is not None: + if previous_version is not None: try: - previous_version = self.Meta.model.objects.get( - author__username__iexact=previous_version_asset.username, - name=previous_version_asset.name, - version=previous_version_asset.version, - ) + previous_object = self.Meta.model.objects.annotate( + fullname=Concat( + "author__username", + V("/"), + "name", + V("/"), + "version", + output_field=CharField(), + ) + ).get(fullname=previous_version) except self.Meta.model.DoesNotExist: raise serializers.ValidationError( "{} '{}' not found".format( - self.Meta.model.__name__, previous_version_asset.fullname + self.Meta.model.__name__, previous_version ) ) - - has_access, _, _ = previous_version.accessibility_for(user) - if not has_access: + accessibility_infos = previous_object.accessibility_for(user) + if not accessibility_infos.has_access: raise serializers.ValidationError("No access allowed") - data["previous_version"] = previous_version - # Retrieve the forked algorithm (if applicable) - if fork_of_asset is not None: - try: - fork_of = self.Meta.model.objects.get( - author__username__iexact=fork_of_asset.username, - name=fork_of_asset.name, - version=fork_of_asset.version, - ) - except self.Meta.model.DoesNotExist: + if version - previous_object.version != 1: raise serializers.ValidationError( - "{} '{}' not found".format( - self.Meta.model.__name__, fork_of_asset.fullname + "The requested version ({}) for this {} does not match" + "the standard increment with {}".format( + version, self.Meta.model.__name__, previous_object.version ) ) + data["previous_version"] = previous_object - has_access, _, _ = fork_of.accessibility_for(user) - if not has_access: - raise serializers.ValidationError("No access allowed") - data["fork_of"] = fork_of - - # Determine the version number - last_version = None - - if previous_version_asset is not None: - if (previous_version_asset.username == user.username) and ( - previous_version_asset.name == name - ): - last_version = self.Meta.model.objects.filter( - author=user, name=name - ).order_by("-version")[0] + elif fork_of is not None: + if version > 1: + raise serializers.ValidationError("A fork starts at 1") - if last_version is None: - if self.Meta.model.objects.filter(author=user, name=name).count() > 0: + try: + forked_of_object = self.Meta.model.objects.annotate( + fullname=Concat( + "author__username", + V("/"), + "name", + V("/"), + "version", + output_field=CharField(), + ) + ).get(fullname=fork_of) + except self.Meta.model.DoesNotExist: raise serializers.ValidationError( - "This {} name already exists on this account".format( - self.Meta.model.__name__.lower() + "{} '{}' fork origin not found".format( + self.Meta.model.__name__, fork_of ) ) - - data["version"] = last_version.version + 1 if last_version is not None else 1 + accessibility_infos = forked_of_object.accessibility_for(user) + if not accessibility_infos.has_access: + raise serializers.ValidationError("No access allowed") + data["fork_of"] = forked_of_object return data -- GitLab From 5f570c006869f400d0ffe665e044fbe0ffe6424d Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 17:54:12 +0200 Subject: [PATCH 07/17] [settings][test] Use the full path to the database as database name This mimicks the Linux behaviour and allows to properly run the beat/beat.cmdline tests on macOS. --- beat/web/settings/test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/beat/web/settings/test.py b/beat/web/settings/test.py index 8ac050df..1abd6fa8 100755 --- a/beat/web/settings/test.py +++ b/beat/web/settings/test.py @@ -48,7 +48,9 @@ if platform.system() == "Linux": database_name = os.path.join(shm_path, "test.sqlite3") else: - database_name = "test.sqlite3" + here = os.path.dirname(os.path.realpath(__file__)) + database_name = os.path.join(here, "test.sqlite3") + DATABASES["default"]["NAME"] = database_name # noqa DATABASES["default"]["TEST"] = {"NAME": DATABASES["default"]["NAME"]} # noqa DATABASES["default"]["OPTIONS"]["timeout"] = 30 # noqa -- GitLab From 8e824c0bfdc27ed290bd061a4df46ff2d9f16e6b Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 17:55:29 +0200 Subject: [PATCH 08/17] [algorithms][tests][api] Add missing version field to concerned tests --- beat/web/algorithms/tests/tests_api.py | 29 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/beat/web/algorithms/tests/tests_api.py b/beat/web/algorithms/tests/tests_api.py index 7c7ada18..3c68aa8f 100755 --- a/beat/web/algorithms/tests/tests_api.py +++ b/beat/web/algorithms/tests/tests_api.py @@ -684,7 +684,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "valid-name1", "description": "blah"}), + json.dumps({"name": "valid-name1", "version": 1, "description": "blah"}), content_type="application/json", ) @@ -710,7 +710,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "valid-name1"}), + json.dumps({"name": "valid-name1", "version": 1}), content_type="application/json", ) @@ -735,7 +735,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "invalid name1", "description": "blah"}), + json.dumps({"name": "invalid name1", "version": 1, "description": "blah"}), content_type="application/json", ) @@ -761,7 +761,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "invalid name1"}), + json.dumps({"name": "invalid name1", "version": 1}), content_type="application/json", ) @@ -789,6 +789,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "valid-name1", + "version": 1, "description": "blah", "declaration": AlgorithmsAPIBase.DECLARATION, "code": AlgorithmsAPIBase.CODE, @@ -824,6 +825,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "valid-name1", + "version": 1, "declaration": AlgorithmsAPIBase.DECLARATION, "code": AlgorithmsAPIBase.CODE, } @@ -857,6 +859,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "invalid name1", + "version": 1, "description": "blah", "declaration": AlgorithmsAPIBase.DECLARATION, "code": AlgorithmsAPIBase.CODE, @@ -892,6 +895,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "invalid name1", + "version": 1, "declaration": AlgorithmsAPIBase.DECLARATION, "code": AlgorithmsAPIBase.CODE, } @@ -922,7 +926,9 @@ class AlgorithmCreation(AlgorithmsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "usable_by_one_user", "description": "blah"}), + json.dumps( + {"name": "usable_by_one_user", "version": 1, "description": "blah"} + ), content_type="application/json", ) @@ -933,7 +939,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "usable_by_one_user"}), + json.dumps({"name": "usable_by_one_user", "version": 1}), content_type="application/json", ) @@ -944,7 +950,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "algorithm 4", "description": "blah"}), + json.dumps({"name": "algorithm 4", "version": 1, "description": "blah"}), content_type="application/json", ) @@ -955,7 +961,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "algorithm 4"}), + json.dumps({"name": "algorithm 4", "version": 1}), content_type="application/json", ) @@ -969,6 +975,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "usable_by_one_user", + "version": 1, "description": "blah", "declaration": AlgorithmsAPIBase.DECLARATION, "code": AlgorithmsAPIBase.CODE, @@ -987,6 +994,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "usable_by_one_user", + "version": 1, "declaration": AlgorithmsAPIBase.DECLARATION, "code": AlgorithmsAPIBase.CODE, } @@ -1004,6 +1012,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "algorithm 4", + "version": 1, "description": "blah", "declaration": AlgorithmsAPIBase.DECLARATION, "code": AlgorithmsAPIBase.CODE, @@ -1022,6 +1031,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "algorithm 4", + "version": 1, "declaration": AlgorithmsAPIBase.DECLARATION, "code": AlgorithmsAPIBase.CODE, } @@ -1039,6 +1049,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "valid-name1", + "version": 1, "description": "blah", "declaration": AlgorithmsAPIBase.CXX_DECLARATION, } @@ -1085,6 +1096,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "valid-name1", + "version": 1, "description": "blah", "declaration": AlgorithmsAPIBase.CXX_DECLARATION, "fork_of": "jackdoe/binary_personal/1", @@ -1124,6 +1136,7 @@ class AlgorithmCreation(AlgorithmsAPIBase): json.dumps( { "name": "binary_personal", + "version": 2, "description": "blah", "declaration": AlgorithmsAPIBase.CXX_DECLARATION, "previous_version": "jackdoe/binary_personal/1", -- GitLab From 0ea0c7aae3cce93a56df7e55f1bd251a472c3d73 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 17:59:51 +0200 Subject: [PATCH 09/17] [dataformats][tests][api] Add missing version field to concerned tests --- beat/web/dataformats/tests/tests_api.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/beat/web/dataformats/tests/tests_api.py b/beat/web/dataformats/tests/tests_api.py index b0b5201a..79f93fd7 100644 --- a/beat/web/dataformats/tests/tests_api.py +++ b/beat/web/dataformats/tests/tests_api.py @@ -293,7 +293,9 @@ class DataFormatCreation(DataFormatsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "valid-name1", "short_description": "blah"}), + json.dumps( + {"name": "valid-name1", "version": 1, "short_description": "blah"} + ), content_type="application/json", ) @@ -319,7 +321,7 @@ class DataFormatCreation(DataFormatsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "valid-name1"}), + json.dumps({"name": "valid-name1", "version": 1}), content_type="application/json", ) @@ -345,7 +347,9 @@ class DataFormatCreation(DataFormatsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "invalid name1", "short_description": "blah"}), + json.dumps( + {"name": "invalid name1", "version": 1, "short_description": "blah"} + ), content_type="application/json", ) @@ -371,7 +375,7 @@ class DataFormatCreation(DataFormatsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "invalid name1"}), + json.dumps({"name": "invalid name1", "version": 1}), content_type="application/json", ) @@ -397,7 +401,7 @@ class DataFormatCreation(DataFormatsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "format2", "description": "blah"}), + json.dumps({"name": "format2", "version": 1, "description": "blah"}), content_type="application/json", ) @@ -440,6 +444,7 @@ class DataFormatCreation(DataFormatsAPIBase): json.dumps( { "name": "valid-name1", + "version": 1, "short_description": "blah", "declaration": {"value": "int32"}, } -- GitLab From 4b2729554133583614a32797ad59e20189f82807 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 18:01:37 +0200 Subject: [PATCH 10/17] [libraries][tests][api] Add missing version field to concerned tests --- beat/web/libraries/tests/tests_api.py | 29 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/beat/web/libraries/tests/tests_api.py b/beat/web/libraries/tests/tests_api.py index db99e68b..7b6e62b4 100644 --- a/beat/web/libraries/tests/tests_api.py +++ b/beat/web/libraries/tests/tests_api.py @@ -421,7 +421,7 @@ class LibraryCreation(LibrariesAPIBase): response = self.client.post( self.url, - json.dumps({"name": "valid-name1", "description": "blah"}), + json.dumps({"name": "valid-name1", "version": 1, "description": "blah"}), content_type="application/json", ) @@ -445,7 +445,7 @@ class LibraryCreation(LibrariesAPIBase): response = self.client.post( self.url, - json.dumps({"name": "valid-name1"}), + json.dumps({"name": "valid-name1", "version": 1}), content_type="application/json", ) @@ -468,7 +468,7 @@ class LibraryCreation(LibrariesAPIBase): response = self.client.post( self.url, - json.dumps({"name": "invalid name1", "description": "blah"}), + json.dumps({"name": "invalid name1", "version": 1, "description": "blah"}), content_type="application/json", ) @@ -492,7 +492,7 @@ class LibraryCreation(LibrariesAPIBase): response = self.client.post( self.url, - json.dumps({"name": "invalid name1"}), + json.dumps({"name": "invalid name1", "version": 1}), content_type="application/json", ) @@ -518,6 +518,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "valid-name1", + "version": 1, "description": "blah", "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, @@ -549,6 +550,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "valid-name1", + "version": 1, "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, } @@ -578,6 +580,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "invalid name1", + "version": 1, "description": "blah", "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, @@ -610,6 +613,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "invalid name1", + "version": 1, "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, } @@ -636,7 +640,9 @@ class LibraryCreation(LibrariesAPIBase): response = self.client.post( self.url, - json.dumps({"name": "usable_by_one_user", "description": "blah"}), + json.dumps( + {"name": "usable_by_one_user", "version": 1, "description": "blah"} + ), content_type="application/json", ) @@ -647,7 +653,7 @@ class LibraryCreation(LibrariesAPIBase): response = self.client.post( self.url, - json.dumps({"name": "usable_by_one_user"}), + json.dumps({"name": "usable_by_one_user", "version": 1}), content_type="application/json", ) @@ -658,7 +664,7 @@ class LibraryCreation(LibrariesAPIBase): response = self.client.post( self.url, - json.dumps({"name": "library 4", "description": "blah"}), + json.dumps({"name": "library 4", "version": 1, "description": "blah"}), content_type="application/json", ) @@ -668,7 +674,9 @@ class LibraryCreation(LibrariesAPIBase): self.login_jackdoe() response = self.client.post( - self.url, json.dumps({"name": "library 4"}), content_type="application/json" + self.url, + json.dumps({"name": "library 4", "version": 1}), + content_type="application/json", ) self.checkResponse(response, 400, content_type="application/json") @@ -681,6 +689,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "usable_by_one_user", + "version": 1, "description": "blah", "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, @@ -699,6 +708,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "usable_by_one_user", + "version": 1, "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, } @@ -716,6 +726,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "library 4", + "version": 1, "description": "blah", "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, @@ -734,6 +745,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "", + "version": 1, "description": "blah", "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, @@ -753,6 +765,7 @@ class LibraryCreation(LibrariesAPIBase): json.dumps( { "name": "library 4", + "version": 1, "declaration": LibrariesAPIBase.DECLARATION, "code": LibrariesAPIBase.CODE, } -- GitLab From 7668180bd6da2a2f9bc25c8bd72971ea9bc13c1b Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 18:02:23 +0200 Subject: [PATCH 11/17] [plotters][tests] Add missing version field to concerned tests --- beat/web/plotters/tests.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/beat/web/plotters/tests.py b/beat/web/plotters/tests.py index 0ddc21ae..72c84c89 100644 --- a/beat/web/plotters/tests.py +++ b/beat/web/plotters/tests.py @@ -81,6 +81,7 @@ class PlotterParameterTestCase(APITestCase): self.data_plotter = { "author": self.plot.username, "name": "plotter_test", + "version": 1, "short_description": "some description plotter", "description": "some longer description plotter", "declaration": { @@ -100,6 +101,7 @@ class PlotterParameterTestCase(APITestCase): self.data = { "name": "plotterparameter1", + "version": 1, "short_description": "some description", "description": "some longer description", "plotter": self.plotter.id, @@ -107,6 +109,7 @@ class PlotterParameterTestCase(APITestCase): self.data2 = { "name": "plotterparameter2", + "version": 1, "short_description": "some description2", "description": "some longer description2", "plotter": self.plotter.id, @@ -154,7 +157,7 @@ class PlotterParameterCreationTestCase(PlotterParameterTestCase): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( json.loads(response.content)["non_field_errors"][0], - "This plotterparameter name already exists on this account", + "plotterparameter plotterparameter1 version 1 already exists on this account", ) -- GitLab From ffb9e4bd1628569e9a1dde98c139e41bfdf3accc Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 9 Apr 2020 18:04:32 +0200 Subject: [PATCH 12/17] [toolchains][tests] Add missing version field to concerned tests --- beat/web/toolchains/tests.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/beat/web/toolchains/tests.py b/beat/web/toolchains/tests.py index da09521e..3de38a63 100644 --- a/beat/web/toolchains/tests.py +++ b/beat/web/toolchains/tests.py @@ -44,7 +44,7 @@ from ..common.testutils import BaseTestCase from ..common.testutils import tearDownModule # noqa test runner will call it from ..common.testutils import get_algorithms_from_data -TEST_PWD = "1234" +TEST_PWD = "1234" # nosec class ToolchainsCreationFunction(TestCase): @@ -710,7 +710,7 @@ class ToolchainCreation(ToolchainsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "valid-name1"}), + json.dumps({"name": "valid-name1", "version": 1}), content_type="application/json", ) @@ -736,7 +736,7 @@ class ToolchainCreation(ToolchainsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "invalid name1"}), + json.dumps({"name": "invalid name1", "version": 1}), content_type="application/json", ) @@ -761,7 +761,9 @@ class ToolchainCreation(ToolchainsAPIBase): self.login_jackdoe() response = self.client.post( - self.url, json.dumps({"name": "personal"}), content_type="application/json" + self.url, + json.dumps({"name": "personal", "version": 1}), + content_type="application/json", ) self.checkResponse(response, 400) @@ -771,7 +773,7 @@ class ToolchainCreation(ToolchainsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "toolchain 4"}), + json.dumps({"name": "toolchain 4", "version": 1}), content_type="application/json", ) @@ -782,7 +784,7 @@ class ToolchainCreation(ToolchainsAPIBase): response = self.client.post( self.url, - json.dumps({"name": "new_toolchain", "description": "blah"}), + json.dumps({"name": "new_toolchain", "version": 1, "description": "blah"}), content_type="application/json", ) @@ -811,7 +813,11 @@ class ToolchainCreation(ToolchainsAPIBase): response = self.client.post( self.url, json.dumps( - {"name": "new_toolchain", "declaration": ToolchainsAPIBase.DECLARATION} + { + "name": "new_toolchain", + "version": 1, + "declaration": ToolchainsAPIBase.DECLARATION, + } ), content_type="application/json", ) @@ -839,6 +845,7 @@ class ToolchainCreation(ToolchainsAPIBase): json.dumps( { "name": "new_toolchain", + "version": 1, "declaration": ToolchainCreation.DECLARATION_INVALID, } ), @@ -856,6 +863,7 @@ class ToolchainCreation(ToolchainsAPIBase): json.dumps( { "name": "name1", + "version": 1, "description": "blah", "fork_of": "johndoe/toolchain1/1", } @@ -874,6 +882,7 @@ class ToolchainCreation(ToolchainsAPIBase): json.dumps( { "name": "name1", + "version": 1, "description": "blah", "fork_of": "johndoe/toolchain1/1", } @@ -890,7 +899,11 @@ class ToolchainCreation(ToolchainsAPIBase): response = self.client.post( url, json.dumps( - {"description": "blah", "fork_of": "jackdoe/public_for_one_user/1"} + { + "version": 1, + "description": "blah", + "fork_of": "jackdoe/public_for_one_user/1", + } ), content_type="application/json", ) @@ -909,6 +922,7 @@ class ToolchainCreation(ToolchainsAPIBase): json.dumps( { "name": "name1", + "version": 1, "description": "blah", "fork_of": "jackdoe/public_for_one_user/1", } @@ -928,6 +942,7 @@ class ToolchainCreation(ToolchainsAPIBase): json.dumps( { "name": "name1", + "version": 1, "description": "blah", "fork_of": "jackdoe/personal/1", } @@ -947,6 +962,7 @@ class ToolchainCreation(ToolchainsAPIBase): json.dumps( { "name": "name1", + "version": 1, "description": "blah", "fork_of": "jackdoe/public_for_all/1", } @@ -998,6 +1014,7 @@ class ToolchainCreation(ToolchainsAPIBase): json.dumps( { "name": "name1", + "version": 1, "description": "blah", "fork_of": "jackdoe/public_for_one_user/1", } @@ -1047,6 +1064,7 @@ class ToolchainCreation(ToolchainsAPIBase): json.dumps( { "name": "name1", + "version": 1, "description": "blah", "declaration": ToolchainsAPIBase.DECLARATION, "fork_of": "jackdoe/public_for_one_user/1", -- GitLab From c1b7a2c18864ec1d599737a0edda8ffd463e0f21 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 14 Apr 2020 11:28:42 +0200 Subject: [PATCH 13/17] [aglorithms][templates] Add version field when creating a new version --- beat/web/algorithms/templates/algorithms/edition.html | 1 + 1 file changed, 1 insertion(+) diff --git a/beat/web/algorithms/templates/algorithms/edition.html b/beat/web/algorithms/templates/algorithms/edition.html index a64bdac1..73667155 100644 --- a/beat/web/algorithms/templates/algorithms/edition.html +++ b/beat/web/algorithms/templates/algorithms/edition.html @@ -331,6 +331,7 @@ function setupEditor(algorithm, dataformats, libraries) {% if not edition %} {% if algorithm_version > 1 and not fork_of %} name: '{{ algorithm_name }}', + version: '{{ algorithm_version }}', previous_version:'{{ algorithm_author }}/{{ algorithm_name }}/{{ algorithm_version|add:-1 }}', {% else %} name: $('#algorithm_name')[0].value.trim(), -- GitLab From 44fa85659db28a5c8689213286b179dc310fea6d Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 14 Apr 2020 11:28:52 +0200 Subject: [PATCH 14/17] [libraries][templates] Add version field when creating a new version --- beat/web/libraries/templates/libraries/edition.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/beat/web/libraries/templates/libraries/edition.html b/beat/web/libraries/templates/libraries/edition.html index ab44d2d5..3f510647 100644 --- a/beat/web/libraries/templates/libraries/edition.html +++ b/beat/web/libraries/templates/libraries/edition.html @@ -2,21 +2,21 @@ {% comment %} * 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/. {% endcomment %} @@ -261,6 +261,7 @@ jQuery(document).ready(function() { data: JSON.stringify({ name: name, {% if op == 'new-version' %} + version: '{{ library.version | add:+1 }}', previous_version: '{{ library.fullname }}', {% endif %} {% if op == 'fork' %} -- GitLab From eae6f73de63c453fbc09af187f182723cc926e5a Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 14 Apr 2020 11:29:03 +0200 Subject: [PATCH 15/17] [toolchains][templates] Add version field when creating a new version --- beat/web/toolchains/templates/toolchains/edition.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/beat/web/toolchains/templates/toolchains/edition.html b/beat/web/toolchains/templates/toolchains/edition.html index 25f6db4b..295c539e 100644 --- a/beat/web/toolchains/templates/toolchains/edition.html +++ b/beat/web/toolchains/templates/toolchains/edition.html @@ -2,21 +2,21 @@ {% comment %} * 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/. {% endcomment %} @@ -159,6 +159,7 @@ function setupEditor(databases, toolchains) {% if not edition %} {% if toolchain_version > 1 %} name: '{{ toolchain_name }}', + version: '{{ toolchain_version }}', previous_version: '{{ toolchain_author }}/{{ toolchain_name }}/{{ toolchain_version|add:-1 }}', {% else %} name: $('#toolchain_name')[0].value.trim(), -- GitLab From 1818de0e3752b4f01f9de7bff3aeb4b8b3737ddf Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 14 Apr 2020 13:20:07 +0200 Subject: [PATCH 16/17] [dev] Update beat packages version - beat.core to 1.10.3 - beat.backend.python to 1.7.6 - beat.cmdline to 1.7.0 --- dev.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev.yml b/dev.yml index 5c410569..bf47412b 100644 --- a/dev.yml +++ b/dev.yml @@ -11,9 +11,9 @@ dependencies: - beat-devel=2020.01.09 # beat dependencies matching release.cfg - - beat.core=1.9.2 - - beat.backend.python=1.7.3 - - beat.cmdline=1.6.1 + - beat.core=1.10.3 + - beat.backend.python=1.7.6 + - beat.cmdline=1.7.1 # requirements.txt, they are indirectly pinned through the above - docopt -- GitLab From 89dd37100efe25b71973451792bd4b87158a6e4f Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 14 Apr 2020 13:20:26 +0200 Subject: [PATCH 17/17] [release] Update beat packages version - beat.core to 1.10.3 - beat.backend.python to 1.7.6 - beat.cmdline to 1.7.0 --- release.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/release.cfg b/release.cfg index 8b2e6f7f..914cafa1 100644 --- a/release.cfg +++ b/release.cfg @@ -25,9 +25,9 @@ eggs = ${buildout:eggs} interpreter = python [sources] -beat.core = git https://gitlab.idiap.ch/beat/beat.core.git rev=v1.9.2 -beat.cmdline = git https://gitlab.idiap.ch/beat/beat.cmdline.git rev=v1.6.1 -beat.backend.python = git https://gitlab.idiap.ch/beat/beat.backend.python.git rev=v1.7.3 +beat.core = git https://gitlab.idiap.ch/beat/beat.core.git rev=v1.10.3 +beat.cmdline = git https://gitlab.idiap.ch/beat/beat.cmdline.git rev=v1.7.1 +beat.backend.python = git https://gitlab.idiap.ch/beat/beat.backend.python.git rev=v1.7.6 [uwsgi] recipe = buildout.recipe.uwsgi -- GitLab