From 66d257584cb69158c1b30fb39526abae673b0d68 Mon Sep 17 00:00:00 2001
From: Philip ABBET <philip.abbet@idiap.ch>
Date: Tue, 15 Nov 2016 09:42:50 +0100
Subject: [PATCH] [algorithms, api] Add support for creating binary algorithms

---
 beat/web/algorithms/models.py          |  9 ++-
 beat/web/algorithms/tests/core.py      |  3 +
 beat/web/algorithms/tests/tests_api.py | 78 ++++++++++++++++++++++++--
 beat/web/code/models.py                | 16 ++++--
 beat/web/code/serializers.py           | 10 +++-
 5 files changed, 100 insertions(+), 16 deletions(-)
 mode change 100644 => 100755 beat/web/algorithms/models.py

diff --git a/beat/web/algorithms/models.py b/beat/web/algorithms/models.py
old mode 100644
new mode 100755
index 28864f022..9e9b90648
--- a/beat/web/algorithms/models.py
+++ b/beat/web/algorithms/models.py
@@ -93,13 +93,13 @@ class AlgorithmStorage(OverwriteStorage):
 class AlgorithmManager(CodeManager):
 
     def create_algorithm(self, author, name, short_description='',
-                         description='', declaration=None, code=None, version=1,
-                         previous_version=None, fork_of=None):
+                         description='', declaration=None, language=None, code=None,
+                         version=1, previous_version=None, fork_of=None):
 
         default = beat.core.algorithm.Algorithm(settings.PREFIX, data=None)
 
         return self.create_code(author, name, default, short_description,
-                                description, declaration, code, version,
+                                description, declaration, language, code, version,
                                 previous_version, fork_of)
 
 
@@ -307,6 +307,9 @@ class Algorithm(Code):
     def deletable(self):
         return (self.experiments.count() == 0) and super(Algorithm, self).deletable()
 
+    def valid(self):
+        return (self.source_code_file.name is not None) and (self.source_code_file.name != '')
+
 
     def core(self):
         return validate_algorithm(self.declaration)
diff --git a/beat/web/algorithms/tests/core.py b/beat/web/algorithms/tests/core.py
index ed684d664..8bebec3fe 100755
--- a/beat/web/algorithms/tests/core.py
+++ b/beat/web/algorithms/tests/core.py
@@ -320,10 +320,13 @@ class AlgorithmsTestCase(AlgorithmsBaseTestCase):
             declaration=declaration,
             )
         assert algorithm, errors
+        assert not algorithm.valid()
 
         algorithm.source_code = binary_data
         algorithm.save()
 
