diff --git a/beat/core/database.py b/beat/core/database.py index ce60f72eef728cec92e0286a318254655a3d03a8..18f4d5a664cfdd5e68a753295ee9c5d8629d1c1d 100644 --- a/beat/core/database.py +++ b/beat/core/database.py @@ -56,6 +56,22 @@ from . import prototypes from beat.backend.python.database import Storage from beat.backend.python.database import Database as BackendDatabase +from beat.backend.python.protocoltemplate import Storage as PTStorage + + +def get_first_procotol_template(prefix): + pt_root_folder = os.path.join(prefix, PTStorage.asset_folder) + pts_available = os.listdir(pt_root_folder) + + if not pts_available: + raise RuntimeError("Invalid prefix content, no protocol template available") + + procotol_template_folder = pts_available[0] + protocol_template_versions = sorted( + os.listdir(os.path.join(pt_root_folder, procotol_template_folder)) + ) + version = protocol_template_versions[-1].split(".")[0] + return "{}/{}".format(procotol_template_folder, version) class Database(BackendDatabase): @@ -132,8 +148,17 @@ class Database(BackendDatabase): self.errors.append("Database declaration file not found: %s" % data) return - # this runs basic validation, including JSON loading if required - self.data, self.errors = schema.validate("database", data) + # At this point, `data' can be a dictionary or ``None`` + if data is None: # loads the default declaration for a database + self.data, self.errors = prototypes.load("database") + self.data["protocols"][0]["template"] = get_first_procotol_template( + self.prefix + ) + assert not self.errors, "\n * %s" % "\n *".join(self.errors) # nosec + else: + # this runs basic validation, including JSON loading if required + self.data, self.errors = schema.validate("database", data) + if self.errors: return # don't proceed with the rest of validation @@ -148,7 +173,7 @@ class Database(BackendDatabase): # At this point, `code' can be a string (or a binary blob) or ``None`` if code is None: # loads the default code for an algorithm - self.code = prototypes.binary_load("view.py") + self.code = prototypes.binary_load("database.py") else: # just assign it - notice that in this case, no language is set self.code = code diff --git a/beat/core/experiment.py b/beat/core/experiment.py index a901c9271178f53f6117cf8adf32480a3bb2e10f..65533af8f31554ca563186bb260f9412b5cf4be0 100644 --- a/beat/core/experiment.py +++ b/beat/core/experiment.py @@ -52,7 +52,6 @@ from . import algorithm from . import schema from . import database from . import toolchain -from . import prototypes from . import hash @@ -233,12 +232,10 @@ class Experiment(object): self.data = None self.errors = [] - if data is None: # loads prototype and validates it - - experiment_data, self.errors = prototypes.load("experiment") - assert not self.errors, "\n * %s" % "\n *".join(self.errors) # nosec - toolchain_data, self.errors = prototypes.load("toolchain") - assert not self.errors, "\n * %s" % "\n *".join(self.errors) # nosec + if data is None: # Invalid case + # There can't be a prototype for experiments they must be + # filled based on the toolchain and the content of the prefix + raise RuntimeError("Experiments can't have default implementation") elif isinstance(data, (tuple, list)): # the user has passed a tuple diff --git a/beat/core/prototypes/database.json b/beat/core/prototypes/database.json new file mode 100644 index 0000000000000000000000000000000000000000..3f7834c56043a929989ce291b33fb29305c9d419 --- /dev/null +++ b/beat/core/prototypes/database.json @@ -0,0 +1,15 @@ +{ + "root_folder": "/tmp/foo/bar", + "protocols": [ + { + "name": "foo", + "template": "bar/1", + "views": { + "foo": { + "view": "FooView" + } + } + } + ], + "schema_version": 2 +} diff --git a/beat/core/prototypes/database.py b/beat/core/prototypes/database.py new file mode 100644 index 0000000000000000000000000000000000000000..9fa8f6c9cae3faf069913cc036d7475741ad2da8 --- /dev/null +++ b/beat/core/prototypes/database.py @@ -0,0 +1,34 @@ +import numpy + +from collections import namedtuple + +from beat.backend.python.database import View + + +class FooView(View): + def setup( + self, + root_folder, + outputs, + parameters, + force_start_index=None, + force_end_index=None, + ): + """Initializes the database""" + + return True + + def index(self, root_folder, parameters): + """Creates the data for the database indexation""" + + Entry = namedtuple("Entry", ["out"]) + + return [Entry(42)] + + def get(self, output, index): + """Returns the data for the output based on the index content""" + + obj = self.objs[index] + + if output == "out": + return {"value": numpy.int32(obj.out)} diff --git a/beat/core/prototypes/view.py b/beat/core/prototypes/view.py deleted file mode 100644 index e306693b0e8cc617d187cd0143a1c8d6e62559cb..0000000000000000000000000000000000000000 --- a/beat/core/prototypes/view.py +++ /dev/null @@ -1,22 +0,0 @@ -class View: - def setup( - self, - root_folder, - outputs, - parameters, - force_start_index=None, - force_end_index=None, - ): - """Initializes the database""" - - return True - - def done(self): - """Should return ``True``, when data is finished""" - - return True - - def next(self): - """Loads the next data block on ``outputs``""" - - return True diff --git a/beat/core/test/test_experiment_loading.py b/beat/core/test/test_experiment_loading.py index 311ee32ff2af275954cc93c47498afab8ba51b47..e3c6fdd7969676a6b9aae2b36c091c23cb2d9049 100644 --- a/beat/core/test/test_experiment_loading.py +++ b/beat/core/test/test_experiment_loading.py @@ -42,6 +42,17 @@ from . import prefix, tmp_prefix from .utils import cleanup +# ---------------------------------------------------------- + + +@nose.tools.raises(RuntimeError) +def test_load_default_experiment_fails(): + Experiment(prefix, data=None) + + +# ---------------------------------------------------------- + + def test_load_valid_experiment(): experiment = Experiment(prefix, "user/integers_addition/1/integers_addition")