From f58d6f8478d5eecd118ed521d6c5a2f619e5f212 Mon Sep 17 00:00:00 2001
From: Samuel Gaist <samuel.gaist@idiap.ch>
Date: Fri, 12 Apr 2019 17:32:57 +0200
Subject: [PATCH] [protocoltemplate] Implement ProcotolTemplate support

---
 beat/core/protocoltemplate.py            | 123 +++++++++++++++++++++++
 beat/core/schema/protocoltemplate/1.json |  58 +++++++++++
 beat/core/test/test_protocoltemplate.py  |  65 ++++++++++++
 3 files changed, 246 insertions(+)
 create mode 100644 beat/core/protocoltemplate.py
 create mode 100644 beat/core/schema/protocoltemplate/1.json
 create mode 100644 beat/core/test/test_protocoltemplate.py

diff --git a/beat/core/protocoltemplate.py b/beat/core/protocoltemplate.py
new file mode 100644
index 00000000..11574f56
--- /dev/null
+++ b/beat/core/protocoltemplate.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###################################################################################
+#                                                                                 #
+# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/               #
+# Contact: beat.support@idiap.ch                                                  #
+#                                                                                 #
+# Redistribution and use in source and binary forms, with or without              #
+# modification, are permitted provided that the following conditions are met:     #
+#                                                                                 #
+# 1. Redistributions of source code must retain the above copyright notice, this  #
+# list of conditions and the following disclaimer.                                #
+#                                                                                 #
+# 2. Redistributions in binary form must reproduce the above copyright notice,    #
+# this list of conditions and the following disclaimer in the documentation       #
+# and/or other materials provided with the distribution.                          #
+#                                                                                 #
+# 3. Neither the name of the copyright holder nor the names of its contributors   #
+# may be used to endorse or promote products derived from this software without   #
+# specific prior written permission.                                              #
+#                                                                                 #
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED   #
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE          #
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE    #
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL      #
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR      #
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER      #
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   #
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   #
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.            #
+#                                                                                 #
+###################################################################################
+
+
+"""
+================
+protocoltemplate
+================
+
+Validation of protocoltemplate
+
+Forward importing from :py:mod:`beat.backend.python.protocoltemplate`:
+:py:class:`beat.backend.python.protocoltemplate.Storage`
+"""
+
+import six
+
+from . import schema
+
+from beat.backend.python.protocoltemplate import Storage
+from beat.backend.python.protocoltemplate import (
+    ProtocolTemplate as BackendProtocolTemplate,
+)
+
+
+class ProtocolTemplate(BackendProtocolTemplate):
+    """Protocol template define the design of the database.
+
+
+    Parameters:
+
+      prefix (str): Establishes the prefix of your installation.
+
+      data (dict, str): The piece of data representing the protocol templates.
+        It must validate against the schema defined for protocol templates. If a
+        string is passed, it is supposed to be a valid path to protocol template
+        in the designated prefix area.
+
+      dataformat_cache (:py:class:`dict`, Optional): A dictionary mapping
+        dataformat names to loaded dataformats. This parameter is optional and,
+        if passed, may greatly speed-up protocol template loading times as
+        dataformats that are already loaded may be re-used. If you use this
+        parameter, you must guarantee that the cache is refreshed as appropriate
+        in case the underlying dataformats change.
+
+
+    Attributes:
+
+      name (str): The full, valid name of this protocol template
+
+      description (str): The short description string, loaded from the JSON
+        file if one was set.
+
+      documentation (str): The full-length docstring for this object.
+
+      storage (object): A simple object that provides information about file
+        paths for this protocol template
+
+      errors (list): A list containing errors found while loading this
+        protocol template.
+
+      data (dict): The original data for this protocol template, as loaded by
+        our JSON decoder.
+
+    """
+
+    def __init__(self, prefix, data, dataformat_cache=None):
+        super(ProtocolTemplate, self).__init__(prefix, data, dataformat_cache)
+
+    def _load(self, data, dataformat_cache):
+        """Loads the database"""
+
+        self._name = None
+        self.storage = None
+        self.dataformats = {}  # preloaded dataformats
+
+        if isinstance(data, six.string_types):  # user has passed a file pointer
+
+            self._name = data
+            self.storage = Storage(self.prefix, self._name)
+            data = self.storage.json.path
+            if not self.storage.json.exists():
+                self.errors.append(
+                    "Protocol template declaration file not found: %s" % data
+                )
+                return
+
+        # this runs basic validation, including JSON loading if required
+        self.data, self.errors = schema.validate("protocoltemplate", data)
+        if self.errors:
+            return  # don't proceed with the rest of validation
diff --git a/beat/core/schema/protocoltemplate/1.json b/beat/core/schema/protocoltemplate/1.json
new file mode 100644
index 00000000..07af41a7
--- /dev/null
+++ b/beat/core/schema/protocoltemplate/1.json
@@ -0,0 +1,58 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Database Protocol descriptor",
+  "description": "This schema defines the properties of a BEAT database protocol",
+
+  "type": "object",
+
+  "properties": {
+
+    "description": { "$ref": "../common/1.json#/definitions/description" },
+
+    "schema_version": { "const": 1 },
+
+    "sets": {
+      "type": "array",
+      "minItems": 1,
+      "uniqueItems": true,
+      "items": { "$ref": "#/definitions/set" }
+    }
+
+  },
+
+  "required": [
+    "schema_version", "sets"
+  ],
+
+  "additionalProperties": false,
+
+  "definitions": {
+
+    "identifier": {
+      "type": "string",
+      "pattern": "^[a-zA-Z_][a-zA-Z0-9_-]*$"
+    },
+
+    "set": {
+      "type": "object",
+      "properties": {
+        "name": { "$ref": "#/definitions/identifier" },
+        "outputs": {
+          "type": "object",
+          "patternProperties": {
+            "^[a-zA-Z_][a-zA-Z0-9_-]*$": {
+              "$ref": "../common/1.json#/definitions/reference"
+            }
+          },
+          "minProperties": 1,
+          "uniqueItems": true,
+          "additionalProperties": false
+        }
+      },
+      "required": ["name", "outputs"],
+      "additionalProperties": false
+    }
+
+  }
+
+}
diff --git a/beat/core/test/test_protocoltemplate.py b/beat/core/test/test_protocoltemplate.py
new file mode 100644
index 00000000..526a8560
--- /dev/null
+++ b/beat/core/test/test_protocoltemplate.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###################################################################################
+#                                                                                 #
+# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/               #
+# Contact: beat.support@idiap.ch                                                  #
+#                                                                                 #
+# Redistribution and use in source and binary forms, with or without              #
+# modification, are permitted provided that the following conditions are met:     #
+#                                                                                 #
+# 1. Redistributions of source code must retain the above copyright notice, this  #
+# list of conditions and the following disclaimer.                                #
+#                                                                                 #
+# 2. Redistributions in binary form must reproduce the above copyright notice,    #
+# this list of conditions and the following disclaimer in the documentation       #
+# and/or other materials provided with the distribution.                          #
+#                                                                                 #
+# 3. Neither the name of the copyright holder nor the names of its contributors   #
+# may be used to endorse or promote products derived from this software without   #
+# specific prior written permission.                                              #
+#                                                                                 #
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED   #
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE          #
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE    #
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL      #
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR      #
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER      #
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   #
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   #
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.            #
+#                                                                                 #
+###################################################################################
+
+
+import nose.tools
+from ..protocoltemplate import ProtocolTemplate
+
+from . import prefix, tmp_prefix
+from .utils import cleanup
+
+
+def test_export():
+    for protocol_name in [
+        "double",
+        "triple",
+        "two_sets",
+        "labelled",
+        "different_frequencies",
+    ]:
+        yield export, f"{protocol_name}/1"
+
+
+@nose.tools.with_setup(teardown=cleanup)
+def export(protocol_name):
+
+    obj = ProtocolTemplate(prefix, protocol_name)
+    nose.tools.assert_true(obj.valid, "\n  * %s" % "\n  * ".join(obj.errors))
+
+    obj.export(tmp_prefix)
+
+    # load from tmp_prefix and validates
+    exported = ProtocolTemplate(tmp_prefix, protocol_name)
+    nose.tools.assert_true(exported.valid, "\n  * %s" % "\n  * ".join(exported.errors))
-- 
GitLab