diff --git a/beat/web/algorithms/templates/algorithms/edition.html b/beat/web/algorithms/templates/algorithms/edition.html
index a64bdac14c474bedfda37ee99b0eb1c9e600ab8b..73667155ed846d514853715fddb0a533d598d75d 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(),
diff --git a/beat/web/algorithms/tests/tests_api.py b/beat/web/algorithms/tests/tests_api.py
index 7c7ada18325a898c6a8c6874b542ee4cff1492ce..3c68aa8f43bb226cd06b5f0385f417d77ef4d926 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",
diff --git a/beat/web/code/models.py b/beat/web/code/models.py
index 2ff0f51e06124ca20dd9c4390cea6098c77250c3..e42994ad9cde44127b1f593315b451ccfc453faa 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
@@ -33,7 +35,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 +46,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 +100,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 +111,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 +159,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 +189,7 @@ class CodeManager(StoredContributionManager):
             elif fork_of is not None:
                 description = fork_of.description
             else:
-                description = ''
+                description = ""
 
         code_db.description = description
 
@@ -162,61 +197,66 @@ 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")
 
 
-#----------------------------------------------------------
+# ----------------------------------------------------------
+
+AccessibilityInfo = namedtuple(
+    "Accessibility", ["has_access", "is_opensource", "accessibility"]
+)
 
 
 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 +269,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 +310,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 +338,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 +350,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 +384,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 +415,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 +460,6 @@ class Code(StoredContribution):
 
         self._update_sharing(sharing, users, teams)
 
-
     def is_accessible(self, public, users=None, teams=None):
         errors = []
 
