diff --git a/datastore_server/build.gradle b/datastore_server/build.gradle
index e217f508800e5c51b2afbd9ccee16a2048234788..99154a6a60835857df79055a69bb6ce703e10004 100644
--- a/datastore_server/build.gradle
+++ b/datastore_server/build.gradle
@@ -33,7 +33,7 @@ dependencies {
 			'apache:commons-fileupload:+',
 			'lyncode:xoai-data-provider:+'
 
-  runtime 'bioformats:bioformats:+'
+  runtime 'bioformats:bioformats:5.9.2'
 
 	testCompile (project(path: ':openbis', configuration: 'tests')) {
 		exclude group: 'google', module: 'gwt-user'
diff --git a/image_readers/build.gradle b/image_readers/build.gradle
index cbe3e1354466a3ef8c02d3e63538abcd29eb0f6e..50400c005e252dda519e812ac9a1cb2a08ff4258 100644
--- a/image_readers/build.gradle
+++ b/image_readers/build.gradle
@@ -7,7 +7,7 @@ dependencies {
     compile project(':common'),
         'imagej:ij:+',
         'sun:jai:+',
-        'bioformats:bioformats:+',
+        'bioformats:bioformats:5.9.2',
         'slf4j:slf4j:+',
         'slf4j:slf4j-log4j12:+'
         
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
index 5819a298be25716525e5f83b98f0b06c8d86b5af..56bf7882d6194095a63ea6f39ba9a5a7d22bcdbd 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
@@ -1111,6 +1111,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			var fo = new dtos.ExperimentTypeFetchOptions();
 			fo.withPropertyAssignments().withPropertyType();
 			fo.withPropertyAssignments().withRegistrator();
+			fo.withPropertyAssignments().withPlugin();
 			return fo;
 		};
 
@@ -1139,6 +1140,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			var fo = new dtos.SampleTypeFetchOptions();
 			fo.withPropertyAssignments().withPropertyType();
 			fo.withPropertyAssignments().withRegistrator();
+			fo.withPropertyAssignments().withPlugin();
 			return fo;
 		};
 
@@ -1168,6 +1170,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			var fo = new dtos.DataSetTypeFetchOptions();
 			fo.withPropertyAssignments().withPropertyType();
 			fo.withPropertyAssignments().withRegistrator();
+			fo.withPropertyAssignments().withPlugin();
 			return fo;
 		};
 
@@ -1186,6 +1189,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			var fo = new dtos.MaterialTypeFetchOptions();
 			fo.withPropertyAssignments().withPropertyType();
 			fo.withPropertyAssignments().withRegistrator();
+			fo.withPropertyAssignments().withPlugin();
 			return fo;
 		};
 
@@ -1293,6 +1297,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			fo.withPropertyType();
 			fo.withPropertyAssignment().withEntityType();
 			fo.withPropertyAssignment().withPropertyType();
+			fo.withPropertyAssignment().withPlugin();
 			return fo;
 		};
 
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
index 20b7b5c2e4c81bf09f295394a4f931952f44a930..9b5fcef22d6d4fd6b0ab7b002fce030fa71c5907 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
@@ -191,6 +191,7 @@ define(
 						c.assertEqual(assignment.isMandatory(), true, "Assignment mandatory");
 						c.assertEqual(assignment.isShowInEditView(), true, "Assignment ShowInEditView");
 						c.assertEqual(assignment.isShowRawValueInForms(), true, "Assignment ShowRawValueInForms");
+						c.assertEqual(assignment.getPlugin().getName(), "Diff_time", "Assignment Plugin");
 					}
 
 					testCreate(c, fCreate, c.findExperimentType, fCheck);
@@ -272,6 +273,7 @@ define(
 						c.assertEqual(assignment.isMandatory(), true, "Assignment mandatory");
 						c.assertEqual(assignment.isShowInEditView(), true, "Assignment ShowInEditView");
 						c.assertEqual(assignment.isShowRawValueInForms(), true, "Assignment ShowRawValueInForms");
+						c.assertEqual(assignment.getPlugin().getName(), "Diff_time", "Assignment Plugin");
 					}
 
 					testCreate(c, fCreate, c.findSampleType, fCheck);
@@ -397,6 +399,7 @@ define(
 						c.assertEqual(assignment.isMandatory(), true, "Assignment mandatory");
 						c.assertEqual(assignment.isShowInEditView(), true, "Assignment ShowInEditView");
 						c.assertEqual(assignment.isShowRawValueInForms(), true, "Assignment ShowRawValueInForms");
+						c.assertEqual(assignment.getPlugin().getName(), "Diff_time", "Assignment Plugin");
 					}
 
 					testCreate(c, fCreate, c.findDataSetType, fCheck);
@@ -463,6 +466,7 @@ define(
 						c.assertEqual(assignment.isMandatory(), true, "Assignment mandatory");
 						c.assertEqual(assignment.isShowInEditView(), true, "Assignment ShowInEditView");
 						c.assertEqual(assignment.isShowRawValueInForms(), true, "Assignment ShowRawValueInForms");
+						c.assertEqual(assignment.getPlugin().getName(), "Diff_time", "Assignment Plugin");
 					}
 
 					testCreate(c, fCreate, c.findMaterialType, fCheck);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/SearchPropertyAssignmentsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/SearchPropertyAssignmentsOperationExecutor.java
