From edb4c46e88ae6d198f24cdb360b9daef5bffa795 Mon Sep 17 00:00:00 2001
From: Cezary Czernecki <ccezary@ethz.ch>
Date: Tue, 18 Sep 2018 09:10:11 +0200
Subject: [PATCH] SSDM-7202 refactor

---
 .../1/as/initialize-master-data.py            |   7 +-
 .../1/as/life-sciences-types/types2.xls       | Bin 45568 -> 0 bytes
 .../creation_parsers.py                       | 199 ++++++++----------
 .../poi_to_definition/definition_parsers.py   |   1 +
 .../1/as/processors/__init__.py               |   1 +
 .../openbis_duplicates_processor.py}          |   3 +-
 .../scripts/dynamic/dynamic.py                |   0
 .../scripts/valid.py                          |   0
 .../types.xls                                 | Bin 47616 -> 47616 bytes
 .../1/as/utils/__init__.py                    |   2 +
 .../1/as/{ => utils}/file_handling.py         |   0
 .../1/as/utils/openbis_utils.py               |   9 +
 12 files changed, 107 insertions(+), 115 deletions(-)
 delete mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/types2.xls
 create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/processors/__init__.py
 rename openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/{openbis_logic.py => processors/openbis_duplicates_processor.py} (97%)
 rename openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/{life-sciences-types => test_files}/scripts/dynamic/dynamic.py (100%)
 rename openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/{life-sciences-types => test_files}/scripts/valid.py (100%)
 rename openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/{life-sciences-types => test_files}/types.xls (95%)
 create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/__init__.py
 rename openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/{ => utils}/file_handling.py (100%)
 create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/openbis_utils.py

diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py
index ead04e46d97..f5e98d86cee 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py
@@ -20,10 +20,10 @@ import sys
 from ch.ethz.sis.openbis.generic.asapi.v3.dto.operation import SynchronousOperationExecutionOptions
 from ch.ethz.sis.openbis.generic.server.asapi.v3 import ApplicationServerApi
 from ch.systemsx.cisd.openbis.generic.server import CommonServiceProvider
-from file_handling import list_xls_files
-from openbis_logic import OpenbisLogicHandler
 from parsers import ExcelToPoiParser, PoiToDefinitionParser, DefinitionToCreationParser, DuplicatesHandler, CreationToOperationParser
+from processors import OpenbisDuplicatesHandler
 from search_engines import SearchEngine
+from utils.file_handling import list_xls_files
 
 api = CommonServiceProvider.getApplicationContext().getBean(ApplicationServerApi.INTERNAL_SERVICE_NAME)
 
@@ -38,11 +38,10 @@ for excel_file_path in list_xls_files():
         else:
             creations[creation_type].extend(partial_creation)
 distinct_creations = DuplicatesHandler.get_distinct_creations(creations)
-
 sessionToken = api.loginAsSystem()
 search_engine = SearchEngine(api, sessionToken)
 existing_elements = search_engine.find_all_existing_elements(distinct_creations)
-server_duplicates_handler = OpenbisLogicHandler(distinct_creations, existing_elements)
+server_duplicates_handler = OpenbisDuplicatesHandler(distinct_creations, existing_elements)
 creations = server_duplicates_handler.remove_existing_elements_from_creations()
 creations = server_duplicates_handler.rewrite_parentchild_creationid_to_permid()
 # creations = server_duplicates_handler.rewrite_vocabulary_labels()
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/types2.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/types2.xls
deleted file mode 100644
index 52bd9cc8dd6c1020b5d9b9624b4a53c092557beb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 45568
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

diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/creation_parsers.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/creation_parsers.py
index 86e26673774..6b98d4de380 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/creation_parsers.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/creation_parsers.py
@@ -14,8 +14,9 @@ from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.create import SpaceCreation
 from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.create import VocabularyCreation
 from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.create import VocabularyTermCreation
 from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.id import VocabularyPermId