@@ -393,116 +467,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 +642,78 @@ 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 (<has_access>, <open_source>, <accessibility>), with <accessibility> being
            either 'public', 'private', 'confidential' """
 
         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')
-                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)
-
+                if self.shared_with.filter(id=user.id).exists() or (
+                    self.shared_with_team.filter(members=user).count() > 0
+                ):
+                    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 AccessibilityInfo(True, False, "confidential")
+
+        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 __________
+    # _____ Protected methods __________
 
     def _share_libraries(self, public, users, teams):
         # Retrieve and process the list of referenced libraries
@@ -598,7 +725,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)
diff --git a/beat/web/common/models.py b/beat/web/common/models.py
index 324a2f72409ca943bd60ff2cdc60d6cd5b27b1d4..590670c6031c65e76a40bc571df577818938eb79 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
@@ -49,7 +51,7 @@ import simplejson
 from collections import OrderedDict
 
 
-#----------------------------------------------------------
+# ----------------------------------------------------------
 
 
 class ShareableManager(models.Manager):
@@ -57,11 +59,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 +76,57 @@ class ShareableManager(models.Manager):
         return self.filter(sharing=Shareable.PUBLIC)
 
 
-#----------------------------------------------------------
+# ----------------------------------------------------------
+
+
+AccessibilityInfo = namedtuple("Accessibility", ["has_access", "accessibility"])
 
 
 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 +135,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 +144,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 +174,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 +236,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 +258,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,73 +297,86 @@ 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 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')
+                if self.shared_with.filter(id=user.id).exists() or (
+                    self.shared_with_team.filter(members=user).count() > 0
+                ):
+                    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 (False, None)
+            return AccessibilityInfo(True, "confidential")
 
+        return AccessibilityInfo(False, None)
 
     def _update_sharing(self, sharing, users, teams):
         if sharing != self.sharing:
@@ -339,81 +386,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 __________
+    # _____ 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'])
-
-    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 +466,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 +482,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 +573,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 +648,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 +689,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 +698,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 +766,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 +783,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 +801,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)
diff --git a/beat/web/common/serializers.py b/beat/web/common/serializers.py
index d3e6b3bcaf6b0e6b5c79b910e884bd6aee2db0c9..05344e4daf42551e3d9abad94887b866db62d9bb 100644
--- a/beat/web/common/serializers.py
+++ b/beat/web/common/serializers.py
@@ -25,17 +25,17 @@
 #                                                                             #
 ###############################################################################
 
-from django.conf import settings
 
-from django.core.exceptions import ValidationError
 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
-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 +44,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 +70,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 +87,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 +122,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 +140,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 +152,7 @@ class DynamicFieldsSerializer(serializers.ModelSerializer):
             self.fields.pop(field_name)
 
 
-#----------------------------------------------------------
+# ----------------------------------------------------------
 
 
 class ShareableSerializer(DynamicFieldsSerializer):
@@ -158,43 +164,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 +218,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 +258,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 +271,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,39 +291,10 @@ class ContributionSerializer(VersionableSerializer):
         description = obj.description
         if len(description) > 0:
             return ensure_html(description)
-        return ''
-
-
-#----------------------------------------------------------
-
-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})
+        return ""
 
-    def __delattr__(self, item):
-        self.__delitem__(item)
 
-    def __delitem__(self, key):
-        super(MapDot, self).__delitem__(key)
-        del self.__dict__[key]
+# ----------------------------------------------------------
 
 
 class ContributionCreationSerializer(serializers.ModelSerializer):
@@ -313,101 +302,122 @@ class ContributionCreationSerializer(serializers.ModelSerializer):
     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 = ['name', 'short_description', 'description',
-                  'declaration', 'previous_version',
-                  'fork_of']
+        fields = [
+            "name",
+            "short_description",
+            "description",
+            "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')
+        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
-
-        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'])
-                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'])
-
-        else:
-            previous_version_id = None
-
-        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'])
-                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'])
-
-        else:
-            fork_of_id = None
-
-        # Retrieve the previous version (if applicable)
-        if previous_version_id is not None:
+        user = self.context.get("user")
+        name = self.Meta.model.sanitize_name(data["name"])
+        data["name"] = name
+        version = data.get("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 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
+                )
+            )
+
+        previous_version = data.get("previous_version")
+        fork_of = data.get("fork_of")
+
+        if previous_version 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)
-            except:
-                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
-
-        # Retrieve the forked algorithm (if applicable)
-        if fork_of_id is not None:
+                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
+                    )
+                )
+            accessibility_infos = previous_object.accessibility_for(user)
+            if not accessibility_infos.has_access:
+                raise serializers.ValidationError("No access allowed")
+
+            if version - previous_object.version != 1:
+                raise serializers.ValidationError(
+                    "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
+
+        elif fork_of is not None:
+            if version > 1:
+                raise serializers.ValidationError("A fork starts at 1")
+
             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))
-
-            is_accessible = fork_of.accessibility_for(user)
-            if not is_accessible[0]:
-                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 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()))
-
-        data['version'] = (last_version.version + 1 if last_version is not None else 1)
+                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(
+                    "{} '{}' fork origin not found".format(
+                        self.Meta.model.__name__, fork_of
+                    )
+                )
+            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
 
diff --git a/beat/web/dataformats/tests/tests_api.py b/beat/web/dataformats/tests/tests_api.py
index b0b5201a64fadc36bf7be3c323cc5e2c2251593a..79f93fd7bd9657e7397e0a752c1e013927e1b34f 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"},
                 }
diff --git a/beat/web/libraries/templates/libraries/edition.html b/beat/web/libraries/templates/libraries/edition.html
index ab44d2d58af716cce99d308bc832bfc62387216a..3f51064712eab3c4e1b33b344ff2347686aa645e 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' %}
diff --git a/beat/web/libraries/tests/tests_api.py b/beat/web/libraries/tests/tests_api.py
index db99e68bb837e733065483dadfbed8538ab696f4..7b6e62b4de4ee501e50285169c9505d9e51ac6df 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,
                 }
diff --git a/beat/web/plotters/tests.py b/beat/web/plotters/tests.py
index 0ddc21ae5f2cb62db602185dba021559188878aa..72c84c893425e3f15aa4a7104ac5ad5021b54594 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",
         )
 
 
diff --git a/beat/web/settings/test.py b/beat/web/settings/test.py
index 8ac050df73c4f69d56ea652e9c42265e631fd114..1abd6fa8af1599f2824731bf8fd3f70b65837faf 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
diff --git a/beat/web/toolchains/templates/toolchains/edition.html b/beat/web/toolchains/templates/toolchains/edition.html
index 25f6db4b3a7fe53348e56895f106ad57983f0936..295c539e7f9e96a78f9180f92074d44355b700cf 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(),
diff --git a/beat/web/toolchains/tests.py b/beat/web/toolchains/tests.py
index da09521e90bcc431f783765e9414de60ac8a1055..3de38a63412b3d69a93de77ea8db61edfdd7f218 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",
diff --git a/dev.yml b/dev.yml
index 5c4105699e0b64e91134e9e2be2694e13f5f3eb3..bf47412b63c3e98f7d2ddfd37dd909dd81680a8b 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
diff --git a/release.cfg b/release.cfg
index 8b2e6f7faca67d3dac60da29300334836f8b00f8..914cafa1bb69e204fe651a5cad448603d9458918 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