index d9eb6f2390e812a1899a68b1f48f5577988f02f0..4bb9c297dca4a67f039a0abadce7f20545279c16 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/SearchPropertyAssignmentsOperationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/SearchPropertyAssignmentsOperationExecutor.java
@@ -40,6 +40,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.property.IProperty
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.property.PropertyAssignmentKey;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.property.PropertyAssignmentRecord;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePropertyTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.ScriptPE;
 
 /**
  * @author pkupczyk
@@ -97,6 +98,11 @@ public class SearchPropertyAssignmentsOperationExecutor extends
             assignmentRecord.show_raw_value = assignment.getShowRawValue();
             assignmentRecord.type_code = assignment.getEntityType().getCode();
             assignmentRecord.type_id = assignment.getEntityType().getId();
+            ScriptPE script = assignment.getScript();
+            if (script != null)
+            {
+                assignmentRecord.script_id = script.getId();
+            }
             assignmentRecords.add(assignmentRecord);
         }
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/property/PropertyAssignmentRecord.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/property/PropertyAssignmentRecord.java
index b2e972d356dd65ad39cd4a6e4b305b0d03740568..7fe9fac082a1cf622d0ac389c0d147e333a6e927 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/property/PropertyAssignmentRecord.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/property/PropertyAssignmentRecord.java
@@ -52,4 +52,5 @@ public class PropertyAssignmentRecord extends ObjectBaseRecord
 
     public Date registration_timestamp;
 
+    public Long script_id;
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/property/PropertyAssignmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/property/PropertyAssignmentTranslator.java
index 1fa9a3b5a9ed08f9c9360f655e678426dc0c6859..031ad2da473ba7b4a8a25e188bb14508e6e12a1b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/property/PropertyAssignmentTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/property/PropertyAssignmentTranslator.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -34,6 +35,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.IEntityTypeId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions.ExperimentTypeFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.fetchoptions.MaterialTypeFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyAssignmentFetchOptions;
@@ -49,11 +51,11 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.dataset.IDataSetTy
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.experiment.IExperimentTypeTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.material.IMaterialTypeTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.person.IPersonTranslator;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.plugin.IPluginTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.sample.ISampleTypeTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.sample.SampleQuery;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.semanticannotation.ISemanticAnnotationTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.basic.CodeConverter;
-
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 import net.lemnik.eodsql.QueryTool;
 
@@ -85,6 +87,9 @@ public class PropertyAssignmentTranslator implements IPropertyAssignmentTranslat
     @Autowired
     private IPersonTranslator personTranslator;
 
+    @Autowired
+    private IPluginTranslator pluginTranslator;
+    
     @Override
     public Map<Long, PropertyAssignment> getIdToAssignmentMap(TranslationContext context, Collection<PropertyAssignmentRecord> assignmentRecords,
             PropertyAssignmentFetchOptions assignmentFetchOptions)
@@ -145,7 +150,7 @@ public class PropertyAssignmentTranslator implements IPropertyAssignmentTranslat
 
         if (assignmentFetchOptions.hasPropertyType())
         {
-            Map<Long, List<PropertyAssignment>> assignmentsByPropertyTypeId = getAssignmentsByPropertyTypeId(assignments, assignmentRecords);
+            Map<Long, List<PropertyAssignment>> assignmentsByPropertyTypeId = getAssignments(assignments, assignmentRecords, r -> r.prty_id);
             Map<Long, PropertyType> propertyTypeMap =
                     propertyTypeTranslator.translate(context, assignmentsByPropertyTypeId.keySet(), assignmentFetchOptions.withPropertyType());
 
@@ -167,7 +172,7 @@ public class PropertyAssignmentTranslator implements IPropertyAssignmentTranslat
 
         if (assignmentFetchOptions.hasRegistrator())
         {
-            Map<Long, List<PropertyAssignment>> assignmentsByRegistatorId = getAssignmentsByRegistratorId(assignments, assignmentRecords);
+            Map<Long, List<PropertyAssignment>> assignmentsByRegistatorId = getAssignments(assignments, assignmentRecords, r -> r.pers_id_registerer);
             Map<Long, Person> registratorMap =
                     personTranslator.translate(context, assignmentsByRegistatorId.keySet(), assignmentFetchOptions.withRegistrator());
 
@@ -180,36 +185,28 @@ public class PropertyAssignmentTranslator implements IPropertyAssignmentTranslat
                 }
             }
         }
-
-        return assignments;
-    }
-
-    private Map<Long, List<PropertyAssignment>> getAssignmentsByPropertyTypeId(Map<PropertyAssignmentKey, PropertyAssignment> assignments,
-            Collection<PropertyAssignmentRecord> assignmentRecords)
-    {
-        Map<Long, List<PropertyAssignment>> map = new HashMap<Long, List<PropertyAssignment>>();
-
-        for (PropertyAssignmentRecord assignmentRecord : assignmentRecords)
+        
+        if (assignmentFetchOptions.hasPlugin())
         {
-            EntityKind entityKind = EntityKind.valueOf(assignmentRecord.kind_code);
-            PropertyAssignmentKey key = new PropertyAssignmentKey(assignmentRecord.id, entityKind);
-            PropertyAssignment assignment = assignments.get(key);
-            List<PropertyAssignment> list = map.get(assignmentRecord.prty_id);
+            Map<Long, List<PropertyAssignment>> assignmentsByPluginId = getAssignments(assignments, assignmentRecords, r -> r.script_id);
+            Map<Long, Plugin> registratorMap =
+                    pluginTranslator.translate(context, assignmentsByPluginId.keySet(), assignmentFetchOptions.withPlugin());
 
-            if (list == null)
+            for (Map.Entry<Long, List<PropertyAssignment>> entry : assignmentsByPluginId.entrySet())
             {
-                list = new ArrayList<PropertyAssignment>();
-                map.put(assignmentRecord.prty_id, list);
+                Plugin plugin = registratorMap.get(entry.getKey());
+                for (PropertyAssignment assignment : entry.getValue())
+                {
+                    assignment.setPlugin(plugin);
+                }
             }
-
-            list.add(assignment);
         }
 
-        return map;
+        return assignments;
     }
 
-    private Map<Long, List<PropertyAssignment>> getAssignmentsByRegistratorId(Map<PropertyAssignmentKey, PropertyAssignment> assignments,
-            Collection<PropertyAssignmentRecord> assignmentRecords)
+    private Map<Long, List<PropertyAssignment>> getAssignments(Map<PropertyAssignmentKey, PropertyAssignment> assignments,
+            Collection<PropertyAssignmentRecord> assignmentRecords, Function<PropertyAssignmentRecord, Long> extractor)
     {
         Map<Long, List<PropertyAssignment>> map = new HashMap<Long, List<PropertyAssignment>>();
 
@@ -218,15 +215,19 @@ public class PropertyAssignmentTranslator implements IPropertyAssignmentTranslat
             EntityKind entityKind = EntityKind.valueOf(assignmentRecord.kind_code);
             PropertyAssignmentKey key = new PropertyAssignmentKey(assignmentRecord.id, entityKind);
             PropertyAssignment assignment = assignments.get(key);
-            List<PropertyAssignment> list = map.get(assignmentRecord.pers_id_registerer);
-
-            if (list == null)
+            Long id = extractor.apply(assignmentRecord);
+            if (id != null)
             {
-                list = new ArrayList<PropertyAssignment>();
-                map.put(assignmentRecord.pers_id_registerer, list);
+                List<PropertyAssignment> list = map.get(id);
+                
+                if (list == null)
+                {
+                    list = new ArrayList<PropertyAssignment>();
+                    map.put(id, list);
+                }
+                
+                list.add(assignment);
             }
-
-            list.add(assignment);
         }
 
         return map;
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/PropertyAssignment.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/PropertyAssignment.js
index d08e89ec4ff01af84b84fff235f4c23d53dbf2dc..423964ac9cca3e91390981989765b0085f7701f8 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/PropertyAssignment.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/PropertyAssignment.js
@@ -17,6 +17,7 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.semanticAnnotationsInherited = null;
 		prototype.registrator = null;
 		prototype.registrationDate = null;
+		prototype.plugin = null;
 
 		prototype.getFetchOptions = function() {
 			return this.fetchOptions;
@@ -116,6 +117,16 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.setRegistrationDate = function(registrationDate) {
 			this.registrationDate = registrationDate;
 		};
+		prototype.getPlugin = function() {
+			if (this.getFetchOptions() && this.getFetchOptions().hasPlugin()) {
+				return this.plugin;
+			} else {
+				throw new exceptions.NotFetchedException("Plugin has not been fetched.");
+			}
+		};
+		prototype.setPlugin = function(plugin) {
+			this.plugin = plugin;
+		};
 	}, {
 		fetchOptions : "PropertyAssignmentFetchOptions",
 		entityType : "IEntityType",
@@ -126,7 +137,8 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		},
 		semanticAnnotationsInherited : "Boolean",
 		registrator : "Person",
-		registrationDate : "Date"
+		registrationDate : "Date",
+		plugin : "Plugin"
 	});
 	return PropertyAssignment;
 })
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/fetchoptions/PropertyAssignmentFetchOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/fetchoptions/PropertyAssignmentFetchOptions.js
index 169205d9021ec08a549e7cc114026e3976987343..1d47470c070bda54dcf7d44dec4349754d269f19 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/fetchoptions/PropertyAssignmentFetchOptions.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/fetchoptions/PropertyAssignmentFetchOptions.js
@@ -1,5 +1,5 @@
 define([ "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/property/fetchoptions/PropertyTypeFetchOptions", "as/dto/person/fetchoptions/PersonFetchOptions",
-		"as/dto/property/fetchoptions/PropertyAssignmentSortOptions", "as/dto/semanticannotation/fetchoptions/SemanticAnnotationFetchOptions" ], function(stjs, FetchOptions) {
+		"as/dto/plugin/fetchoptions/PluginFetchOptions", "as/dto/property/fetchoptions/PropertyAssignmentSortOptions", "as/dto/semanticannotation/fetchoptions/SemanticAnnotationFetchOptions" ], function(stjs, FetchOptions) {
 	var PropertyAssignmentFetchOptions = function() {
 	};
 	stjs.extend(PropertyAssignmentFetchOptions, FetchOptions, [ FetchOptions ], function(constructor, prototype) {
@@ -9,6 +9,7 @@ define([ "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/property/fet
 		prototype.propertyType = null;
 		prototype.semanticAnnotations = null;
 		prototype.registrator = null;
+		prototype.plugin = null;
 		prototype.sort = null;
 
 		prototype.withEntityType = function() {
@@ -63,6 +64,19 @@ define([ "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/property/fet
 		prototype.hasRegistrator = function() {
 			return this.registrator != null;
 		};
+		prototype.withPlugin = function() {
+			if (this.plugin == null) {
+				var PluginFetchOptions = require("as/dto/plugin/fetchoptions/PluginFetchOptions");
+				this.plugin = new PluginFetchOptions();
+			}
+			return this.plugin;
+		};
+		prototype.withPluginUsing = function(fetchOptions) {
+			return this.plugin = fetchOptions;
+		};
+		prototype.hasPlugin = function() {
+			return this.plugin != null;
+		};
 		prototype.sortBy = function() {
 			if (this.sort == null) {
 				var PropertyAssignmentSortOptions = require("as/dto/property/fetchoptions/PropertyAssignmentSortOptions");
@@ -78,6 +92,7 @@ define([ "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/property/fet
 		propertyType : "PropertyTypeFetchOptions",
 		semanticAnnotations : "SemanticAnnotationFetchOptions",
 		registrator : "PersonFetchOptions",
+		plugin : "PluginFetchOptions",
 		sort : "PropertyAssignmentSortOptions"
 	});
 	return PropertyAssignmentFetchOptions;
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractGetEntityTypeTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractGetEntityTypeTest.java
index 0a49f1e18e04f76b450fa3688e0f0dd527436f86..e831dbd430f097ba7e00b268c12e6b2695da09fa 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractGetEntityTypeTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractGetEntityTypeTest.java
@@ -239,6 +239,7 @@ public abstract class AbstractGetEntityTypeTest extends AbstractTest
 
         PropertyAssignmentCreation assignmentCreation = new PropertyAssignmentCreation();
         assignmentCreation.setPropertyTypeId(new PropertyTypePermId("DESCRIPTION"));
+        assignmentCreation.setPluginId(new PluginPermId("properties"));
 
         EntityTypePermId permId = createEntityType(sessionToken, "ENTITY_TEST_TYPE", Arrays.asList(assignmentCreation), null);
 
@@ -248,7 +249,7 @@ public abstract class AbstractGetEntityTypeTest extends AbstractTest
 
         assertEquals(((EntityTypePermId) type.getPermId()).getPermId(), permId.getPermId());
         assertEquals(type.getCode(), permId.getPermId());
-        assertPropertyAssignments(type.getPropertyAssignments(), "ENTITY_TEST_TYPE.DESCRIPTION");
+        assertPropertyAssignments(type.getPropertyAssignments(), "properties", "ENTITY_TEST_TYPE.DESCRIPTION");
 
         assertValidationPluginNotFetched(type);
     }
@@ -263,6 +264,7 @@ public abstract class AbstractGetEntityTypeTest extends AbstractTest
 
         PropertyAssignmentCreation assignmentCreation = new PropertyAssignmentCreation();
         assignmentCreation.setPropertyTypeId(new PropertyTypePermId(propertyTypeCodeWithDolar));
+        assignmentCreation.setPluginId(new PluginPermId("properties"));
 
         EntityTypePermId permId = createEntityType(sessionToken, entityTypeCode, Arrays.asList(assignmentCreation), null);
 
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java
index e2cb01c07a992b5fd7e4ede2379c7ec983e4fc9e..7ce17ff5f91d54b0fb0375be807d2f3b07d543ec 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java
@@ -1120,13 +1120,17 @@ public class AbstractTest extends SystemTestCase
         assertCollectionContainsOnly(actualSet, expectedCodes);
     }
 
-    protected static void assertPropertyAssignments(Collection<PropertyAssignment> propertyAssignments,
+    protected static void assertPropertyAssignments(Collection<PropertyAssignment> propertyAssignments, String pluginNameOrNull,
             String... expectedEntityTypeAndPropertyTypeCodes)
     {
         Set<String> actualSet = new HashSet<String>();
         for (PropertyAssignment propertyAssignment : propertyAssignments)
         {
             actualSet.add(propertyAssignment.getEntityType().getCode() + "." + propertyAssignment.getPropertyType().getCode());
+            if (pluginNameOrNull != null)
+            {
+                assertEquals(propertyAssignment.getPlugin().getName(), pluginNameOrNull);
+            }
         }
 
         assertCollectionContainsOnly(actualSet, expectedEntityTypeAndPropertyTypeCodes);
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/ConfirmDeletionTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/ConfirmDeletionTest.java
index f1335efd86aaa7ed371d84914db6da8687ab0e07..0f59020ff3fb1bd64c9bfa3be7e19224fe029fbe 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/ConfirmDeletionTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/ConfirmDeletionTest.java
@@ -331,13 +331,21 @@ public class ConfirmDeletionTest extends AbstractDeletionTest
     @Test
     public void testLogging()
     {
+        // given
         String sessionToken = v3api.login(TEST_USER, PASSWORD);
-
-        v3api.confirmDeletions(sessionToken, Arrays.asList(new DeletionTechId(1L), new DeletionTechId(2L)));
-
-        assertAccessLog("confirm-deletions  DELETION_IDS('[1, 2]')");
+        ExperimentPermId experimentId = createCisdExperiment();
+        ExperimentDeletionOptions deletionOptions = new ExperimentDeletionOptions();
+        deletionOptions.setReason("It is just a test");
+        assertExperimentExists(experimentId);
+        IDeletionId deletionId = v3api.deleteExperiments(sessionToken, Collections.singletonList(experimentId), deletionOptions);
+        assertDeletionExists(deletionId);
+        // when
+        v3api.confirmDeletions(sessionToken, Collections.singletonList(deletionId));
+        // then
+        assertAccessLog("confirm-deletions  DELETION_IDS('[" + deletionId + "]')");
     }
 
+
     private DataSetCreation dataSetCreation(String typeCode, String dataSetCode)
     {
         PhysicalDataCreation physicalCreation = new PhysicalDataCreation();
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetDataSetTypeTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetDataSetTypeTest.java
index d1a92118fbce38cc8a6a7afbd20974361b220654..98d873b208c0b71991e390564eefde4f21b79db1 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetDataSetTypeTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetDataSetTypeTest.java
@@ -68,6 +68,7 @@ public class GetDataSetTypeTest extends AbstractGetEntityTypeTest
         {
             fo.withPropertyAssignments().withEntityType();
             fo.withPropertyAssignments().withPropertyType();
+            fo.withPropertyAssignments().withPlugin();
         }
         if (withValidationPlugin)
         {
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetExperimentTypeTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetExperimentTypeTest.java
index 4f41ecd9faa41a7970524a3a4bb0003e265cfc1f..59cf285f23daf87966dc603748c37dc593c37b07 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetExperimentTypeTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetExperimentTypeTest.java
@@ -68,6 +68,7 @@ public class GetExperimentTypeTest extends AbstractGetEntityTypeTest
         {
             fo.withPropertyAssignments().withEntityType();
             fo.withPropertyAssignments().withPropertyType();
+            fo.withPropertyAssignments().withPlugin();
         }
         if (withValidationPlugin)
         {
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetMaterialTypeTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetMaterialTypeTest.java
index 2833e8078b827f4f3aa92c74d2fbb312d748751f..aa84b1ff7d136025fca5b442bb75158fa75c8197 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetMaterialTypeTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetMaterialTypeTest.java
@@ -68,6 +68,7 @@ public class GetMaterialTypeTest extends AbstractGetEntityTypeTest
         {
             fo.withPropertyAssignments().withEntityType();
             fo.withPropertyAssignments().withPropertyType();
+            fo.withPropertyAssignments().withPlugin();
         }
         if (withValidationPlugin)
         {
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetSampleTypeTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetSampleTypeTest.java
index 1d904181fc24d81cea207d8497cefd131c1d377f..354dfce5455fc42822774363eb9a0888bcc4e16b 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetSampleTypeTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/GetSampleTypeTest.java
@@ -69,6 +69,7 @@ public class GetSampleTypeTest extends AbstractGetEntityTypeTest
         {
             fo.withPropertyAssignments().withEntityType();
             fo.withPropertyAssignments().withPropertyType();
+            fo.withPropertyAssignments().withPlugin();
         }
         if (withValidationPlugin)
         {
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchPropertyAssignmentTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchPropertyAssignmentTest.java
index bc3be6513eeb28ccfdeb3a0ba03d375f3a0e040a..286e6010596d96d5286c1454cdf58da0e415825f 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchPropertyAssignmentTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchPropertyAssignmentTest.java
@@ -149,9 +149,9 @@ public class SearchPropertyAssignmentTest extends AbstractTest
             }
         }
 
-        assertPropertyAssignments(withOwnSemanticAnnotations, "MASTER_PLATE.$PLATE_GEOMETRY", "CONTROL_LAYOUT.$PLATE_GEOMETRY",
+        assertPropertyAssignments(withOwnSemanticAnnotations, null, "MASTER_PLATE.$PLATE_GEOMETRY", "CONTROL_LAYOUT.$PLATE_GEOMETRY",
                 "CELL_PLATE.ORGANISM");
-        assertPropertyAssignments(withInheritedSemanticAnnotations, "MASTER_PLATE.DESCRIPTION", "CONTROL_LAYOUT.DESCRIPTION", "NORMAL.ORGANISM",
+        assertPropertyAssignments(withInheritedSemanticAnnotations, null, "MASTER_PLATE.DESCRIPTION", "CONTROL_LAYOUT.DESCRIPTION", "NORMAL.ORGANISM",
                 "DELETION_TEST.ORGANISM", "DELETION_TEST.DESCRIPTION");
 
         v3api.logout(sessionToken);
@@ -234,12 +234,13 @@ public class SearchPropertyAssignmentTest extends AbstractTest
         PropertyAssignmentFetchOptions fo = new PropertyAssignmentFetchOptions();
         fo.withEntityType();
         fo.withPropertyType();
+        fo.withPlugin();
 
         SearchResult<PropertyAssignment> searchResult =
                 v3api.searchPropertyAssignments(sessionToken, criteria, fo);
         List<PropertyAssignment> propertyAssignments = searchResult.getObjects();
 
-        assertPropertyAssignments(propertyAssignments, expectedEntityTypeAndPropertyTypeCodes);
+        assertPropertyAssignments(propertyAssignments, null, expectedEntityTypeAndPropertyTypeCodes);
         v3api.logout(sessionToken);
     }
 
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyAssignment.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyAssignment.java
index 32d69393b62e38d39fb7b84d5db895034105638c..92fd9e2a24fe58df0a8743cfa2b75a42691e1fdb 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyAssignment.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyAssignment.java
@@ -22,6 +22,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationD
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistratorHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ISemanticAnnotationsHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id.IPluginId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyAssignmentFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyAssignmentPermId;
@@ -38,7 +40,8 @@ import java.util.List;
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.property.PropertyAssignment")
-public class PropertyAssignment implements Serializable, IPermIdHolder, IPropertyTypeHolder, IRegistrationDateHolder, IRegistratorHolder, ISemanticAnnotationsHolder
+public class PropertyAssignment
+        implements Serializable, IPermIdHolder, IPropertyTypeHolder, IRegistrationDateHolder, IRegistratorHolder, ISemanticAnnotationsHolder
 {
     private static final long serialVersionUID = 1L;
 
@@ -81,6 +84,9 @@ public class PropertyAssignment implements Serializable, IPermIdHolder, IPropert
     @JsonProperty
     private Date registrationDate;
 
+    @JsonProperty
+    private Plugin plugin;
+
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     public PropertyAssignmentFetchOptions getFetchOptions()
@@ -141,8 +147,7 @@ public class PropertyAssignment implements Serializable, IPermIdHolder, IPropert
         if (getFetchOptions() != null && getFetchOptions().hasEntityType())
         {
             return entityType;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Entity type has not been fetched.");
         }
@@ -162,8 +167,7 @@ public class PropertyAssignment implements Serializable, IPermIdHolder, IPropert
         if (getFetchOptions() != null && getFetchOptions().hasPropertyType())
         {
             return propertyType;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Property type has not been fetched.");
         }
@@ -222,8 +226,7 @@ public class PropertyAssignment implements Serializable, IPermIdHolder, IPropert
         if (getFetchOptions() != null && getFetchOptions().hasSemanticAnnotations())
         {
             return semanticAnnotations;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Semantic annotations have not been fetched.");
         }
@@ -242,8 +245,7 @@ public class PropertyAssignment implements Serializable, IPermIdHolder, IPropert
         if (getFetchOptions() != null && getFetchOptions().hasSemanticAnnotations())
         {
             return semanticAnnotationsInherited;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Semantic annotations have not been fetched.");
         }
@@ -263,8 +265,7 @@ public class PropertyAssignment implements Serializable, IPermIdHolder, IPropert
         if (getFetchOptions() != null && getFetchOptions().hasRegistrator())
         {
             return registrator;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Registrator has not been fetched.");
         }
@@ -290,11 +291,29 @@ public class PropertyAssignment implements Serializable, IPermIdHolder, IPropert
         this.registrationDate = registrationDate;
     }
 
+    @JsonIgnore
+    public Plugin getPlugin()
+    {
+        if (getFetchOptions() != null && getFetchOptions().hasPlugin())
+        {
+            return plugin;
+        } else
+        {
+            throw new NotFetchedException("Plugin has not been fetched.");
+        }
+    }
+
+    public void setPlugin(Plugin plugin)
+    {
+        this.plugin = plugin;
+    }
+
     // Method automatically generated with DtoGenerator
     @Override
     public String toString()
     {
-        return "PropertyAssignment entity type: " + (entityType != null ? entityType.getCode() : null) + ", property type: " + (propertyType != null ? propertyType.getCode() : null) + ", mandatory: " + mandatory;
+        return "PropertyAssignment entity type: " + (entityType != null ? entityType.getCode() : null) + ", property type: "
+                + (propertyType != null ? propertyType.getCode() : null) + ", mandatory: " + mandatory;
     }
 
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/fetchoptions/PropertyAssignmentFetchOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/fetchoptions/PropertyAssignmentFetchOptions.java
index c296fc1511d61208449b7251b26a5d88242693b1..f0a962d1d9b3e085089552f27f4383405de45744 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/fetchoptions/PropertyAssignmentFetchOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/fetchoptions/PropertyAssignmentFetchOptions.java
@@ -19,6 +19,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptions
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptionsToStringBuilder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.fetchoptions.EntityTypeFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyTypeFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.fetchoptions.SemanticAnnotationFetchOptions;
@@ -48,6 +49,9 @@ public class PropertyAssignmentFetchOptions extends FetchOptions<PropertyAssignm
 
     @JsonProperty
     private PropertyAssignmentSortOptions sort;
+    
+    @JsonProperty
+    private PluginFetchOptions plugin;
 
     // Method automatically generated with DtoGenerator
     public EntityTypeFetchOptions withEntityType()
@@ -136,6 +140,25 @@ public class PropertyAssignmentFetchOptions extends FetchOptions<PropertyAssignm
     {
         return registrator != null;
     }
+    
+    public PluginFetchOptions withPlugin()
+    {
+        if (plugin == null)
+        {
+            plugin = new PluginFetchOptions();
+        }
+        return plugin;
+    }
+    
+    public PluginFetchOptions withPluginUsing(PluginFetchOptions fetchOptions)
+    {
+        return plugin = fetchOptions;
+    }
+    
+    public boolean hasPlugin()
+    {
+        return plugin != null;
+    }
 
     // Method automatically generated with DtoGenerator
     @Override
diff --git a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
index 5267a108c147d3d71c6a5dabb9dbb6057ae8cc92..7fd792f5b5086483b2b17fafbb4dfd58005a4f35 100644
--- a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
+++ b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
@@ -2231,3 +2231,10 @@ Archiving Requested Search Criteria
 with Archiving Requested
 
 with Option
+
+get Plugin
+has Plugin
+set Plugin
+with Plugin
+with Plugin Using
+
diff --git a/openbis_standard_technologies/build.gradle b/openbis_standard_technologies/build.gradle
index 52b6d0428f651371d3971bad5d8e86aa6cc1b2d9..79b276ae7afa40327b54bbda3ee9551d5bd01454 100644
--- a/openbis_standard_technologies/build.gradle
+++ b/openbis_standard_technologies/build.gradle
@@ -84,7 +84,7 @@ dependencies {
 					 project(':screening'),
 					 project(':rtd_phosphonetx'),
 					 project(':plasmid'),
-					 'bioformats:bioformats:+',
+					 'bioformats:bioformats:5.9.2',
 					 'imagej:ij:+',
 					 'cisd:cisd-openbis-knime-server:+',
 					 'apache:xml-apis:+',
diff --git a/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/SimpleInfoObject.pyc b/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/SimpleInfoObject.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..183ac1b78d803a0a85229ae2b28e035ac42c5f96
Binary files /dev/null and b/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/SimpleInfoObject.pyc differ
diff --git a/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/Util.pyc b/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/Util.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7862ab27e20c12749825a15c2d4502ebb6eab295
Binary files /dev/null and b/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/Util.pyc differ
diff --git a/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/test_simpleInfoTest.pyc b/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/test_simpleInfoTest.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dc7ee0604fd06ea1cc94806217f62c6cf15b67c6
Binary files /dev/null and b/openbis_standard_technologies/dist/core-plugins/dropbox-monitor/2/dss/reporting-plugins/dropboxReporter/test_simpleInfoTest.pyc differ
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 f57c0264f63f1f5a15992e412df6d7fe7b2eaf5a..ead04e46d97b8ee25bba52a26b321f5b06f4f7c1 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
@@ -16,12 +16,14 @@
 # MasterDataRegistrationTransaction Class
 import os
 import sys
-from ch.systemsx.cisd.openbis.generic.server import CommonServiceProvider
-from ch.ethz.sis.openbis.generic.server.asapi.v3 import ApplicationServerApi
+
 from ch.ethz.sis.openbis.generic.asapi.v3.dto.operation import SynchronousOperationExecutionOptions
-from parsers import ExcelToPoiParser, PoiToDefinitionParser, DefinitionToCreationParser, CreationToOperationParser, DuplicatesHandler
-from openbis_logic import ServerDuplicatesCreationHandler
+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 search_engines import SearchEngine
 
 api = CommonServiceProvider.getApplicationContext().getBean(ApplicationServerApi.INTERNAL_SERVICE_NAME)
 
@@ -30,16 +32,21 @@ for excel_file_path in list_xls_files():
     poi_definitions = ExcelToPoiParser.parse(excel_file_path)
     definitions = PoiToDefinitionParser.parse(poi_definitions)
     partial_creations = DefinitionToCreationParser.parse(definitions)
-    print(partial_creations)
     for creation_type, partial_creation in partial_creations.items():
         if creation_type not in creations:
             creations[creation_type] = partial_creation
         else:
             creations[creation_type].extend(partial_creation)
 distinct_creations = DuplicatesHandler.get_distinct_creations(creations)
+
 sessionToken = api.loginAsSystem()
-server_duplicates_handler = ServerDuplicatesCreationHandler(api, sessionToken, distinct_creations)
-creations = server_duplicates_handler.remove_already_existing_elements()
+search_engine = SearchEngine(api, sessionToken)
+existing_elements = search_engine.find_all_existing_elements(distinct_creations)
+server_duplicates_handler = OpenbisLogicHandler(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()
+
 operations = CreationToOperationParser.parse(creations)
 result = api.executeOperations(sessionToken, operations, SynchronousOperationExecutionOptions())
 print("========================eln-life-sciences-types xls ingestion result========================")
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/life-sciences-types/scripts/valid.py
index 14afec52e04849fdb34de83bdadb5a5793868218..d87950f600d11b50b96dcedef36fe8f36971140c 100644
--- 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/life-sciences-types/scripts/valid.py
@@ -1,4 +1,3 @@
 def validate(entity, isNew):
   if isNew:
-    if not entity.properties() is None:
-      return "It is not allowed to attach properties to new sample."
\ No newline at end of file
+    return
\ No newline at end of file
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/life-sciences-types/types.xls
index 3cdae5a2d0b77a23eaf1d1878982744bb55b51a1..473cc33398b77fb5980ba414df0437cd2cc91825 100644
Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/types.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/types.xls differ
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
new file mode 100644
index 0000000000000000000000000000000000000000..52bd9cc8dd6c1020b5d9b9624b4a53c092557beb
Binary files /dev/null and b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/life-sciences-types/types2.xls differ
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/openbis_logic.py
index eb4006b87e254ac46b1ae55f9b78583d76c46fda..2e6c63b0bdd118e7bcb872c00ddf8a8756934101 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/openbis_logic.py
@@ -1,130 +1,111 @@
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id import ExperimentPermId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id import ProjectPermId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id import SamplePermId, SampleIdentifier
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id import SpacePermId
 from parsers import VocabularyDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, SampleTypeDefinitionToCreationParser, \
                     ExperimentTypeDefinitionToCreationParser, DatasetTypeDefinitionToCreationParser, SpaceDefinitionToCreationParser, \
-                    ProjectDefinitionToCreationParser, ScriptDefinitionToCreationParser
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.search import VocabularySearchCriteria, \
-    SearchVocabulariesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search import PropertyTypeSearchCriteria, \
-    SearchPropertyTypesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search import SampleTypeSearchCriteria, \
-    SearchSampleTypesOperation, SampleSearchCriteria, SearchSamplesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.search import ExperimentTypeSearchCriteria, \
-    SearchExperimentTypesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search import DataSetTypeSearchCriteria, \
-    SearchDataSetTypesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions import VocabularyFetchOptions
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions import PropertyTypeFetchOptions
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions import SampleTypeFetchOptions, \
-    SampleFetchOptions
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions import ExperimentTypeFetchOptions
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions import DataSetTypeFetchOptions
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.operation import SynchronousOperationExecutionOptions
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search import SearchOperator
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.search import SpaceSearchCriteria, \
-    SearchSpacesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.fetchoptions import SpaceFetchOptions
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.search import ProjectSearchCriteria, \
-    SearchProjectsOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.fetchoptions import ProjectFetchOptions
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.search import PluginSearchCriteria, \
-    SearchPluginsOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions import PluginFetchOptions
+                    ProjectDefinitionToCreationParser, ExperimentDefinitionToCreationParser, ScriptDefinitionToCreationParser, SampleDefinitionToCreationParser
 
 
-class ServerDuplicatesCreationHandler(object):
-    
-    def __init__(self, api, sesstion_token, creations):
-        self.api = api
-        self.session_token = sesstion_token
+class OpenbisLogicHandler(object):
+
+    def __init__(self, creations, existing_elements):
         self.creations = creations
-    
-    def remove_already_existing_elements(self):
-        duplicates_removal_strategies = [
-            {
-            'creations_type': VocabularyDefinitionToCreationParser.type,
-            'search_criteria_object' : VocabularySearchCriteria,
-            'search_operation':SearchVocabulariesOperation,
-            'fetch_options':VocabularyFetchOptions,
-            'distinct_property_name': 'code'
-            },
-            {
-            'creations_type': PropertyTypeDefinitionToCreationParser.type,
-            'search_criteria_object' : PropertyTypeSearchCriteria,
-            'search_operation': SearchPropertyTypesOperation,
-            'fetch_options': PropertyTypeFetchOptions,
-            'distinct_property_name': 'code'
-            },
-            {
-            'creations_type': SampleTypeDefinitionToCreationParser.type,
-            'search_criteria_object' : SampleTypeSearchCriteria,
-            'search_operation': SearchSampleTypesOperation,
-            'fetch_options': SampleTypeFetchOptions,
-            'distinct_property_name': 'code'
-            },
-            {
-            'creations_type': ExperimentTypeDefinitionToCreationParser.type,
-            'search_criteria_object' : ExperimentTypeSearchCriteria,
-            'search_operation': SearchExperimentTypesOperation,
-            'fetch_options': ExperimentTypeFetchOptions,
-            'distinct_property_name': 'code'
-            },
-            {
-            'creations_type': DatasetTypeDefinitionToCreationParser.type,
-            'search_criteria_object' : DataSetTypeSearchCriteria,
-            'search_operation': SearchDataSetTypesOperation,
-            'fetch_options': DataSetTypeFetchOptions,
-            'distinct_property_name': 'code'
-            },
-            {
-            'creations_type': SpaceDefinitionToCreationParser.type,
-            'search_criteria_object' : SpaceSearchCriteria,
-            'search_operation': SearchSpacesOperation,
-            'fetch_options': SpaceFetchOptions,
-            'distinct_property_name': 'code'
-            },
-            {
-            'creations_type': ProjectDefinitionToCreationParser.type,
-            'search_criteria_object' : ProjectSearchCriteria,
-            'search_operation': SearchProjectsOperation,
-            'fetch_options': ProjectFetchOptions,
-            'distinct_property_name': 'code'
-            },
-            {
-            'creations_type': ScriptDefinitionToCreationParser.type,
-            'search_criteria_object' : PluginSearchCriteria,
-            'search_operation': SearchPluginsOperation,
-            'fetch_options': PluginFetchOptions,
-            'distinct_property_name': 'name'
-            }
-        ]
-        for strategy in duplicates_removal_strategies:
-            if strategy['creations_type'] in self.creations:
-                self.creations[strategy['creations_type']] = self.remove_specific_existing_elements(**strategy)
+        self.existing_elements = existing_elements
+
+    def rewrite_parentchild_creationid_to_permid(self):
+        if ProjectDefinitionToCreationParser.type in self.creations:
+            for creation in self.creations[ProjectDefinitionToCreationParser.type]:
+                for existing_element in self.existing_elements[SpaceDefinitionToCreationParser.type]:
+                    if existing_element.code == creation.spaceId.creationId:
+                        creation.spaceId = SpacePermId(str(existing_element.permId))
+                        break
+        if ExperimentDefinitionToCreationParser.type in self.creations:
+            for creation in self.creations[ExperimentDefinitionToCreationParser.type]:
+                for existing_element in self.existing_elements[ProjectDefinitionToCreationParser.type]:
+                    if existing_element.code == creation.projectId.creationId:
+                        creation.projectId = ProjectPermId(str(existing_element.permId))
+                        break
+        if SampleDefinitionToCreationParser.type in self.creations:
+            for creation in self.creations[SampleDefinitionToCreationParser.type]:
+                if creation.spaceId is not None:
+                    for existing_element in self.existing_elements[SpaceDefinitionToCreationParser.type]:
+                        if existing_element.code == creation.spaceId.creationId:
+                            creation.spaceId = SpacePermId(str(existing_element.permId))
+                            break
+                if creation.projectId is not None:
+                    for existing_element in self.existing_elements[ProjectDefinitionToCreationParser.type]:
+                        if existing_element.code == creation.projectId.creationId:
+                            creation.projectId = ProjectPermId(str(existing_element.permId))
+                            break
+                if creation.experimentId is not None:
+                    for existing_element in self.existing_elements[ExperimentDefinitionToCreationParser.type]:
+                        if existing_element.code == creation.experimentId.creationId:
+                            creation.experimentId = ExperimentPermId(str(existing_element.permId))
+                            break
+
+                rewritten_children = []
+                if creation.childIds is not None:
+                    for child in creation.childIds:
+                        new_id = None
+                        for existing_element in self.existing_elements[SampleDefinitionToCreationParser.type]:
+                            if existing_element.permId.permId == child.creationId:
+                                new_id = existing_element.permId
+                                break
+                            elif existing_element.identifier.identifier == child.creationId:
+                                new_id = existing_element.identifier
+                                break
+
+                        if new_id is None:
+                            rewritten_children.append(child)
+                        else:
+                            rewritten_children.append(new_id)
+
+                rewritten_parents = []
+                if creation.parentIds is not None:
+                    for parent in creation.parentIds:
+                        new_id = None
+                        for existing_element in self.existing_elements[SampleDefinitionToCreationParser.type]:
+                            if existing_element.permId.permId == parent.creationId:
+                                new_id = existing_element.permId
+                                break
+                            elif existing_element.identifier.identifier == parent.creationId:
+                                new_id = existing_element.identifier
+                                break
+
+                        if new_id is None:
+                            rewritten_parents.append(parent)
+                        else:
+                            rewritten_parents.append(new_id)
+                creation.setChildIds(rewritten_children)
+                creation.setParentIds(rewritten_parents)
+
         return self.creations
 
-    def remove_specific_existing_elements(self, creations_type, search_criteria_object, search_operation, fetch_options, distinct_property_name):
-        if creations_type in self.creations:
-            search_criteria = self.get_search_criteria(creations_type, search_criteria_object())
-            result = self.execute_search_operation(search_operation(search_criteria, fetch_options()))
-            return self.filter_creations_from_existing_objects(creations_type, result.getObjects(), distinct_property_name)
+    def remove_existing_elements_from_creations(self):
+        for creations_type, existing_elements in self.existing_elements.items():
+            if creations_type == SampleDefinitionToCreationParser.type:
+                existing_object_codes = [object.identifier.identifier for object in existing_elements]
+                self.creations[creations_type] = list(filter(lambda creation: creation.code is None or self._create_sample_identifier_string(creation) not in existing_object_codes, self.creations[creations_type]))
+            else:
+                distinct_property_name = self._get_distinct_property_name(creations_type)
+                self.creations[creations_type] = self._filter_creations_from_existing_objects(creations_type, existing_elements, distinct_property_name)
+        return self.creations
 
-    def get_search_criteria(self, creations_type, search_criteria):
-        specific_creations = self.creations[creations_type]
-        
-        if 'withCodes' in dir(search_criteria):
-            search_criteria.withCodes().thatIn([creation.code for creation in specific_creations])
-        elif 'withName' in dir(search_criteria):
-            for creation in specific_creations:
-                search_criteria.withName().thatEquals(creation.name)
-            search_criteria.withOrOperator()
+    def _get_distinct_property_name(self, creation_type):
+        if creation_type == ScriptDefinitionToCreationParser.type:
+            return 'name'
         else:
-            for creation in specific_creations:
-                search_criteria.withCode().thatEquals(creation.code)
-            search_criteria.withOrOperator()
-        return search_criteria
+            return 'code'
 
-    def execute_search_operation(self, operation):
-        return self.api.executeOperations(self.session_token, [operation], SynchronousOperationExecutionOptions()).getResults().get(0).getSearchResult()
+    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
 
-    def filter_creations_from_existing_objects(self, creations_type, existing_objects, attr):
+    def _filter_creations_from_existing_objects(self, creations_type, existing_objects, attr):
         existing_object_codes = [getattr(object, attr) for object in existing_objects]
         return list(filter(lambda creation: getattr(creation, attr) not in existing_object_codes, self.creations[creations_type]))
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers.py
deleted file mode 100644
index 0660d40d33ad535797d0bf1127c8dc520f20f2c2..0000000000000000000000000000000000000000
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers.py
+++ /dev/null
@@ -1,682 +0,0 @@
-from copy import deepcopy
-from org.python.core.io import FileIO
-from org.apache.poi.ss.usermodel import WorkbookFactory
-from org.apache.poi.ss.usermodel import CellType
-from org.apache.commons.lang import StringUtils
-from org.apache.poi.ss.util import NumberToTextConverter
-from java.lang import UnsupportedOperationException
-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 CreateVocabulariesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.create import VocabularyTermCreation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create import SampleTypeCreation, CreateSampleTypesOperation, \
-    SampleCreation, CreateSamplesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.create import PropertyAssignmentCreation, PropertyTypeCreation, CreatePropertyTypesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.id import VocabularyPermId
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id import EntityTypePermId
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype import EntityKind
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.create import ExperimentTypeCreation, CreateExperimentTypesOperation, \
-    ExperimentCreation, CreateExperimentsOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create import DataSetTypeCreation, CreateDataSetTypesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id import PropertyTypePermId
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.property import DataType
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.create import SpaceCreation, \
-    CreateSpacesOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create import ProjectCreation, \
-    CreateProjectsOperation
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id import CreationId
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create import PluginCreation, \
-    CreatePluginsOperation
-from file_handling import get_script, get_filename_from_path
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id import PluginPermId
-from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin import PluginType
-
-
-class ExcelToPoiParser(object):
-
-    @staticmethod
-    def parse(excel_file_path):
-        workbook = WorkbookFactory.create(FileIO(excel_file_path, 'r').asInputStream())
-
-        data_colection = []
-        for sheet in workbook.sheetIterator():
-            # get whole one definition
-            definitions = []
-            i = 0
-            sheet_done = False
-            while not sheet_done:
-                definition_rows = []
-                while not ExcelToPoiParser.is_row_empty(sheet.getRow(i)):
-                    row = sheet.getRow(i)
-                    first_cell = row.getCell(0)
-                    if first_cell is None or first_cell == CellType.BLANK:
-                        continue
-                    else:
-                        definition_rows.append(row)
-                        i += 1
-                i += 1  # skip empty row
-                        
-                if not definition_rows:
-                    sheet_done = True
-                else:
-                    definitions.append(definition_rows)
-        
-        workbook.close()
-
-        definitions_stripped = []
-        for definition in definitions:
-            definition_stripped = []
-            for row in definition:
-                row_stripped = {}
-                for cell in row.cellIterator():
-                    cell_value = ExcelToPoiParser.extract_string_value_from(cell)
-                    cell_value = cell_value if cell_value != '' else None
-                    cell_col = cell.getColumnIndex()
-                    row_stripped[cell_col] = cell_value
-                definition_stripped.append(row_stripped)
-            definitions_stripped.append(definition_stripped)
-              
-        return definitions_stripped
-            
-    @staticmethod
-    def extract_string_value_from(cell):
-        cell_type = cell.getCellTypeEnum()
-        if cell_type == CellType.BLANK:
-            return "";
-        elif cell_type == CellType.BOOLEAN:
-            return str(cell.getBooleanCellValue());
-        elif cell_type == CellType.NUMERIC:
-            return NumberToTextConverter.toText(cell.getNumericCellValue());
-        elif cell_type == CellType.STRING:
-            return cell.getStringCellValue();
-        elif cell_type == CellType.FORMULA:
-            raise SyntaxError("Excel formulas are not supported but one was found in cell " + extractCellPosition(cell));
-        elif cell_type == CellType.ERROR:
-            raise SyntaxError("There is an error in cell " + extractCellPosition(cell));
-        else:
-            raise SyntaxError("Unknown data type of cell " + extractCellPosition(cell));
-             
-    @staticmethod
-    def is_row_empty(row):
-        if row is None:
-            return True
-        if row.getLastCellNum <= 0:
-            return True
-
-        return all(ExcelToPoiParser.is_cell_empty(cell) for cell in row.cellIterator())
-    
-    @staticmethod
-    def is_cell_empty(cell):
-        if cell is not None and cell.getCellType() != CellType.BLANK and StringUtils.isNotBlank(cell.toString()):
-            return False;
-        return True
-
-
-class Definition(object):
-    '''
-        Used to hold values for object(Vocabulary, SampleType etc.) creation.
-    '''
-
-    def __init__(self):
-        self.type = None
-        self.attributes = {}
-        self.properties = []
-     
-    def __str__(self):
-        return "\n".join([
-            "Definition type:",
-            str(self.type),
-            "Attributes:",
-            str(self.attributes),
-            "Properties:",
-            str(self.properties),
-            "==================" * 3])
-
-    __repr__ = __str__
-        
-
-class DefinitionParserFactory(object):
-        
-        @staticmethod
-        def get_parser(definition_type):
-            if definition_type in ['VOCABULARY_TYPE', 'SAMPLE_TYPE', 'EXPERIMENT_TYPE', 'DATASET_TYPE', 'SPACE', 'PROJECT', 'EXPERIMENT', 'SAMPLE']:
-                return GeneralDefinitionParser
-            else:
-                raise UnsupportedOperationException("Definition of " + str(definition_type) + " is not supported (probably yet).")
-
-
-class GeneralDefinitionParser(object):
-
-    @staticmethod
-    def parse(poi_definition):
-        DEFINITION_TYPE_ROW = 0
-        DEFINITION_TYPE_CELL = 0
-        ATTRIBUTES_HEADER_ROW = 1
-        ATTRIBUTES_VALUES_ROW = 2
-        PROPERTIES_HEADER_ROW = 3
-        PROPERTIES_VALUES_ROW_START = 4
-        
-        definition = Definition()
-        definition.type = poi_definition[DEFINITION_TYPE_ROW][DEFINITION_TYPE_CELL]
-        
-        for col, header in poi_definition[ATTRIBUTES_HEADER_ROW].items():
-            cell_value = poi_definition[ATTRIBUTES_VALUES_ROW][col]
-            definition.attributes[header] = cell_value
-        
-        if GeneralDefinitionParser.hasProperties(poi_definition):
-            properties_headers = poi_definition[PROPERTIES_HEADER_ROW]
-            
-            for property_definitions in poi_definition[PROPERTIES_VALUES_ROW_START:]:
-                property = {}
-                for col, header in properties_headers.items():
-                    property[header] = property_definitions[col]
-                definition.properties.append(property)
-        
-        return definition
-        
-    @staticmethod
-    def hasProperties(poi_definition):
-        PROPERTIES_HEADER_ROW = 3
-        return len(poi_definition) > PROPERTIES_HEADER_ROW
-
-
-class PoiCleaner(object):
-    
-    @staticmethod
-    def hasProperties(poi_definition):
-        PROPERTIES_HEADER_ROW = 3
-        return len(poi_definition) > PROPERTIES_HEADER_ROW
-    
-    @staticmethod
-    def clean_data(xls_definitions):
-        DEFINITION_TYPE_ROW = 0
-        DEFINITION_TYPE_CELL = 0
-        ATTRIBUTES_HEADER_ROW = 1
-        ATTRIBUTES_VALUES_ROW = 2
-        PROPERTIES_HEADER_ROW = 3
-        PROPERTIES_VALUES_ROW_START = 4
-        
-        xls_def = deepcopy(xls_definitions)
-        '''
-            First row can only have definition type, rest is trimmed
-        '''
-        for definition in xls_def:
-            definition[DEFINITION_TYPE_ROW] = {0:definition[DEFINITION_TYPE_ROW][DEFINITION_TYPE_CELL]}
-            '''
-                Header rows cannot have empty cells
-            '''
-            PoiCleaner.delete_empty_cells_from(definition, ATTRIBUTES_HEADER_ROW)
-            if PoiCleaner.hasProperties(definition):
-                PoiCleaner.delete_empty_cells_from(definition, PROPERTIES_HEADER_ROW)
-            '''
-                Values that do not have corresponding headers are removed.
-            '''
-            PoiCleaner.delete_cells_if_no_header(definition, ATTRIBUTES_HEADER_ROW, ATTRIBUTES_VALUES_ROW)
-            if PoiCleaner.hasProperties(definition):
-                for property_value_row_num in range(PROPERTIES_VALUES_ROW_START, len(definition)):
-                    PoiCleaner.delete_cells_if_no_header(definition, PROPERTIES_HEADER_ROW, property_value_row_num)
-            '''
-                All attributes and properties should have corresponding value in values row when header exists.
-                If there's no corresponding value, None is inserted.
-            '''
-            PoiCleaner.create_cells_if_no_value_but_header_exists(definition, ATTRIBUTES_HEADER_ROW, ATTRIBUTES_VALUES_ROW)
-            if PoiCleaner.hasProperties(definition):
-                for property_value_row_num in range(PROPERTIES_VALUES_ROW_START, len(definition)):
-                    PoiCleaner.create_cells_if_no_value_but_header_exists(definition, PROPERTIES_HEADER_ROW, property_value_row_num)
-            '''
-                Headers to lowercase
-            '''
-            definition[ATTRIBUTES_HEADER_ROW] = PoiCleaner.dict_values_to_lowercase(definition[ATTRIBUTES_HEADER_ROW])
-            if PoiCleaner.hasProperties(definition):
-                definition[PROPERTIES_HEADER_ROW] = PoiCleaner.dict_values_to_lowercase(definition[PROPERTIES_HEADER_ROW])
-                
-        return xls_def
-    
-    @staticmethod
-    def delete_empty_cells_from(definition, row_number):
-        header_cell_cols_to_pop = []
-        for cell_col, cell in definition[row_number].items():
-            if cell is None or cell == '':
-                header_cell_cols_to_pop.append(cell_col)
-        
-        for cell_col in header_cell_cols_to_pop:
-            del definition[row_number][cell_col]
-            
-        return definition
-        
-    @staticmethod   
-    def delete_cells_if_no_header(definition, header_row_number, value_row_number):
-        values_cell_cols_to_pop = []
-        for cell_col in definition[value_row_number]:
-            if cell_col not in definition[header_row_number]:
-                values_cell_cols_to_pop.append(cell_col)
-
-        for cell_col in values_cell_cols_to_pop:
-            del definition[value_row_number][cell_col]
-            
-        return definition
-    
-    @staticmethod    
-    def create_cells_if_no_value_but_header_exists(definition, header_row_number, value_row_number):
-        values_cell_cols_to_insert = []
-        for cell_col in definition[header_row_number]:
-            if cell_col not in definition[value_row_number]:
-                values_cell_cols_to_insert.append(cell_col)
-
-        for cell_col in values_cell_cols_to_insert:
-            definition[value_row_number][cell_col] = None
-
-        return definition
-
-    @staticmethod
-    def dict_values_to_lowercase(row):
-        return dict((k, v.lower()) for (k, v) in row.items())
-
-
-class PoiToDefinitionParser(object):
-
-    @staticmethod
-    def parse(uncleaned_poi_definitions):
-        '''
-            Expecting definitions to be in such layout:
-            [
-                `DEFINITIONS_LIST`
-                [
-                    `DEFINITION`
-                    {
-                        `ROWS`
-                        (column, row) : string_value,
-                        .
-                        .
-                        .
-                    }
-                ],
-                .
-                .
-                .
-            ]
-        '''
-        poi_definitions = PoiCleaner.clean_data(uncleaned_poi_definitions)
-        definitons = []
-        for poi_definition in poi_definitions:
-            FIRST_ROW = 0
-            FIRST_CELL = 0
-            definition_type = poi_definition[FIRST_ROW][FIRST_CELL]
-            definition_parser = DefinitionParserFactory.get_parser(definition_type)
-            definition = definition_parser.parse(poi_definition)
-            definitons.append(definition)
-
-        return definitons
-
-
-class DefinitionToCreationParser(object):
-
-    @staticmethod
-    def parse(definitions):
-        creations = {}
-        
-        for definition in definitions:
-            # One definition may contain more than one creation
-            parsers = DefinitionToCreationParserFactory.getParsers(definition)
-            for parser in parsers:
-                creation = parser.parse(definition)
-                if creation is None or creation == []:
-                    continue
-                creation_type = parser.get_type()
-                if creation_type not in creations:
-                    creations[creation_type] = []
-                creations[creation_type].extend(creation if type(creation) == list else [creation])
-
-        return creations
-
-    @staticmethod
-    def get_boolean_from_string(text):
-        return True if text.lower() == u'true' else False
-
-    
-class PropertyTypeDefinitionToCreationParser(object):
-
-    type = "PropertyTypeCreation"
-
-    @staticmethod
-    def parse(definition):
-        property_creations = []
-        for property in definition.properties:
-            property_type_creation = PropertyTypeCreation()
-            property_type_creation.code = property[u'code']
-            if property[u'code'].startswith(u'$'):
-                property_type_creation.setInternalNameSpace(True)
-            property_type_creation.setLabel(property[u'property label'])
-            property_type_creation.setDataType(DataType.valueOf(property[u'property type']))
-            property_type_creation.setVocabularyId(VocabularyPermId(property[u'vocabulary code']) if property[u'vocabulary code'] is not None else None)
-            property_type_creation.setDescription(property[u'description'])
-            property_creations.append(property_type_creation)
-
-        return property_creations
-
-    @staticmethod
-    def get_type():
-        return PropertyTypeDefinitionToCreationParser.type
-
-
-class VocabularyDefinitionToCreationParser(object):
-    
-    type = "VocabularyCreation"
-    
-    @staticmethod
-    def parse(definition):
-        vocabulary_creation = VocabularyCreation()
-        vocabulary_creation.setCode(definition.attributes[u'code'])
-        vocabulary_creation.setDescription(definition.attributes[u'description'])
-        
-        vocabulary_creations_terms = []
-        for property in definition.properties:
-            vocabulary_creation_term = VocabularyTermCreation()
-            vocabulary_creation_term.setCode(property[u'code'])
-            vocabulary_creation_term.setLabel(property[u'label'])
-            vocabulary_creation_term.setDescription(property[u'description'])
-            vocabulary_creations_terms.append(vocabulary_creation_term)
-            
-        vocabulary_creation.setTerms(vocabulary_creations_terms)
-
-        return vocabulary_creation
-    
-    @staticmethod
-    def get_type():
-        return VocabularyDefinitionToCreationParser.type
-
-
-class PropertyAssignmentDefinitionToCreationParser(object):
-    
-    type = "PropertyAssignmentCreation"
-    
-    @staticmethod
-    def parse(property):
-        property_assingment_creation = PropertyAssignmentCreation()
-        is_mandatory = DefinitionToCreationParser.get_boolean_from_string(property[u'mandatory'])
-        property_assingment_creation.setMandatory(is_mandatory)
-        should_show_in_edit_view = DefinitionToCreationParser.get_boolean_from_string(property[u'show in edit views'])
-        property_assingment_creation.setShowInEditView(should_show_in_edit_view)
-        property_assingment_creation.setSection(property[u'section'])
-        property_assingment_creation.setPropertyTypeId(PropertyTypePermId(property[u'code']))
-        
-        return property_assingment_creation
-    
-    @staticmethod
-    def get_type():
-        return PropertyAssignmentDefinitionToCreationParser.type
-
-
-class SampleTypeDefinitionToCreationParser(object):
-    
-    type = "SampleTypeCreation"
-   
-    @staticmethod
-    def parse(definition):
-        sample_creation = SampleTypeCreation()
-        sample_creation.setCode(definition.attributes[u'code'])
-        should_auto_generate_codes = DefinitionToCreationParser.get_boolean_from_string(definition.attributes[u'auto generate codes'])
-        sample_creation.setAutoGeneratedCode(should_auto_generate_codes)
-        validation_script_path = definition.attributes[u'validation script']
-#         if validation_script_path is not None:
-#             sample_creation.setValidationPluginId(PluginPermId(get_filename_from_path(validation_script_path)))
-
-        property_assingment_creations = []
-        for property in definition.properties:
-            property_assingment_creation = PropertyAssignmentDefinitionToCreationParser.parse(property)
-            property_assingment_creations.append(property_assingment_creation)
-
-        sample_creation.setPropertyAssignments(property_assingment_creations)
-        return sample_creation
-    
-    @staticmethod
-    def get_type():
-        return SampleTypeDefinitionToCreationParser.type
-
-
-class ExperimentTypeDefinitionToCreationParser(object):
-    
-    type = "ExperimentTypeCreation"
-
-    @staticmethod
-    def parse(definition):
-        experiment_type_creation = ExperimentTypeCreation()
-        experiment_type_creation.setCode(definition.attributes[u'code'])
-
-        property_assingment_creations = []
-        for property in definition.properties:
-            property_assingment_creation = PropertyAssignmentDefinitionToCreationParser.parse(property)
-            property_assingment_creations.append(property_assingment_creation)
-
-        experiment_type_creation.setPropertyAssignments(property_assingment_creations)
-        return experiment_type_creation
-
-    @staticmethod
-    def get_type():
-        return ExperimentTypeDefinitionToCreationParser.type
-
-
-class DatasetTypeDefinitionToCreationParser(object):
-    
-    type = "DatasetTypeCreation"
-
-    @staticmethod
-    def parse(definition):
-        dataset_type_creation = DataSetTypeCreation()
-        dataset_type_creation.setCode(definition.attributes[u'code'])
-        
-        property_assingment_creations = []
-        for property in definition.properties:
-            property_assingment_creation = PropertyAssignmentDefinitionToCreationParser.parse(property)
-            property_assingment_creations.append(property_assingment_creation)
-
-        dataset_type_creation.setPropertyAssignments(property_assingment_creations)
-        return dataset_type_creation
-    
-    @staticmethod
-    def get_type():
-        return DatasetTypeDefinitionToCreationParser.type
-
-    
-class SpaceDefinitionToCreationParser(object):
-    
-    type = "SpaceCreation"
-    
-    @staticmethod
-    def parse(definition):
-        space_creation = SpaceCreation()
-        space_creation.setCode(definition.attributes[u'code'])
-        space_creation.setDescription(definition.attributes[u'description'])
-        creation_id = definition.attributes[u'code']
-        space_creation.setCreationId(CreationId(creation_id))
-        return space_creation
-        
-    @staticmethod
-    def get_type():
-        return SpaceDefinitionToCreationParser.type
-
-    
-class ProjectDefinitionToCreationParser(object):
-    
-    type = "ProjectCreation"
-    
-    @staticmethod
-    def parse(definition):
-        project_creation = ProjectCreation()
-        project_creation.setCode(definition.attributes[u'code'])
-        project_creation.setDescription(definition.attributes[u'description'])
-        project_creation.setSpaceId(CreationId(definition.attributes[u'space']))
-        creation_id = definition.attributes[u'code']
-        project_creation.setCreationId(CreationId(creation_id))
-        return project_creation
-
-    @staticmethod
-    def get_type():
-        return ProjectDefinitionToCreationParser.type
-
-    
-class ExperimentDefinitionToCreationParser(object):
-    
-    type = "ExperimentCreation"
-    
-    @staticmethod
-    def parse(definition):
-        experiments = []
-        mandatory_attributes = [u'code', u'project']
-        for experiment_properties in definition.properties:
-            experiment_creation = ExperimentCreation()
-            experiment_creation.setTypeId(EntityTypePermId(definition.attributes[u'experiment type']))
-            experiment_creation.setCode(experiment_properties[u'code'])
-            experiment_creation.setProjectId(CreationId(experiment_properties[u'project']))
-            creation_id = experiment_properties[u'code']
-            experiment_creation.setCreationId(CreationId(creation_id))
-            for property, value in experiment_properties.items():
-                if property not in mandatory_attributes:
-                    experiment_creation.setProperty(property, value)
-            experiments.append(experiment_creation)
-        return experiments
-    
-    @staticmethod
-    def get_type():
-        return ExperimentDefinitionToCreationParser.type
-
-    
-class SampleDefinitionToCreationParser(object):
-    
-    type = "SampleCreation"
-    
-    @staticmethod
-    def parse(definition):
-        samples = []
-        mandatory_attributes = [u'code', u'space', u'project', u'experiment', u'auto generate code', u'parents', u'children']
-        for sample_properties in definition.properties:
-            sample_creation = SampleCreation()
-            sample_creation.setTypeId(EntityTypePermId(definition.attributes[u'sample type']))
-            if u'code' in sample_properties and sample_properties[u'code'] is not None and sample_properties[u'code'] != '':
-                sample_creation.setCode(sample_properties[u'code'])
-                creation_id = sample_properties[u'code']
-                sample_creation.setCreationId(CreationId(creation_id))
-            if u'auto generate code' in sample_properties and sample_properties[u'auto generate code'] is not None and sample_properties[u'auto generate code'] != '':
-                sample_creation.setAutoGeneratedCode(DefinitionToCreationParser.get_boolean_from_string(sample_properties[u'auto generate code']))
-            if u'space' in sample_properties and sample_properties[u'space'] is not None:
-                sample_creation.setSpaceId(CreationId(sample_properties[u'space']))
-            if u'project' in sample_properties and sample_properties[u'project'] is not None:
-                sample_creation.setProjectId(CreationId(sample_properties[u'project']))
-            if u'experiment' in sample_properties and sample_properties[u'experiment'] is not None:
-                sample_creation.setExperimentId(CreationId(sample_properties[u'experiment']))
-            for property, value in sample_properties.items():
-                if property not in mandatory_attributes:
-                    sample_creation.setProperty(property, value)
-            samples.append(sample_creation)
-        return samples
-    
-    @staticmethod
-    def get_type():
-        return SampleDefinitionToCreationParser.type
-
-    
-class ScriptDefinitionToCreationParser(object):
-    
-    type = "ScriptCreation"
-    
-    @staticmethod
-    def parse(definition):
-        scripts = []
-        validation_script_path = definition.attributes[u'validation script']
-        if validation_script_path is not None:
-            validation_script_creation = PluginCreation()
-            script_file = open(get_script(validation_script_path))
-            script = script_file.read()
-            script_file.close()
-            validation_script_creation.setName(get_filename_from_path(validation_script_path))
-            validation_script_creation.setScript(script)
-            validation_script_creation.setPluginType(PluginType.ENTITY_VALIDATION)
-            scripts.append(validation_script_creation)
-        
-        for property in definition.properties:
-            dynamic_prop_script_path = property[u'dynamic script']
-            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.setName(get_filename_from_path(dynamic_prop_script_path))
-                validation_script_creation.setScript(script)
-                validation_script_creation.setPluginType(PluginType.DYNAMIC_PROPERTY)
-                scripts.append(validation_script_creation)
-
-        return scripts
-
-    @staticmethod
-    def parse_script():
-        pass
-    
-    @staticmethod
-    def get_type():
-        return ScriptDefinitionToCreationParser.type
-
-
-class DefinitionToCreationParserFactory(object):
-    
-    @staticmethod
-    def getParsers(definition):
-        if definition.type == u'VOCABULARY_TYPE':
-            return [VocabularyDefinitionToCreationParser]
-        elif definition.type == u'SAMPLE_TYPE':
-            return [SampleTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, ScriptDefinitionToCreationParser]
-        elif definition.type == u'EXPERIMENT_TYPE':
-            return [ExperimentTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser]
-        elif definition.type == u'DATASET_TYPE':
-            return [DatasetTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser]
-        elif definition.type == u'SPACE':
-            return [SpaceDefinitionToCreationParser]
-        elif definition.type == u'PROJECT':
-            return [ProjectDefinitionToCreationParser]
-        elif definition.type == u'EXPERIMENT':
-            return [ExperimentDefinitionToCreationParser]
-        elif definition.type == u'SAMPLE':
-            return [SampleDefinitionToCreationParser]
-        else:
-            raise UnsupportedOperationException("Definition of " + str(definition.type) + " is not supported (probably yet).")
-
-
-class DuplicatesHandler(object):
-    
-    @staticmethod
-    def get_distinct_creations(creations):
-        distinct_creations = {}
-        for creation_type, creations in creations.items():
-            if creation_type == ScriptDefinitionToCreationParser.type:
-                distinct_creations[creation_type] = dict((creation.name, creation) for creation in creations).values()
-            elif creation_type not in [SampleDefinitionToCreationParser.type]:
-                distinct_creations[creation_type] = dict((creation.code, creation) for creation in creations).values()
-            else:
-                distinct_creations[creation_type] = list(creations)
-        return distinct_creations
-
-
-class CreationToOperationParser(object):
-
-    @staticmethod
-    def parse(creations):
-        creation_operations = []
-        if VocabularyDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreateVocabulariesOperation(creations[VocabularyDefinitionToCreationParser.type]))
-        if PropertyTypeDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreatePropertyTypesOperation(creations[PropertyTypeDefinitionToCreationParser.type]))
-        if SampleTypeDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreateSampleTypesOperation(creations[SampleTypeDefinitionToCreationParser.type]))
-        if ExperimentTypeDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreateExperimentTypesOperation(creations[ExperimentTypeDefinitionToCreationParser.type]))
-        if DatasetTypeDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreateDataSetTypesOperation(creations[DatasetTypeDefinitionToCreationParser.type]))
-        if SpaceDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreateSpacesOperation(creations[SpaceDefinitionToCreationParser.type]))
-        if ProjectDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreateProjectsOperation(creations[ProjectDefinitionToCreationParser.type]))
-        if ExperimentDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreateExperimentsOperation(creations[ExperimentDefinitionToCreationParser.type]))
-        if SampleDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreateSamplesOperation(creations[SampleDefinitionToCreationParser.type]))
-        if ScriptDefinitionToCreationParser.type in creations:
-            creation_operations.append(CreatePluginsOperation(creations[ScriptDefinitionToCreationParser.type]))
-        return creation_operations
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/__init__.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f3c10c3c3690bb834a59df53111873ffac988d8
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/__init__.py
@@ -0,0 +1,9 @@
+from .creation_to_operation import CreationToOperationParser
+from .definition_to_creation import DefinitionToCreationParser
+from .definition_to_creation import DuplicatesHandler, VocabularyDefinitionToCreationParser, \
+                    PropertyTypeDefinitionToCreationParser, SampleTypeDefinitionToCreationParser, ExperimentTypeDefinitionToCreationParser, \
+                    DatasetTypeDefinitionToCreationParser, SpaceDefinitionToCreationParser, ProjectDefinitionToCreationParser, \
+                    ExperimentDefinitionToCreationParser, ScriptDefinitionToCreationParser, SampleDefinitionToCreationParser
+from .excel_to_poi import ExcelToPoiParser
+from .poi_to_definition import PoiToDefinitionParser
+
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/creation_to_operation/__init__.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/creation_to_operation/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..da17c96a5342705fbffea8a907ef83535f0c39a5
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/creation_to_operation/__init__.py
@@ -0,0 +1 @@
+from .creation_to_operation import CreationToOperationParser
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/creation_to_operation/creation_to_operation.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/creation_to_operation/creation_to_operation.py
new file mode 100644
index 0000000000000000000000000000000000000000..3baf8f307b88656295af994c4f37d6f1933f62c6
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/creation_to_operation/creation_to_operation.py
@@ -0,0 +1,39 @@
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create import CreateDataSetTypesOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.create import CreateExperimentTypesOperation, CreateExperimentsOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create import CreatePluginsOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create import CreateProjectsOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.create import CreatePropertyTypesOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create import CreateSampleTypesOperation, CreateSamplesOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.create import CreateSpacesOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.create import CreateVocabulariesOperation
+from ..definition_to_creation import VocabularyDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, SampleTypeDefinitionToCreationParser, \
+                    ExperimentTypeDefinitionToCreationParser, DatasetTypeDefinitionToCreationParser, SpaceDefinitionToCreationParser, \
+                    ProjectDefinitionToCreationParser, ExperimentDefinitionToCreationParser, ScriptDefinitionToCreationParser, SampleDefinitionToCreationParser
+
+
+class CreationToOperationParser(object):
+
+    @staticmethod
+    def parse(creations):
+        creation_operations = []
+        if VocabularyDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreateVocabulariesOperation(creations[VocabularyDefinitionToCreationParser.type]))
+        if PropertyTypeDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreatePropertyTypesOperation(creations[PropertyTypeDefinitionToCreationParser.type]))
+        if SampleTypeDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreateSampleTypesOperation(creations[SampleTypeDefinitionToCreationParser.type]))
+        if ExperimentTypeDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreateExperimentTypesOperation(creations[ExperimentTypeDefinitionToCreationParser.type]))
+        if DatasetTypeDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreateDataSetTypesOperation(creations[DatasetTypeDefinitionToCreationParser.type]))
+        if SpaceDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreateSpacesOperation(creations[SpaceDefinitionToCreationParser.type]))
+        if ProjectDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreateProjectsOperation(creations[ProjectDefinitionToCreationParser.type]))
+        if ExperimentDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreateExperimentsOperation(creations[ExperimentDefinitionToCreationParser.type]))
+        if SampleDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreateSamplesOperation(creations[SampleDefinitionToCreationParser.type]))
+        if ScriptDefinitionToCreationParser.type in creations:
+            creation_operations.append(CreatePluginsOperation(creations[ScriptDefinitionToCreationParser.type]))
+        return creation_operations
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/__init__.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b57ceeb03d8999dc5b928dfa8a374095b465e963
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/__init__.py
@@ -0,0 +1,6 @@
+from .creation_parsers import VocabularyDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, SampleTypeDefinitionToCreationParser, \
+                    ExperimentTypeDefinitionToCreationParser, DatasetTypeDefinitionToCreationParser, SpaceDefinitionToCreationParser, \
+                    ProjectDefinitionToCreationParser, ExperimentDefinitionToCreationParser, ScriptDefinitionToCreationParser, SampleDefinitionToCreationParser
+from .definition_to_creation import DefinitionToCreationParser
+from .duplicates_handler import DuplicatesHandler
+
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
new file mode 100644
index 0000000000000000000000000000000000000000..86e26673774e25b68cb5b047c7b992c77a34a168
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/creation_parsers.py
@@ -0,0 +1,358 @@
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id import CreationId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create import DataSetTypeCreation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id import EntityTypePermId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.create import ExperimentTypeCreation, ExperimentCreation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin import PluginType
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.create import PluginCreation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.id import PluginPermId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.create import ProjectCreation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.property import DataType
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.create import PropertyAssignmentCreation, PropertyTypeCreation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id import PropertyTypePermId
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create import SampleTypeCreation, SampleCreation
+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
+
+
+def get_boolean_from_string(text):
+    return True if text.lower() == u'true' else False
+
+
+class DefinitionToCreationParserFactory(object):
+
+    @staticmethod
+    def getParsers(definition):
+        if definition.type == u'VOCABULARY_TYPE':
+            return [VocabularyDefinitionToCreationParser]
+        elif definition.type == u'SAMPLE_TYPE':
+            return [SampleTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, ScriptDefinitionToCreationParser]
+        elif definition.type == u'EXPERIMENT_TYPE':
+            return [ExperimentTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, ScriptDefinitionToCreationParser]
+        elif definition.type == u'DATASET_TYPE':
+            return [DatasetTypeDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, ScriptDefinitionToCreationParser]
+        elif definition.type == u'SPACE':
+            return [SpaceDefinitionToCreationParser]
+        elif definition.type == u'PROJECT':
+            return [ProjectDefinitionToCreationParser]
+        elif definition.type == u'EXPERIMENT':
+            return [ExperimentDefinitionToCreationParser]
+        elif definition.type == u'SAMPLE':
+            return [SampleDefinitionToCreationParser]
+        elif definition.type == u'PROPERTY_TYPE':
+            return [PropertyTypeDefinitionToCreationParser]
+        else:
+            raise UnsupportedOperationException("Definition of " + str(definition.type) + " is not supported (probably yet).")
+
+
+class PropertyTypeDefinitionToCreationParser(object):
+
+    type = "PropertyTypeCreation"
+
+    @staticmethod
+    def parse(definition):
+        property_creations = []
+        for property 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_creations.append(property_type_creation)
+
+        return property_creations
+
+    @staticmethod
+    def get_type():
+        return PropertyTypeDefinitionToCreationParser.type
+
+
+class VocabularyDefinitionToCreationParser(object):
+
+    type = "VocabularyCreation"
+
+    @staticmethod
+    def parse(definition):
+        code = definition.attributes[u'code']
+        vocabulary_creation = VocabularyCreation()
+        vocabulary_creation.code = code
+        if code.startswith(u'$'):
+            vocabulary_creation.internalNameSpace = True
+        vocabulary_creation.description = definition.attributes[u'description']
+
+        vocabulary_creations_terms = []
+        for property 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_creations_terms.append(vocabulary_creation_term)
+
+        vocabulary_creation.terms = vocabulary_creations_terms
+
+        return vocabulary_creation
+
+    @staticmethod
+    def get_type():
+        return VocabularyDefinitionToCreationParser.type
+
+
+class PropertyAssignmentDefinitionToCreationParser(object):
+
+    type = "PropertyAssignmentCreation"
+
+    @staticmethod
+    def parse(property):
+        code = property[u'code']
+        property_assingment_creation = PropertyAssignmentCreation()
+        is_mandatory = get_boolean_from_string(property[u'mandatory'])
+        property_assingment_creation.mandatory = is_mandatory
+        should_show_in_edit_view = get_boolean_from_string(property[u'show in edit views'])
+        property_assingment_creation.showInEditView = should_show_in_edit_view
+        property_assingment_creation.section = property[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))
+
+        return property_assingment_creation
+
+    @staticmethod
+    def get_type():
+        return PropertyAssignmentDefinitionToCreationParser.type
+
+
+class SampleTypeDefinitionToCreationParser(object):
+
+    type = "SampleTypeCreation"
+
+    @staticmethod
+    def parse(definition):
+        code = definition.attributes[u'code']
+        sample_creation = SampleTypeCreation()
+        sample_creation.code = code
+        should_auto_generate_codes = get_boolean_from_string(definition.attributes[u'auto generate codes'])
+        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))
+
+        property_assingment_creations = []
+        for property in definition.properties:
+            property_assingment_creation = PropertyAssignmentDefinitionToCreationParser.parse(property)
+            property_assingment_creations.append(property_assingment_creation)
+
+        sample_creation.propertyAssignments = property_assingment_creations
+        return sample_creation
+
+    @staticmethod
+    def get_type():
+        return SampleTypeDefinitionToCreationParser.type
+
+
+class ExperimentTypeDefinitionToCreationParser(object):
+
+    type = "ExperimentTypeCreation"
+
+    @staticmethod
+    def parse(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_assingment_creations.append(property_assingment_creation)
+
+        experiment_type_creation.propertyAssignments = property_assingment_creations
+        return experiment_type_creation
+
+    @staticmethod
+    def get_type():
+        return ExperimentTypeDefinitionToCreationParser.type
+
+
+class DatasetTypeDefinitionToCreationParser(object):
+
+    type = "DatasetTypeCreation"
+
+    @staticmethod
+    def parse(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_assingment_creations.append(property_assingment_creation)
+
+        dataset_type_creation.propertyAssignments = property_assingment_creations
+        return dataset_type_creation
+
+    @staticmethod
+    def get_type():
+        return DatasetTypeDefinitionToCreationParser.type
+
+
+class SpaceDefinitionToCreationParser(object):
+
+    type = "SpaceCreation"
+
+    @staticmethod
+    def parse(definition):
+        space_creation = SpaceCreation()
+        space_creation.code = definition.attributes[u'code']
+        space_creation.description = definition.attributes[u'description']
+        creation_id = definition.attributes[u'code']
+        space_creation.creationId = CreationId(creation_id)
+        return space_creation
+
+    @staticmethod
+    def get_type():
+        return SpaceDefinitionToCreationParser.type
+
+
+class ProjectDefinitionToCreationParser(object):
+
+    type = "ProjectCreation"
+
+    @staticmethod
+    def parse(definition):
+        project_creation = ProjectCreation()
+        project_creation.code = definition.attributes[u'code']
+        project_creation.description = definition.attributes[u'description']
+        project_creation.spaceId = CreationId(definition.attributes[u'space'])
+        creation_id = definition.attributes[u'code']
+        project_creation.creationId = CreationId(creation_id)
+        return project_creation
+
+    @staticmethod
+    def get_type():
+        return ProjectDefinitionToCreationParser.type
+
+
+class ExperimentDefinitionToCreationParser(object):
+
+    type = "ExperimentCreation"
+
+    @staticmethod
+    def parse(definition):
+        experiments = []
+        mandatory_attributes = [u'code', u'project']
+        for experiment_properties in definition.properties:
+            experiment_creation = ExperimentCreation()
+            experiment_creation.typeId = EntityTypePermId(definition.attributes[u'experiment type'])
+            experiment_creation.code = experiment_properties[u'code']
+            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)
+            experiments.append(experiment_creation)
+        return experiments
+
+    @staticmethod
+    def get_type():
+        return ExperimentDefinitionToCreationParser.type
+
+
+class SampleDefinitionToCreationParser(object):
+
+    type = "SampleCreation"
+
+    @staticmethod
+    def parse(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:
+            sample_creation = SampleCreation()
+            sample_creation.typeId = EntityTypePermId(definition.attributes[u'sample type'])
+            if u'code' in sample_properties and sample_properties[u'code'] is not None:
+                sample_creation.code = sample_properties[u'code']
+                creation_id = sample_properties[u'code']
+                sample_creation.creationId = CreationId(creation_id)
+            if u'$' in sample_properties and sample_properties[u'$'] is not None:
+                # may overwrite creationId from code, which is intended
+                sample_creation.creationId = CreationId(sample_properties[u'$'])
+            if u'auto generate code' in sample_properties and sample_properties[u'auto generate code'] is not None and sample_properties[u'auto generate code'] != '':
+                sample_creation.autoGeneratedCode = get_boolean_from_string(sample_properties[u'auto generate code'])
+            if u'space' in sample_properties and sample_properties[u'space'] is not None:
+                sample_creation.spaceId = CreationId(sample_properties[u'space'])
+            if u'project' in sample_properties and sample_properties[u'project'] is not None:
+                sample_creation.projectId = CreationId(sample_properties[u'project'])
+            if u'experiment' in sample_properties and sample_properties[u'experiment'] is not None:
+                sample_creation.experimentId = CreationId(sample_properties[u'experiment'])
+            if u'parents' in sample_properties and sample_properties[u'parents'] is not None:
+                parent_creationids = []
+                parents = sample_properties[u'parents'].split('\n')
+                for parent in parents:
+                    parent_creationids.append(CreationId(parent))
+                sample_creation.parentIds = parent_creationids
+            if u'children' in sample_properties and sample_properties[u'children'] is not None:
+                child_creationids = []
+                children = sample_properties[u'children'].split('\n')
+                for child in children:
+                    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)
+            samples.append(sample_creation)
+        return samples
+
+    @staticmethod
+    def get_type():
+        return SampleDefinitionToCreationParser.type
+
+
+class ScriptDefinitionToCreationParser(object):
+
+    type = "ScriptCreation"
+
+    @staticmethod
+    def parse(definition):
+        scripts = []
+        if u'validation script' in definition.attributes:
+            validation_script_path = definition.attributes[u'validation script']
+            if validation_script_path is not None:
+                code = definition.attributes[u'code']
+                validation_script_creation = PluginCreation()
+                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.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']
+                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.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():
+        return ScriptDefinitionToCreationParser.type
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/definition_to_creation.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/definition_to_creation.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd9e0857114abb6ad2eb9b94489465ea7e18932e
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/definition_to_creation.py
@@ -0,0 +1,23 @@
+from .creation_parsers import DefinitionToCreationParserFactory
+
+
+class DefinitionToCreationParser(object):
+
+    @staticmethod
+    def parse(definitions):
+        creations = {}
+
+        for definition in definitions:
+            # One definition may contain more than one creation
+            parsers = DefinitionToCreationParserFactory.getParsers(definition)
+            for parser in parsers:
+                creation = parser.parse(definition)
+                if creation is None or creation == []:
+                    continue
+                creation_type = parser.get_type()
+                if creation_type not in creations:
+                    creations[creation_type] = []
+                creations[creation_type].extend(creation if type(creation) == list else [creation])
+
+        return creations
+
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/duplicates_handler.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/duplicates_handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ec8c34b7d7193fbd193c81e07dd43ec49fa6d8b
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/definition_to_creation/duplicates_handler.py
@@ -0,0 +1,16 @@
+from .creation_parsers import ScriptDefinitionToCreationParser, SampleDefinitionToCreationParser
+
+
+class DuplicatesHandler(object):
+
+    @staticmethod
+    def get_distinct_creations(creations):
+        distinct_creations = {}
+        for creation_type, creations in creations.items():
+            if creation_type == ScriptDefinitionToCreationParser.type:
+                distinct_creations[creation_type] = dict((creation.name, creation) for creation in creations).values()
+            elif creation_type not in [SampleDefinitionToCreationParser.type]:
+                distinct_creations[creation_type] = dict((creation.code, creation) for creation in creations).values()
+            else:
+                distinct_creations[creation_type] = list(creations)
+        return distinct_creations
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/excel_to_poi/__init__.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/excel_to_poi/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a77a879d5aca68360c1e66b9f8637e7986077929
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/excel_to_poi/__init__.py
@@ -0,0 +1 @@
+from .excel_to_poi import ExcelToPoiParser
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/excel_to_poi/excel_to_poi.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/excel_to_poi/excel_to_poi.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca1d8032bc8d07d366d2cd0492a4d512cd43c1cc
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/excel_to_poi/excel_to_poi.py
@@ -0,0 +1,80 @@
+from org.apache.commons.lang import StringUtils
+from org.apache.poi.ss.usermodel import CellType
+from org.apache.poi.ss.usermodel import WorkbookFactory
+from org.apache.poi.ss.util import NumberToTextConverter
+from org.python.core.io import FileIO
+
+
+class ExcelToPoiParser(object):
+
+    @staticmethod
+    def parse(excel_file_path):
+        workbook = WorkbookFactory.create(FileIO(excel_file_path, 'r').asInputStream())
+
+        data_colection = []
+        definitions = []
+        for sheet in workbook.sheetIterator():
+            i = 0
+            sheet_done = False
+            while not sheet_done:
+                definition_rows = []
+                while not ExcelToPoiParser.is_row_empty(sheet.getRow(i)):
+                    row = sheet.getRow(i)
+                    definition_rows.append(row)
+                    i += 1
+                i += 1  # skip empty row
+
+                if not definition_rows:
+                    sheet_done = True
+                else:
+                    definitions.append(definition_rows)
+
+        workbook.close()
+
+        definitions_stripped = []
+        for definition in definitions:
+            definition_stripped = []
+            for row in definition:
+                row_stripped = {}
+                for cell in row.cellIterator():
+                    cell_value = ExcelToPoiParser.extract_string_value_from(cell)
+                    cell_value = cell_value if cell_value != '' else None
+                    cell_col = cell.getColumnIndex()
+                    row_stripped[cell_col] = cell_value
+                definition_stripped.append(row_stripped)
+            definitions_stripped.append(definition_stripped)
+
+        return definitions_stripped
+
+    @staticmethod
+    def extract_string_value_from(cell):
+        cell_type = cell.getCellTypeEnum()
+        if cell_type == CellType.BLANK:
+            return "";
+        elif cell_type == CellType.BOOLEAN:
+            return str(cell.getBooleanCellValue());
+        elif cell_type == CellType.NUMERIC:
+            return NumberToTextConverter.toText(cell.getNumericCellValue());
+        elif cell_type == CellType.STRING:
+            return cell.getStringCellValue();
+        elif cell_type == CellType.FORMULA:
+            raise SyntaxError("Excel formulas are not supported but one was found in cell " + extractCellPosition(cell));
+        elif cell_type == CellType.ERROR:
+            raise SyntaxError("There is an error in cell " + extractCellPosition(cell));
+        else:
+            raise SyntaxError("Unknown data type of cell " + extractCellPosition(cell));
+
+    @staticmethod
+    def is_row_empty(row):
+        if row is None:
+            return True
+        if row.getLastCellNum <= 0:
+            return True
+
+        return all(ExcelToPoiParser.is_cell_empty(cell) for cell in row.cellIterator())
+
+    @staticmethod
+    def is_cell_empty(cell):
+        if cell is not None and cell.getCellType() != CellType.BLANK and StringUtils.isNotBlank(cell.toString()):
+            return False;
+        return True
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/__init__.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..1260cc7bc899990fb6e8191ff457d841aa64b3d4
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/__init__.py
@@ -0,0 +1,2 @@
+from .poi_to_definition import PoiToDefinitionParser
+
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/definition.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/definition.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9dce04066f511d2129170478af1e19d4df6cb8c
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/definition.py
@@ -0,0 +1,21 @@
+class Definition(object):
+    '''
+        Used to hold values for object(Vocabulary, SampleType etc.) creation.
+    '''
+
+    def __init__(self):
+        self.type = None
+        self.attributes = {}
+        self.properties = []
+
+    def __str__(self):
+        return "\n".join([
+            "Definition type:",
+            str(self.type),
+            "Attributes:",
+            str(self.attributes),
+            "Properties:",
+            str(self.properties),
+            "==================" * 3])
+
+    __repr__ = __str__
\ No newline at end of file
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
new file mode 100644
index 0000000000000000000000000000000000000000..2990cc1345df82b4fc01806a0bc4a67c197149d8
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/definition_parsers.py
@@ -0,0 +1,98 @@
+from .definition import Definition
+from .poi_cleaner import PoiCleaner
+
+
+class DefinitionParserFactory(object):
+
+        @staticmethod
+        def get_parser(definition_type):
+            if definition_type in ['VOCABULARY_TYPE', 'SAMPLE_TYPE', 'EXPERIMENT_TYPE', 'DATASET_TYPE', 'SPACE', 'PROJECT', 'EXPERIMENT', 'SAMPLE']:
+                return GeneralDefinitionParser
+            elif definition_type == 'PROPERTY_TYPE':
+                return PropertyTypeDefinitionParser
+            else:
+                raise UnsupportedOperationException("Definition of " + str(definition_type) + " is not supported (probably yet).")
+
+
+class PropertyTypeDefinitionParser(object):
+
+    @staticmethod
+    def parse(poi_definition):
+        DEFINITION_TYPE_ROW = 0
+        DEFINITION_TYPE_CELL = 0
+        PROPERTIES_HEADER_ROW = 1
+        PROPERTIES_VALUES_ROW_START = 2
+
+        row_numbers = {
+            'DEFINITION_TYPE_ROW' : DEFINITION_TYPE_ROW,
+            'DEFINITION_TYPE_CELL' : DEFINITION_TYPE_CELL,
+            'ATTRIBUTES_HEADER_ROW' : None,
+            'ATTRIBUTES_VALUES_ROW' : None,
+            'PROPERTIES_HEADER_ROW' : PROPERTIES_HEADER_ROW,
+            'PROPERTIES_VALUES_ROW_START' : PROPERTIES_VALUES_ROW_START
+        }
+
+        poi_definition = PoiCleaner.clean_data(poi_definition, row_numbers)
+
+        definition = Definition()
+        definition.type = poi_definition[DEFINITION_TYPE_ROW][DEFINITION_TYPE_CELL]
+        if GeneralDefinitionParser.hasProperties(poi_definition):
+            properties_headers = poi_definition[PROPERTIES_HEADER_ROW]
+
+            for property_definitions in poi_definition[PROPERTIES_VALUES_ROW_START:]:
+                property = {}
+                for col, header in properties_headers.items():
+                    property[header] = property_definitions[col]
+                definition.properties.append(property)
+
+        return definition
+
+    @staticmethod
+    def hasProperties(poi_definition):
+        PROPERTIES_HEADER_ROW = 1
+        return len(poi_definition) > PROPERTIES_HEADER_ROW
+
+
+class GeneralDefinitionParser(object):
+
+    @staticmethod
+    def parse(poi_definition):
+        DEFINITION_TYPE_ROW = 0
+        DEFINITION_TYPE_CELL = 0
+        ATTRIBUTES_HEADER_ROW = 1
+        ATTRIBUTES_VALUES_ROW = 2
+        PROPERTIES_HEADER_ROW = 3
+        PROPERTIES_VALUES_ROW_START = 4
+
+        row_numbers = {
+            'DEFINITION_TYPE_ROW' : DEFINITION_TYPE_ROW,
+            'DEFINITION_TYPE_CELL' : DEFINITION_TYPE_CELL,
+            'ATTRIBUTES_HEADER_ROW' : ATTRIBUTES_HEADER_ROW,
+            'ATTRIBUTES_VALUES_ROW' : ATTRIBUTES_VALUES_ROW,
+            'PROPERTIES_HEADER_ROW' : PROPERTIES_HEADER_ROW,
+            'PROPERTIES_VALUES_ROW_START' : PROPERTIES_VALUES_ROW_START
+        }
+
+        poi_definition = PoiCleaner.clean_data(poi_definition, row_numbers)
+
+        definition = Definition()
+        definition.type = poi_definition[DEFINITION_TYPE_ROW][DEFINITION_TYPE_CELL]
+        for col, header in poi_definition[ATTRIBUTES_HEADER_ROW].items():
+            cell_value = poi_definition[ATTRIBUTES_VALUES_ROW][col]
+            definition.attributes[header] = cell_value
+
+        if GeneralDefinitionParser.hasProperties(poi_definition):
+            properties_headers = poi_definition[PROPERTIES_HEADER_ROW]
+
+            for property_definitions in poi_definition[PROPERTIES_VALUES_ROW_START:]:
+                property = {}
+                for col, header in properties_headers.items():
+                    property[header] = property_definitions[col]
+                definition.properties.append(property)
+
+        return definition
+
+    @staticmethod
+    def hasProperties(poi_definition):
+        PROPERTIES_HEADER_ROW = 3
+        return len(poi_definition) > PROPERTIES_HEADER_ROW
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/poi_cleaner.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/poi_cleaner.py
new file mode 100644
index 0000000000000000000000000000000000000000..64845fd0884b45d05711f520dcb9517c8429c4ef
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/poi_cleaner.py
@@ -0,0 +1,86 @@
+from copy import deepcopy
+
+
+class PoiCleaner(object):
+
+    @staticmethod
+    def hasProperties(poi_definition):
+        PROPERTIES_HEADER_ROW = 3
+        return len(poi_definition) > PROPERTIES_HEADER_ROW
+
+    @staticmethod
+    def clean_data(xls_definition, row_numbers):
+        DEFINITION_TYPE_ROW = row_numbers['DEFINITION_TYPE_ROW']
+        DEFINITION_TYPE_CELL = row_numbers['DEFINITION_TYPE_CELL']
+        ATTRIBUTES_HEADER_ROW = row_numbers['ATTRIBUTES_HEADER_ROW']
+        ATTRIBUTES_VALUES_ROW = row_numbers['ATTRIBUTES_VALUES_ROW']
+        PROPERTIES_HEADER_ROW = row_numbers['PROPERTIES_HEADER_ROW']
+        PROPERTIES_VALUES_ROW_START = row_numbers['PROPERTIES_VALUES_ROW_START']
+
+        definition = deepcopy(xls_definition)
+        '''
+            First row can only have definition type, rest is trimmed. Rules for cleaning are the following:
+            1. Header rows cannot have empty cells
+            2. Values that do not have corresponding headers are removed.
+            3. All attributes and properties should have corresponding value in values row when header exists.
+                   If there's no corresponding value, None is inserted.
+            4. Headers to lowercase
+        '''
+        definition[DEFINITION_TYPE_ROW] = {0:definition[DEFINITION_TYPE_ROW][DEFINITION_TYPE_CELL]}
+
+        if ATTRIBUTES_HEADER_ROW is not None:
+            PoiCleaner.delete_empty_cells_from(definition, ATTRIBUTES_HEADER_ROW)
+            if ATTRIBUTES_VALUES_ROW is not None:
+                PoiCleaner.delete_cells_if_no_header(definition, ATTRIBUTES_HEADER_ROW, ATTRIBUTES_VALUES_ROW)
+                PoiCleaner.create_cells_if_no_value_but_header_exists(definition, ATTRIBUTES_HEADER_ROW, ATTRIBUTES_VALUES_ROW)
+            definition[ATTRIBUTES_HEADER_ROW] = PoiCleaner.dict_values_to_lowercase(definition[ATTRIBUTES_HEADER_ROW])
+
+        if PoiCleaner.hasProperties(definition) and PROPERTIES_HEADER_ROW is not None:
+            PoiCleaner.delete_empty_cells_from(definition, PROPERTIES_HEADER_ROW)
+            if PROPERTIES_VALUES_ROW_START is not None:
+                for property_value_row_num in range(PROPERTIES_VALUES_ROW_START, len(definition)):
+                    PoiCleaner.delete_cells_if_no_header(definition, PROPERTIES_HEADER_ROW, property_value_row_num)
+                    PoiCleaner.create_cells_if_no_value_but_header_exists(definition, PROPERTIES_HEADER_ROW, property_value_row_num)
+            definition[PROPERTIES_HEADER_ROW] = PoiCleaner.dict_values_to_lowercase(definition[PROPERTIES_HEADER_ROW])
+
+        return definition
+
+    @staticmethod
+    def delete_empty_cells_from(definition, row_number):
+        header_cell_cols_to_pop = []
+        for cell_col, cell in definition[row_number].items():
+            if cell is None or cell == '':
+                header_cell_cols_to_pop.append(cell_col)
+
+        for cell_col in header_cell_cols_to_pop:
+            del definition[row_number][cell_col]
+
+        return definition
+
+    @staticmethod
+    def delete_cells_if_no_header(definition, header_row_number, value_row_number):
+        values_cell_cols_to_pop = []
+        for cell_col in definition[value_row_number]:
+            if cell_col not in definition[header_row_number]:
+                values_cell_cols_to_pop.append(cell_col)
+
+        for cell_col in values_cell_cols_to_pop:
+            del definition[value_row_number][cell_col]
+
+        return definition
+
+    @staticmethod
+    def create_cells_if_no_value_but_header_exists(definition, header_row_number, value_row_number):
+        values_cell_cols_to_insert = []
+        for cell_col in definition[header_row_number]:
+            if cell_col not in definition[value_row_number]:
+                values_cell_cols_to_insert.append(cell_col)
+
+        for cell_col in values_cell_cols_to_insert:
+            definition[value_row_number][cell_col] = None
+
+        return definition
+
+    @staticmethod
+    def dict_values_to_lowercase(row):
+        return dict((k, v.lower()) for (k, v) in row.items())
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/poi_to_definition.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/poi_to_definition.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f921f64b412162b1bb793d0d90e23a9b35525f7
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/parsers/poi_to_definition/poi_to_definition.py
@@ -0,0 +1,36 @@
+from .definition_parsers import DefinitionParserFactory
+
+
+class PoiToDefinitionParser(object):
+
+    @staticmethod
+    def parse(poi_definitions):
+        '''
+            Expecting definitions to be in such layout:
+            [
+                `DEFINITIONS_LIST`
+                [
+                    `DEFINITION`
+                    {
+                        `ROWS`
+                        (column, row) : string_value,
+                        .
+                        .
+                        .
+                    }
+                ],
+                .
+                .
+                .
+            ]
+        '''
+        definitons = []
+        for poi_definition in poi_definitions:
+            FIRST_ROW = 0
+            FIRST_CELL = 0
+            definition_type = poi_definition[FIRST_ROW][FIRST_CELL]
+            definition_parser = DefinitionParserFactory.get_parser(definition_type)
+            definition = definition_parser.parse(poi_definition)
+            definitons.append(definition)
+
+        return definitons
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/search_engines/__init__.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/search_engines/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0dc844ed16ac3a1ecf31f210f5e9430fbb72da37
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/search_engines/__init__.py
@@ -0,0 +1 @@
+from .search_engine import SearchEngine
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/search_engines/search_engine.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/search_engines/search_engine.py
new file mode 100644
index 0000000000000000000000000000000000000000..42634e1ef274b9b68f57a1c759bb91944a021527
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/search_engines/search_engine.py
@@ -0,0 +1,156 @@
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search import SearchOperator
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions import DataSetTypeFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search import DataSetTypeSearchCriteria, SearchDataSetTypesOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions import ExperimentTypeFetchOptions, ExperimentFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.search import ExperimentTypeSearchCriteria, \
+    SearchExperimentTypesOperation, ExperimentSearchCriteria, SearchExperimentsOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.operation import SynchronousOperationExecutionOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions import PluginFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.search import PluginSearchCriteria, SearchPluginsOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.fetchoptions import ProjectFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.search import ProjectSearchCriteria, SearchProjectsOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions import PropertyTypeFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search import PropertyTypeSearchCriteria, SearchPropertyTypesOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions import SampleTypeFetchOptions, SampleFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search import SampleTypeSearchCriteria, \
+    SearchSampleTypesOperation, SampleSearchCriteria, SearchSamplesOperation, \
+    SampleSearchRelation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.fetchoptions import SpaceFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.search import SpaceSearchCriteria, SearchSpacesOperation
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions import VocabularyFetchOptions
+from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.search import VocabularySearchCriteria, SearchVocabulariesOperation
+from parsers import VocabularyDefinitionToCreationParser, PropertyTypeDefinitionToCreationParser, SampleTypeDefinitionToCreationParser, \
+                    ExperimentTypeDefinitionToCreationParser, DatasetTypeDefinitionToCreationParser, SpaceDefinitionToCreationParser, \
+                    ProjectDefinitionToCreationParser, ExperimentDefinitionToCreationParser, ScriptDefinitionToCreationParser, SampleDefinitionToCreationParser
+
+
+class SearchEngine():
+
+    def __init__(self, api, sesstion_token):
+            self.api = api
+            self.session_token = sesstion_token
+
+    def find_all_existing_elements(self, creations):
+        search_strategy = [
+            {
+            'creations_type': VocabularyDefinitionToCreationParser.type,
+            'search_criteria_object' : VocabularySearchCriteria,
+            'search_operation':SearchVocabulariesOperation,
+            'fetch_options':VocabularyFetchOptions
+            },
+            {
+            'creations_type': PropertyTypeDefinitionToCreationParser.type,
+            'search_criteria_object' : PropertyTypeSearchCriteria,
+            'search_operation': SearchPropertyTypesOperation,
+            'fetch_options': PropertyTypeFetchOptions
+            },
+            {
+            'creations_type': SampleTypeDefinitionToCreationParser.type,
+            'search_criteria_object' : SampleTypeSearchCriteria,
+            'search_operation': SearchSampleTypesOperation,
+            'fetch_options': SampleTypeFetchOptions
+            },
+            {
+            'creations_type': ExperimentTypeDefinitionToCreationParser.type,
+            'search_criteria_object' : ExperimentTypeSearchCriteria,
+            'search_operation': SearchExperimentTypesOperation,
+            'fetch_options': ExperimentTypeFetchOptions
+            },
+            {
+            'creations_type': DatasetTypeDefinitionToCreationParser.type,
+            'search_criteria_object' : DataSetTypeSearchCriteria,
+            'search_operation': SearchDataSetTypesOperation,
+            'fetch_options': DataSetTypeFetchOptions
+            },
+            {
+            'creations_type': SpaceDefinitionToCreationParser.type,
+            'search_criteria_object' : SpaceSearchCriteria,
+            'search_operation': SearchSpacesOperation,
+            'fetch_options': SpaceFetchOptions
+            },
+            {
+            'creations_type': ProjectDefinitionToCreationParser.type,
+            'search_criteria_object' : ProjectSearchCriteria,
+            'search_operation': SearchProjectsOperation,
+            'fetch_options': ProjectFetchOptions
+            },
+            {
+            'creations_type': ExperimentDefinitionToCreationParser.type,
+            'search_criteria_object' : ExperimentSearchCriteria,
+            'search_operation': SearchExperimentsOperation,
+            'fetch_options': ExperimentFetchOptions
+            },
+            {
+            'creations_type': SampleDefinitionToCreationParser.type,
+            'search_criteria_object' : SampleSearchCriteria,
+            'search_operation': SearchSamplesOperation,
+            'fetch_options': SampleFetchOptions
+            },
+            {
+            'creations_type': ScriptDefinitionToCreationParser.type,
+            'search_criteria_object' : PluginSearchCriteria,
+            'search_operation': SearchPluginsOperation,
+            'fetch_options': PluginFetchOptions
+            }
+        ]
+
+        existing_elements = {}
+        for strategy in search_strategy:
+            creations_type = strategy['creations_type']
+            if creations_type in creations:
+                existing_specific_elements = self._get_existing_elements(creations=creations, **strategy)
+                if existing_specific_elements is not None:
+                    existing_elements[creations_type] = existing_specific_elements
+        return existing_elements
+
+    def _get_existing_elements(self, creations, creations_type, search_criteria_object, search_operation, fetch_options):
+        search_criteria = self._get_search_criteria(creations_type, creations[creations_type], search_criteria_object)
+        if search_criteria is []:
+            return None
+        result = self._execute_search_operation(search_operation(search_criteria, fetch_options()))
+        return result.getObjects()
+
+    def _get_search_criteria(self, creations_type, specific_creations, search_criteria_class):
+        search_criteria = search_criteria_class()
+
+        if creations_type == SampleDefinitionToCreationParser.type:
+            search_criterias = []
+            for creation in specific_creations:
+                search_criteria.withOrOperator()
+                if creation.code is not None:
+                    search_criteria.withCode().thatEquals(creation.code)
+                    if creation.experimentId is not None:
+                        search_criteria.withExperiment().withCode().thatEquals(creation.experimentId.creationId)
+                    else:
+                        search_criteria.withoutExperiment()
+
+                    if creation.projectId is not None:
+                        search_criteria.withProject().withCode().thatEquals(creation.projectId.creationId)
+                    else:
+                        search_criteria.withoutProject()
+
+                    if creation.spaceId is not None:
+                        search_criteria.withSpace().withCode().thatEquals(creation.spaceId.creationId)
+                    else:
+                        search_criteria.withoutSpace()
+
+                    search_criterias.append(search_criteria)
+            return search_criteria
+
+        if 'withCodes' in dir(search_criteria):
+            search_criteria.withCodes().thatIn([creation.code for creation in specific_creations])
+        elif 'withName' in dir(search_criteria):
+            for creation in specific_creations:
+                search_criteria.withName().thatEquals(creation.name)
+            search_criteria.withOrOperator()
+        else:
+            for creation in specific_creations:
+                search_criteria.withCode().thatEquals(creation.code)
+            search_criteria.withOrOperator()
+        return search_criteria
+
+    def _execute_search_operation(self, operation):
+        operations = []
+        operations.extend(operation if type(operation) == list else [operation])
+        return self.api.executeOperations(self.session_token, operations, SynchronousOperationExecutionOptions()).getResults().get(0).getSearchResult()
+
diff --git a/screening/build.gradle b/screening/build.gradle
index 31394fd8925e66a4904614d8f1b1c6d718652394..e9fe8c77801eeec99367a4b167d046026dfd7f6c 100644
--- a/screening/build.gradle
+++ b/screening/build.gradle
@@ -14,7 +14,7 @@ dependencies {
             project(':datastore_server'),
             'openhms:jackcess:+',
             'reveregroup:gwt-image-loader:+',
-            'bioformats:bioformats:+'
+            'bioformats:bioformats:5.9.2'
 
     testCompile project(path: ':datastore_server', configuration: 'tests')
 }