-from file_handling import get_script, get_filename_from_path
 from java.lang import UnsupportedOperationException
+from utils.file_handling import get_script
+from utils.openbis_utils import is_internal_namespace, get_script_name_for
 
 
 def get_boolean_from_string(text):
@@ -27,113 +28,109 @@ class DefinitionToCreationParserFactory(object):
     @staticmethod
     def getParsers(definition):
         if definition.type == u'VOCABULARY_TYPE':
-            return [VocabularyDefinitionToCreationParser]
+            return [VocabularyDefinitionToCreationParser()]
         elif definition.type == u'SAMPLE_TYPE':
-            return [SampleTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, ScriptDefinitionToCreationParser]
+            return [SampleTypeDefinitionToCreationParser(), PropertyTypeDefinitionToCreationParser(), ScriptDefinitionToCreationParser()]
         elif definition.type == u'EXPERIMENT_TYPE':
-            return [ExperimentTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, ScriptDefinitionToCreationParser]
+            return [ExperimentTypeDefinitionToCreationParser(), PropertyTypeDefinitionToCreationParser(), ScriptDefinitionToCreationParser()]
         elif definition.type == u'DATASET_TYPE':
-            return [DatasetTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, ScriptDefinitionToCreationParser]
+            return [DatasetTypeDefinitionToCreationParser(), PropertyTypeDefinitionToCreationParser(), ScriptDefinitionToCreationParser()]
         elif definition.type == u'SPACE':
-            return [SpaceDefinitionToCreationParser]
+            return [SpaceDefinitionToCreationParser()]
         elif definition.type == u'PROJECT':
-            return [ProjectDefinitionToCreationParser]
+            return [ProjectDefinitionToCreationParser()]
         elif definition.type == u'EXPERIMENT':
-            return [ExperimentDefinitionToCreationParser]
+            return [ExperimentDefinitionToCreationParser()]
         elif definition.type == u'SAMPLE':
-            return [SampleDefinitionToCreationParser]
+            return [SampleDefinitionToCreationParser()]
         elif definition.type == u'PROPERTY_TYPE':
-            return [PropertyTypeDefinitionToCreationParser]
+            return [PropertyTypeDefinitionToCreationParser()]
         else:
             raise UnsupportedOperationException("Definition of " + str(definition.type) + " is not supported (probably yet).")
 
 
-class PropertyTypeDefinitionToCreationParser(object):
+class DefinitionToCreationParser(object):
+    pass
+
+
+class PropertyTypeDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "PropertyTypeCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         property_creations = []
-        for property in definition.properties:
+
+        for prop in definition.properties:
             property_type_creation = PropertyTypeCreation()
-            property_type_creation.code = property[u'code']
-            if property[u'code'].startswith(u'$'):
-                property_type_creation.internalNameSpace = True
-            property_type_creation.label = property[u'property label']
-            property_type_creation.dataType = DataType.valueOf(property[u'property type'])
-            property_type_creation.vocabularyId = VocabularyPermId(property[u'vocabulary code']) if property[u'vocabulary code'] is not None else None
-            property_type_creation.description = property[u'description']
+            property_type_creation.code = prop[u'code']
+            property_type_creation.label = prop[u'property label']
+            property_type_creation.description = prop[u'description']
+            property_type_creation.dataType = DataType.valueOf(prop[u'property type'])
+            property_type_creation.internalNameSpace = is_internal_namespace(prop[u'code'])
+            property_type_creation.vocabularyId = VocabularyPermId(prop[u'vocabulary code']) if prop[u'vocabulary code'] is not None else None
             property_creations.append(property_type_creation)
 
         return property_creations
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return PropertyTypeDefinitionToCreationParser.type
 
 
-class VocabularyDefinitionToCreationParser(object):
+class VocabularyDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "VocabularyCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         code = definition.attributes[u'code']
         vocabulary_creation = VocabularyCreation()
         vocabulary_creation.code = code