+        assert algorithm.valid()
+
         # Usable by one user
         (algorithm, errors) = Algorithm.objects.create_algorithm(
             author=user1,
diff --git a/beat/web/algorithms/tests/tests_api.py b/beat/web/algorithms/tests/tests_api.py
index 5604c781c..1bff20107 100755
--- a/beat/web/algorithms/tests/tests_api.py
+++ b/beat/web/algorithms/tests/tests_api.py
@@ -35,6 +35,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
 
 from ...dataformats.models import DataFormat
 from ...common.testutils import tearDownModule
+from ...code.models import Code
 
 import beat.core.algorithm
 
@@ -561,14 +562,17 @@ class AlgorithmCreation(AlgorithmsAPIBase):
                 "declaration (beyond white spaces): %r != %r" %  \
                 (in_storage, expected)
 
-        # set storage language so it can find the code
-        storage.language = in_storage['language']
+        if in_storage['language'] == 'python':
+            self.assertTrue(algorithm.valid())
 
-        in_storage = storage.code.try_load()
-        expected = code
+            # set storage language so it can find the code
+            storage.language = in_storage['language']
 
-        assert in_storage == expected, "There are differences on the " \
-                "code: %r != %r" % (in_storage, expected)
+            in_storage = storage.code.try_load()
+            expected = code
+
+            assert in_storage == expected, "There are differences on the " \
+                    "code: %r != %r" % (in_storage, expected)
 
 
     def test_no_creation_for_anonymous_user(self):
@@ -919,6 +923,68 @@ class AlgorithmCreation(AlgorithmsAPIBase):
         self.checkResponse(response, 400, content_type='application/json')
 
 
+    def test_cxx_algorithm_using_declaration(self):
+        self.client.login(username='jackdoe', password='1234')
+
+        response = self.client.post(self.url,
+            json.dumps({
+                'name': 'valid-name1',
+                'description': 'blah',
+                'declaration':AlgorithmsAPIBase.CXX_DECLARATION,
+            }), content_type='application/json')
+
+        url = reverse('api_algorithms:object', args=['jackdoe', 'valid-name1', 1])
+        content = self.checkResponse(response, 201,
+                                     content_type='application/json',
+                                     location=url)
+
+        self.assertTrue(isinstance(content, dict))
+        self.assertEqual(content['name'], 'valid-name1')
+        self.assertTrue(content['url'].endswith(url))
+
+        try:
+            algorithm = Algorithm.objects.get(author__username='jackdoe', name='valid-name1')
+        except:
+            self.assertTrue(False)
+
+        self.assertEqual(algorithm.short_description, '')
+        self.assertEqual(algorithm.description, 'blah')
+        self.assertEqual(algorithm.language, Code.CXX)
+        self.assertFalse(algorithm.valid())
+        self.checkAlgorithm(algorithm, declaration=AlgorithmsAPIBase.CXX_DECLARATION, code=None)
+
+
+    def test_cxx_algorithm_using_language(self):
+        self.client.login(username='jackdoe', password='1234')
+
+        response = self.client.post(self.url,
+            json.dumps({
+                'name': 'valid-name1',
+                'description': 'blah',
+                'language': 'cxx',
+            }), content_type='application/json')
+
+        url = reverse('api_algorithms:object', args=['jackdoe', 'valid-name1', 1])
+        content = self.checkResponse(response, 201,
+                                     content_type='application/json',
+                                     location=url)
+
+        self.assertTrue(isinstance(content, dict))
+        self.assertEqual(content['name'], 'valid-name1')
+        self.assertTrue(content['url'].endswith(url))
+
+        try:
+            algorithm = Algorithm.objects.get(author__username='jackdoe', name='valid-name1')
+        except:
+            self.assertTrue(False)
+
+        self.assertEqual(algorithm.short_description, '')
+        self.assertEqual(algorithm.description, 'blah')
+        self.assertEqual(algorithm.language, Code.CXX)
+        self.assertFalse(algorithm.valid())
+
+
+
 class AlgorithmUpdate(AlgorithmsAPIBase):
     def setUp(self):
         super(AlgorithmUpdate, self).setUp()
diff --git a/beat/web/code/models.py b/beat/web/code/models.py
index 8131372e8..768e8c542 100755
--- a/beat/web/code/models.py
+++ b/beat/web/code/models.py
@@ -51,12 +51,14 @@ 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):
+                      declaration=None, language=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)
+        return create(author=author, name=name, short_description=short_description,
+                      description=description, declaration=declaration, language=language,
+                      code=code, version=version, previous_version=previous_version,
+                      fork_of=fork_of)
 
     def for_user(self, user, add_public=False):
         if user.is_anonymous():
@@ -101,8 +103,8 @@ class CodeManager(StoredContributionManager):
         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):
+                    description='', declaration=None, language=None, code=None,
+                    version=1, previous_version=None, fork_of=None):
 
         # Create the database representation
         code_db = self.model(
@@ -122,6 +124,8 @@ class CodeManager(StoredContributionManager):
                 declaration = fork_of.declaration
             else:
                 declaration = default.data
+                if language is not None:
+                    declaration['language'] = language
         elif not(isinstance(declaration, dict)):
             declaration = simplejson.loads(declaration)
 
diff --git a/beat/web/code/serializers.py b/beat/web/code/serializers.py
index b50d101f6..75569fe62 100755
--- a/beat/web/code/serializers.py
+++ b/beat/web/code/serializers.py
@@ -43,9 +43,17 @@ import difflib
 
 class CodeCreationSerializer(ContributionCreationSerializer):
     code = serializers.CharField(required=False, allow_blank=True, trim_whitespace=False)
+    language = serializers.CharField(required=False, allow_blank=True, trim_whitespace=False)
 
     class Meta(ContributionCreationSerializer.Meta):
-        fields = ContributionCreationSerializer.Meta.fields + ['code']
+        fields = ContributionCreationSerializer.Meta.fields + ['code', 'language']
+
+    def validate_language(self, value):
+        try:
+            if len(filter(lambda x: x[1].lower() == value, iter(Code.CODE_LANGUAGE))) > 0:
+                return value
+        except:
+            raise serializers.ValidationError('Unsupported language: %s' % value)
 
 
 #----------------------------------------------------------
-- 
GitLab