-        if code.startswith(u'$'):
-            vocabulary_creation.internalNameSpace = True
+        vocabulary_creation.internalNameSpace = is_internal_namespace(code)
         vocabulary_creation.description = definition.attributes[u'description']
 
         vocabulary_creations_terms = []
-        for property in definition.properties:
+        for prop in definition.properties:
             vocabulary_creation_term = VocabularyTermCreation()
-            vocabulary_creation_term.code = property[u'code']
-            vocabulary_creation_term.label = property[u'label']
-            vocabulary_creation_term.description = property[u'description']
+            vocabulary_creation_term.code = prop[u'code']
+            vocabulary_creation_term.label = prop[u'label']
+            vocabulary_creation_term.description = prop[u'description']
             vocabulary_creations_terms.append(vocabulary_creation_term)
 
         vocabulary_creation.terms = vocabulary_creations_terms
 
         return vocabulary_creation
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return VocabularyDefinitionToCreationParser.type
 
 
-class PropertyAssignmentDefinitionToCreationParser(object):
+class PropertyAssignmentDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "PropertyAssignmentCreation"
 
-    @staticmethod
-    def parse(property):
-        code = property[u'code']
+    def parse(self, prop):
+        code = prop[u'code']
         property_assingment_creation = PropertyAssignmentCreation()
-        is_mandatory = get_boolean_from_string(property[u'mandatory'])
+        is_mandatory = get_boolean_from_string(prop[u'mandatory'])
         property_assingment_creation.mandatory = is_mandatory
-        should_show_in_edit_view = get_boolean_from_string(property[u'show in edit views'])
+        should_show_in_edit_view = get_boolean_from_string(prop[u'show in edit views'])
         property_assingment_creation.showInEditView = should_show_in_edit_view
-        property_assingment_creation.section = property[u'section']
+        property_assingment_creation.section = prop[u'section']
         property_assingment_creation.propertyTypeId = PropertyTypePermId(code)
-        if u'dynamic script' in property and property[u'dynamic script'] is not None:
-            dynamic_script_path = property[u'dynamic script']
-            property_assingment_creation.pluginId = PluginPermId(ScriptDefinitionToCreationParser.get_name_for(code, dynamic_script_path))
+        if u'dynamic script' in prop and prop[u'dynamic script'] is not None:
+            dynamic_script_path = prop[u'dynamic script']
+            property_assingment_creation.pluginId = PluginPermId(get_script_name_for(code, dynamic_script_path))
 
         return property_assingment_creation
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return PropertyAssignmentDefinitionToCreationParser.type
 
 
-class SampleTypeDefinitionToCreationParser(object):
+class SampleTypeDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "SampleTypeCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         code = definition.attributes[u'code']
         sample_creation = SampleTypeCreation()
         sample_creation.code = code
@@ -141,71 +138,68 @@ class SampleTypeDefinitionToCreationParser(object):
         sample_creation.autoGeneratedCode = should_auto_generate_codes
         if u'validation script' in definition.attributes and definition.attributes[u'validation script'] is not None:
             validation_script_path = definition.attributes[u'validation script']
-            sample_creation.validationPluginId = PluginPermId(ScriptDefinitionToCreationParser.get_name_for(code, validation_script_path))
+            sample_creation.validationPluginId = PluginPermId(get_script_name_for(code, validation_script_path))
 
         property_assingment_creations = []
-        for property in definition.properties:
-            property_assingment_creation = PropertyAssignmentDefinitionToCreationParser.parse(property)
+        property_assignment_parser = PropertyAssignmentDefinitionToCreationParser()
+        for prop in definition.properties:
+            property_assingment_creation = property_assignment_parser.parse(prop)
             property_assingment_creations.append(property_assingment_creation)
 
         sample_creation.propertyAssignments = property_assingment_creations
         return sample_creation
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return SampleTypeDefinitionToCreationParser.type
 
 
-class ExperimentTypeDefinitionToCreationParser(object):
+class ExperimentTypeDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "ExperimentTypeCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         experiment_type_creation = ExperimentTypeCreation()
         experiment_type_creation.code = definition.attributes[u'code']
 
         property_assingment_creations = []
-        for property in definition.properties:
-            property_assingment_creation = PropertyAssignmentDefinitionToCreationParser.parse(property)
+        property_assignment_parser = PropertyAssignmentDefinitionToCreationParser()
+        for prop in definition.properties:
+            property_assingment_creation = property_assignment_parser.parse(prop)
             property_assingment_creations.append(property_assingment_creation)
 
         experiment_type_creation.propertyAssignments = property_assingment_creations
         return experiment_type_creation
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return ExperimentTypeDefinitionToCreationParser.type
 
 
-class DatasetTypeDefinitionToCreationParser(object):
+class DatasetTypeDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "DatasetTypeCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         dataset_type_creation = DataSetTypeCreation()
         dataset_type_creation.code = definition.attributes[u'code']
 
         property_assingment_creations = []
-        for property in definition.properties:
-            property_assingment_creation = PropertyAssignmentDefinitionToCreationParser.parse(property)
+        property_assignment_parser = PropertyAssignmentDefinitionToCreationParser()
+        for prop in definition.properties:
+            property_assingment_creation = property_assignment_parser.parse(prop)
             property_assingment_creations.append(property_assingment_creation)
 
         dataset_type_creation.propertyAssignments = property_assingment_creations
         return dataset_type_creation
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return DatasetTypeDefinitionToCreationParser.type
 
 
-class SpaceDefinitionToCreationParser(object):
+class SpaceDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "SpaceCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         space_creation = SpaceCreation()
         space_creation.code = definition.attributes[u'code']
         space_creation.description = definition.attributes[u'description']
@@ -213,17 +207,15 @@ class SpaceDefinitionToCreationParser(object):
         space_creation.creationId = CreationId(creation_id)
         return space_creation
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return SpaceDefinitionToCreationParser.type
 
 
-class ProjectDefinitionToCreationParser(object):
+class ProjectDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "ProjectCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         project_creation = ProjectCreation()
         project_creation.code = definition.attributes[u'code']
         project_creation.description = definition.attributes[u'description']
@@ -232,17 +224,15 @@ class ProjectDefinitionToCreationParser(object):
         project_creation.creationId = CreationId(creation_id)
         return project_creation
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return ProjectDefinitionToCreationParser.type
 
 
-class ExperimentDefinitionToCreationParser(object):
+class ExperimentDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "ExperimentCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         experiments = []
         mandatory_attributes = [u'code', u'project']
         for experiment_properties in definition.properties:
@@ -252,23 +242,21 @@ class ExperimentDefinitionToCreationParser(object):
             experiment_creation.projectId = CreationId(experiment_properties[u'project'])
             creation_id = experiment_properties[u'code']
             experiment_creation.creationId = CreationId(creation_id)
-            for property, value in experiment_properties.items():
-                if property not in mandatory_attributes:
-                    experiment_creation.setProperty(property, value)
+            for prop, value in experiment_properties.items():
+                if prop not in mandatory_attributes:
+                    experiment_creation.setProperty(prop, value)
             experiments.append(experiment_creation)
         return experiments
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return ExperimentDefinitionToCreationParser.type
 
 
-class SampleDefinitionToCreationParser(object):
+class SampleDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "SampleCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         samples = []
         mandatory_attributes = [u'$', u'code', u'space', u'project', u'experiment', u'auto generate code', u'parents', u'children']
         for sample_properties in definition.properties:
@@ -302,23 +290,21 @@ class SampleDefinitionToCreationParser(object):
                     child_creationids.append(CreationId(child))
                 sample_creation.childIds = child_creationids
 
-            for property, value in sample_properties.items():
-                if property not in mandatory_attributes:
-                    sample_creation.setProperty(property, value)
+            for prop, value in sample_properties.items():
+                if prop not in mandatory_attributes:
+                    sample_creation.setProperty(prop, value)
             samples.append(sample_creation)
         return samples
 
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return SampleDefinitionToCreationParser.type
 
 
-class ScriptDefinitionToCreationParser(object):
+class ScriptDefinitionToCreationParser(DefinitionToCreationParser):
 
     type = "ScriptCreation"
 
-    @staticmethod
-    def parse(definition):
+    def parse(self, definition):
         scripts = []
         if u'validation script' in definition.attributes:
             validation_script_path = definition.attributes[u'validation script']
@@ -328,31 +314,26 @@ class ScriptDefinitionToCreationParser(object):
                 script_file = open(get_script(validation_script_path))
                 script = script_file.read()
                 script_file.close()
-                validation_script_creation.name = ScriptDefinitionToCreationParser.get_name_for(code, validation_script_path)
+                validation_script_creation.name = get_script_name_for(code, validation_script_path)
                 validation_script_creation.script = script
                 validation_script_creation.pluginType = PluginType.ENTITY_VALIDATION
                 scripts.append(validation_script_creation)
 
-        for property in definition.properties:
-            if u'dynamic script' in property:
-                dynamic_prop_script_path = property[u'dynamic script']
-                code = property[u'code']
+        for prop in definition.properties:
+            if u'dynamic script' in prop:
+                dynamic_prop_script_path = prop[u'dynamic script']
+                code = prop[u'code']
                 if dynamic_prop_script_path is not None:
                     validation_script_creation = PluginCreation()
                     script_file = open(get_script(dynamic_prop_script_path))
                     script = script_file.read()
                     script_file.close()
-                    validation_script_creation.name = ScriptDefinitionToCreationParser.get_name_for(code, dynamic_prop_script_path)
+                    validation_script_creation.name = get_script_name_for(code, dynamic_prop_script_path)
                     validation_script_creation.script = script
                     validation_script_creation.pluginType = PluginType.DYNAMIC_PROPERTY
                     scripts.append(validation_script_creation)
 
         return scripts
 
-    @staticmethod
-    def get_name_for(owner_code, script_path):
-        return owner_code + '.' + get_filename_from_path(script_path)
-
-    @staticmethod
-    def get_type():
+    def get_type(self):
         return ScriptDefinitionToCreationParser.type
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/definition_parsers.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/definition_parsers.py
index 2990cc1345d..3162d6fa808 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/definition_parsers.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/definition_parsers.py
@@ -1,3 +1,4 @@
+from java.lang import UnsupportedOperationException
 from .definition import Definition
 from .poi_cleaner import PoiCleaner
 
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/processors/__init__.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/processors/__init__.py
new file mode 100644
index 00000000000..870975eb874
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/processors/__init__.py
@@ -0,0 +1 @@
+from .openbis_duplicates_processor import OpenbisDuplicatesHandler
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/openbis_logic.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/processors/openbis_duplicates_processor.py
similarity index 97%
rename from openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/openbis_logic.py
rename to openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/processors/openbis_duplicates_processor.py
index 2e6c63b0bdd..8b0c3077a0c 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/openbis_logic.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/processors/openbis_duplicates_processor.py
@@ -7,7 +7,7 @@ from parsers import VocabularyDefinitionToCreationParser, PropertyTypeDefinition
                     ProjectDefinitionToCreationParser, ExperimentDefinitionToCreationParser, ScriptDefinitionToCreationParser, SampleDefinitionToCreationParser
 
 
-class OpenbisLogicHandler(object):
+class OpenbisDuplicatesHandler(object):
 
     def __init__(self, creations, existing_elements):
         self.creations = creations
@@ -101,7 +101,6 @@ class OpenbisLogicHandler(object):
     def _create_sample_identifier_string(self, creation):
         spaceId = creation.spaceId.creationId if creation.spaceId is not None else None
         projectId = creation.projectId.creationId if creation.projectId is not None else None
-#         experimentId = creation.experimentId.creationId if creation.experimentId is not None else None
         code = creation.code
         sample_identifier = SampleIdentifier(spaceId, projectId, None, code)
         return sample_identifier.identifier
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/scripts/dynamic/dynamic.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/test_files/scripts/dynamic/dynamic.py
similarity index 100%
rename from openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/scripts/dynamic/dynamic.py
rename to openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/test_files/scripts/dynamic/dynamic.py
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/scripts/valid.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/test_files/scripts/valid.py
similarity index 100%
rename from openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/scripts/valid.py
rename to openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/test_files/scripts/valid.py
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/types.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/test_files/types.xls
similarity index 95%
rename from openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/types.xls
rename to openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/test_files/types.xls
index 473cc33398b77fb5980ba414df0437cd2cc91825..c2d224eb2ae234f8e00d673d04f887f8cc7922ea 100644
GIT binary patch
delta 386
zcmZqp!qo7EX+sVRn}XdRQF;H#MJ#QM7dQW8S<k}8P;C5I&VTbdPBTWn3lF=1>?@1{
zDzkv}<WF2ha3O}jGeOFVxQm$4#Xj*Bp=;e-B=FEo=)DI65U?_YL<YD9w=(cCe4d=@
z?$6K1zyuNp14ahM&3oO`*&^?lGBEG}g`&+E7`lP9r#S;dB_jjFY9M_ZNQ+u9Ft`Hs
z$y+io_%kywECte6fwZI*P%R4s!!{uO3`lcXGcZ`OGBDHu>2*N*#^zeT2|Q}<UJMKq
z!x$K3V;C6H;~5xMCNnVHO=n=x%wb?STEM_yUdq6bT|Qa7YtH8L(T8*y875Ea)ZkWU
z0NM=1>I@o_k9PX7a#k`hm~Iy9ie=keJK+E$qsU~tN!pC_CTC5u;}ZsY6r@%d2w7Pt
TpX-$0d=)4;*?@&*(G)cRiF<2y

delta 382
zcmZ9`KS;ws6bA6`(j*vLqHPBe)I}(Q2s#*T1wl%w7PW$Zps0vQ?NUJkV!H)Ip>-)e
z2N6M01VO05+ZA<mDPmM`auFO{N*6a@9dkGQ?!J5PcvfAq>XMz6)&A(abv4CJd5<~b
zSH6~2>u7@#QPB*h+o!j5eqmLm+Ni2Eo32QhUen{wYP#ue6=v(F5%?YlEd_I_nb_<u
zbo}siJgGH-HF7{a#xrW_brA5;ZQcO($><P}r-}>mgKQZFViM3E29h4&gmlPY1elNk
zoBSf>5g?)fIkG}Ni=F9`FEo~*Zx?{JC17<0I9~(aGC+40xZ4B<wt)0D?<^h*XYrwr
zN&K+T<Msc~$l!;cKNThwPaX)1j^eVaF-b*`=1huQ>7KDJ9x3;-OTJzn)dSGzfgwO!
M)5J|V!Ojjsf4i7$wg3PC

diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/__init__.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/__init__.py
new file mode 100644
index 00000000000..2ca4ab4e79e
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/__init__.py
@@ -0,0 +1,2 @@
+import file_handling
+import openbis_utils
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/file_handling.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/file_handling.py
similarity index 100%
rename from openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/file_handling.py
rename to openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/file_handling.py
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/openbis_utils.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/openbis_utils.py
new file mode 100644
index 00000000000..994918600bd
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/utils/openbis_utils.py
@@ -0,0 +1,9 @@
+from utils.file_handling import get_filename_from_path
+
+
+def is_internal_namespace(property_value):
+    return property_value.startswith(u'$')
+
+
+def get_script_name_for(owner_code, script_path):
+        return owner_code + '.' + get_filename_from_path(script_path)
-- 
GitLab