diff --git a/sanofi/.classpath b/sanofi/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..dc1aac1a05c9f18d34503f26441c2ecaa11697f0
--- /dev/null
+++ b/sanofi/.classpath
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="source/java"/>
+	<classpathentry kind="src" path="sourceTest/java"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/screening"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/common"/>
+	<classpathentry kind="lib" path="/libraries/testng/testng-jdk15.jar" sourcepath="/libraries/testng/src.zip"/>
+	<classpathentry kind="lib" path="/libraries/cisd-base/cisd-base-test.jar" sourcepath="/libraries/cisd-base/cisd-base-src.zip"/>
+	<classpathentry kind="lib" path="/libraries/cisd-base/cisd-base.jar" sourcepath="/libraries/cisd-base/cisd-base-src.zip"/>
+	<classpathentry kind="lib" path="/libraries/jmock/hamcrest/hamcrest-core.jar"/>
+	<classpathentry kind="lib" path="/libraries/jmock/hamcrest/hamcrest-library.jar"/>
+	<classpathentry kind="lib" path="/libraries/jmock/objenesis/objenesis-1.0.jar"/>
+	<classpathentry kind="lib" path="/libraries/jmock/jmock.jar"/>
+	<classpathentry kind="lib" path="/libraries/log4j/log4j.jar" sourcepath="/libraries/log4j/src.zip"/>
+	<classpathentry kind="lib" path="/libraries/mail/mail.jar"/>
+	<classpathentry kind="lib" path="/libraries/activation/activation.jar"/>
+	<classpathentry kind="lib" path="/libraries/jaxb/jaxb-api.jar" sourcepath="/libraries/jaxb/jaxb-api-src.zip"/>
+	<classpathentry kind="lib" path="/libraries/jaxb/jsr173_1.0_api.jar"/>
+	<classpathentry kind="lib" path="/libraries/gwt2.0/gwt-isserializable.jar"/>
+	<classpathentry kind="lib" path="/libraries/spring/spring.jar" sourcepath="/libraries/spring/src.jar"/>
+	<classpathentry kind="lib" path="/libraries/cisd-args4j/cisd-args4j.jar" sourcepath="/libraries/cisd-args4j/cisd-args4j-src.zip"/>
+	<classpathentry kind="lib" path="/libraries/poi/poi-3.7-20101029.jar"/>
+	<classpathentry kind="lib" path="/libraries/jython/standalone/jython.jar" sourcepath="/Users/gpawel/Downloads/jython_installer-2.2.1.jar"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/datastore_server"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/openbis"/>
+	<classpathentry kind="lib" path="/libraries/eodsql/eodsql.jar" sourcepath="/libraries/eodsql/eodsql_src.zip"/>
+	<classpathentry kind="output" path="targets/classes"/>
+</classpath>
diff --git a/sanofi/.project b/sanofi/.project
new file mode 100644
index 0000000000000000000000000000000000000000..36b7fe7a11e5e37c6e446a8a96ae69f0bf8d5d6d
--- /dev/null
+++ b/sanofi/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>sanofi</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.python.pydev.PyDevBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+                <buildCommand>
+                        <name>org.eclipse.jdt.core.javabuilder</name>
+                        <arguments>
+                        </arguments>
+                </buildCommand>
+	</buildSpec>
+	<natures>
+                <nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.python.pydev.pythonNature</nature>
+	</natures>
+</projectDescription>
diff --git a/sanofi/.pydevproject b/sanofi/.pydevproject
new file mode 100644
index 0000000000000000000000000000000000000000..a9cca037b33037ebc3232e6b63f3f00283463a4e
--- /dev/null
+++ b/sanofi/.pydevproject
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+</pydev_project>
diff --git a/sanofi/build/antrun.sh b/sanofi/build/antrun.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5ff1a62b259d6011d228440f2fb89ec0dbecf747
--- /dev/null
+++ b/sanofi/build/antrun.sh
@@ -0,0 +1,6 @@
+#! /bin/bash
+
+ME="$0"
+MYDIR=${ME%/*}
+cd $MYDIR
+ant -lib ../../build_resources/lib/ecj.jar "$@"
diff --git a/sanofi/build/build.xml b/sanofi/build/build.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5dd99be6cd1a5a153129d1bb5209d867f2e17df5
--- /dev/null
+++ b/sanofi/build/build.xml
@@ -0,0 +1,49 @@
+<project name="sanofi" default="ci" basedir="..">
+	<import file="../../datastore_server/build/build.xml" />
+	<project-classpath name="ecp" classes="${classes}" />
+
+	<property name="original.dist" value="dist" />
+	<property name="mainfolder" value="sanofi" />
+
+	<target name="compile" depends="build-common.compile, clean" />
+
+	<target name="run-tests">
+		<antcall target="build-common.run-tests">
+			<param name="test.suite" value="tests.xml" />
+		</antcall>
+	</target>
+
+	<!--
+	  No jar files produced by the module yet.
+	<target name="jar" depends="compile">
+		<mkdir dir="${dist}" />
+		<build-info revision="revision.number" version="version.number" clean="clean.flag" />
+		<echo file="${build.info.file}">${version.number}:${revision.number}:${clean.flag}</echo>
+		<jar destfile="${plugin-jar.file}">
+			<fileset dir="${classes}">
+				<include name="ch/**/*.class" />
+				<include name="${build.info.filename}" />
+			</fileset>
+			<fileset dir="source">
+				<include name="**/*.sql" />
+			</fileset>	
+			<manifest>
+				<attribute name="Version" value="${version.number}" />
+				<attribute name="Build-Number"
+				           value="${version.number} (r${revision.number},${clean.flag})" />
+			</manifest>
+		</jar>
+	</target>
+	-->
+	
+	<!--
+    // Task for creating distributions
+	<target name="dist" depends="jar" />
+   -->
+
+	<!--
+        // Task for continuous integration server.
+        -->
+	<target name="ci" depends="build-common.ci, check-dependencies" />
+
+</project>
\ No newline at end of file
diff --git a/sanofi/dist/etc/sanofi-dropbox/sanofi-dropbox.py b/sanofi/dist/etc/sanofi-dropbox/sanofi-dropbox.py
new file mode 100644
index 0000000000000000000000000000000000000000..6399023c81462c4bc39f087ba4d084f19f19d87d
--- /dev/null
+++ b/sanofi/dist/etc/sanofi-dropbox/sanofi-dropbox.py
@@ -0,0 +1,215 @@
+import ch.systemsx.cisd.etlserver.registrator.api.v1.MaterialIdentifierCollection as MaterialIdentifierCollection
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria as SearchCriteria 
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause as MatchClause 
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute as MatchClauseAttribute 
+
+PLATE_TYPE = "PLATE"
+
+DATA_SET_TYPE = "HCS_IMAGE_RAW"
+DATA_SET_BATCH_PROPNAME = "ACQUISITION_BATCH"
+
+def findPlateByCode(code):
+    """
+       Finds a plate (openBIS sample) matching a specified bar code.
+    """
+    criteria = SearchCriteria()
+    criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.TYPE, PLATE_TYPE))
+    criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, code))
+    
+    searchService = transaction.getSearchService()
+    platesFound = list(searchService.searchForSamples(criteria))
+
+    if len(platesFound) > 0:
+        return platesFound[0]
+    else:
+        return None      
+
+def parseIncomingDirname(dirName):
+    """
+       Parses the name of an incoming dataset folder from the format
+       'AcquisitionBatch_BarCode_Timestamp' to a tuple (acquisitionBatch, barCode)
+    """
+    tokens = dirName.split("_")
+    assert len(tokens) >= 2, "Data set directory name does not match the pattern 'AcquisitionBatch_BarCode_Timestamp': " + dirName
+    acquisitionBatch = tokens[0]
+    barCode = tokens[1].split('.')[0]
+    return (acquisitionBatch, barCode)
+
+def removeDuplicates(list):
+    dict = {}
+    for item in list:
+        dict[item] = item
+    return dict.keys()
+    
+class SanofiMaterial:
+    """
+       A data structure class holding compound materials as they exist in the Abase (Sanofi) database.
+    """
+    def __init__(self, wellCode, materialCode, sanofiId, sanofiBatchId):
+        self.wellCode = wellCode
+        self.materialCode = materialCode
+        self.sanofiId = sanofiId
+        self.sanofiBatchId = sanofiBatchId
+            
+class PlateInitializer:
+    ABASE_DATA_SOURCE = "abase-datasource"
+    ABASE_QUERY = "TODO: this a query provided by Matt"
+    
+    LIBRARY_TEMPLATE_PROPNAME = "LIBRARY_TEMPLATE"
+    
+    WELL_TYPE = "COMPOUND_WELL"
+    WELL_CONCENTRATION_PROPNAME = "CONCENTRATION"
+    WELL_MATERIAL_PROPNAME = "COMPOUND_BATCH"
+    
+    MATERIAL_TYPE = "COMPOUND_BATCH"
+    MATERIAL_ID_PROPNAME = "COMPOUND_ID"
+    MATERIAL_BATCH_ID_PROPNAME = "COMPOUND_BATCH_ID"
+            
+    def __init__(self, transaction):
+        self.transaction = transaction
+        self.plate = plate
+        
+    def getWellCode(self, x, y):
+        return chr(ord('A') + x) + str(y)
+    
+    def getPlateDimensions(self):
+        # TODO KE: implement me
+        return (2, 2)
+    
+    def validateLibraryDimensions(self, csvLists):
+        (plateWidth, plateHeight) = self.getPlateDimensions()
+        
+        assert plateHeight == len(csvLists), \
+            "Plate geometry (height=%i) does not agree with LIBRARY_TEMPLATE (height=%i)." % (plateHeight, len(csvLists))
+            
+        for i in range(0, len(csvLists)):
+            assert plateWidth == len(csvLists[i]), \
+                "Plate geometry (width=%i) does not agree with LIBRARY_TEMPLATE (line=%i,width=%i)." % (plateWidth, i, len(csvLists[i]))
+        
+    def parseLibraryTemplate(self):
+        experimentId = plate.getExperiment().getExperimentIdentifier()
+        experiment = transaction.getExperiment(experimentId)
+        template = experiment.getPropertyValue(self.LIBRARY_TEMPLATE_PROPNAME)
+        
+        csvLists = [ line.split(",")  for line in template.splitlines() ]
+        self.validateLibraryDimensions(csvLists)
+        
+        library = {}
+        for x in range(0, len(csvLists)):
+            for y in range(0, len(csvLists[0])):
+                wellCode = self.getWellCode(x,y)
+                library[wellCode] = csvLists[x][y].strip()
+                 
+        return library
+    
+    
+    def fetchPlateCompounds(self):
+        """
+           Fetch well metadata from the Abase database.
+           
+           @return: a list of tuples (one per well) in the form 
+                    (wellCode, openBisCompoundCode, abaseCompoundBatchId, abaseCompoundId). 
+                    In case the plate is not found in Abase return None.
+        """
+        queryService = service.getDataSourceQueryService()
+        queryResult = queryService.select(self.ABASE_DATA_SOURCE, self.ABASE_QUERY, [plate.code])
+        
+        sanofiMaterials = []
+        for materialMap in list(queryResult):
+            wellCode = str(materialMap['WELLCODE'])
+            materialCode = str(materialMap['MATERIALCODE'])
+            sanofiId = str(materialMap['ABASE_COMPOUND_ID'])
+            sanofiBatchId = str(materialMap['ABASE_COMPOUND_BATCH_ID'])
+            material = SanofiMaterial(wellCode, materialCode, sanofiId, sanofiBatchId)
+            sanofiMaterials.append(material)
+            
+        queryResult.close()
+        
+        return sanofiMaterials
+    
+    def createMaterial(self, sanofiMaterial):
+        material = transaction.createNewMaterial(sanofiMaterial.materialCode, self.MATERIAL_TYPE)
+        material.setPropertyValue(self.MATERIAL_ID_PROPNAME, sanofiMaterial.sanofiId)
+        material.setPropertyValue(self.MATERIAL_BATCH_ID_PROPNAME, sanofiMaterial.sanofiBatchId)
+        return material
+    
+    def getOrCreateMaterials(self, library, sanofiMaterials):
+        materialCodes = [sanofiMaterial.materialCode for sanofiMaterial in sanofiMaterials]
+        materialCodes = removeDuplicates(materialCodes)
+        
+        materialIdentifiers = MaterialIdentifierCollection()
+        for materialCode in materialCodes:
+            materialIdentifiers.addIdentifier(self.MATERIAL_TYPE, materialCode)
+        searchService = transaction.getSearchService() 
+        preExistingMaterials = list(searchService.listMaterials(materialIdentifiers))
+        
+        materialsByCode = {}
+        for material in preExistingMaterials:
+            materialsByCode[ material.getCode() ] = material
+            
+        for materialCode in materialCodes:
+            if not materialCode in materialsByCode:
+                sanofiMaterial = self.getByMaterialCode(materialCode, sanofiMaterials)
+                openbisMaterial = self.createMaterial(sanofiMaterial)
+                materialsByCode[materialCode] = openbisMaterial 
+        
+        return materialsByCode
+    
+    def getByMaterialCode(self, materialCode, sanofiMaterials):
+        for sanofiMaterial in sanofiMaterials:
+            if materialCode == sanofiMaterial.materialCode:
+                return sanofiMaterial
+            
+        raise RuntimeError("No material found for materialCode " + wellCode)
+    
+    def getByWellCode(self, wellCode, sanofiMaterials):
+        for sanofiMaterial in sanofiMaterials:
+            if wellCode == sanofiMaterial.wellCode:
+                return sanofiMaterial
+            
+        raise RuntimeError("No material found for wellCode " + wellCode)
+    
+    def createWells(self, library, sanofiMaterials, openbisMaterials):
+        controlWellTypes = { "H" : "posti", "L" : "negative"};
+        for wellCode in library:    
+           libraryValue = library[wellCode].uppercase()
+           if libraryValue in controlWellTypes:
+               pass
+           else:
+               try 
+               except ValueError:
+                   raise RuntimeError("TODO...")
+               well = transaction.createNewSample(plate.code +":" + wellCode, self.WELL_TYPE)
+               well.setContainer(plate)
+               concentration = library[wellCode]
+               well.setPropertyValue(self.WELL_CONCENTRATION_PROPNAME, concentration)
+               materialCode = self.getByWellCode(wellCode, sanofiMaterials).materialCode
+               material = openbisMaterials[materialCode]
+               well.setMaterialPropertyValue(self.WELL_MATERIAL_PROPNAME, material)
+        
+
+    def createWellsAndMaterials(self):
+        library = self.parseLibraryTemplate()
+        sanofiMaterials = self.fetchPlateCompounds()
+        
+        # TODO KE: validate that library and sanofiMaterials data agrees
+        openbisMaterials = self.getOrCreateMaterials(library, sanofiMaterials)
+        self.createWells(library, sanofiMaterials, openbisMaterials)
+ 
+
+transaction = service.transaction()
+dataSet = transaction.createNewDataSet(DATA_SET_TYPE)
+
+(batchName, barCode) = parseIncomingDirname(incoming.getName())
+dataSet.setPropertyValue(DATA_SET_BATCH_PROPNAME, batchName)
+plate = findPlateByCode(barCode)
+
+if len(plate.getContainedSamples()) == 0:
+    plateInitializer = PlateInitializer(plate)
+    plateInitializer.createWellsAndMaterials()
+    
+    
+dataSet.setSample(plate)
+transaction.moveFile(incoming.getAbsolutePath(), dataSet)
+
+# TODO KE: send emails on ERROR/SUCCESS
diff --git a/sanofi/etc/log.xml b/sanofi/etc/log.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5cee0a68436a19a6c4ec4b14b580e6d9f75bf84f
--- /dev/null
+++ b/sanofi/etc/log.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
+
+  <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+     <layout class="org.apache.log4j.PatternLayout">
+       <param name="ConversionPattern" value="%d %-5p [%t] %c - %m%n"/>
+     </layout>
+  </appender>
+
+  <appender name="NULL" class="org.apache.log4j.varia.NullAppender" />
+
+  <root>
+     <priority value ="info" />
+     <appender-ref ref="STDOUT" />
+  </root>
+
+</log4j:configuration>
diff --git a/sanofi/.gitignore b/sanofi/source/java/.gitignore
similarity index 100%
rename from sanofi/.gitignore
rename to sanofi/source/java/.gitignore
diff --git a/sanofi/sourceTest/examples/batchNr_plateCode.variant_2011.06.28/sample.data b/sanofi/sourceTest/examples/batchNr_plateCode.variant_2011.06.28/sample.data
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/sanofi/sourceTest/java/ch/systemsx/cisd/sanofi/dss/test/SanofiDropboxJythonTest.java b/sanofi/sourceTest/java/ch/systemsx/cisd/sanofi/dss/test/SanofiDropboxJythonTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f69e8b5b9de791cb0eecf2611fa5c57a8acdfd06
--- /dev/null
+++ b/sanofi/sourceTest/java/ch/systemsx/cisd/sanofi/dss/test/SanofiDropboxJythonTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.sanofi.dss.test;
+
+import static ch.systemsx.cisd.common.Constants.IS_FINISHED_PREFIX;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.io.FileUtils;
+import org.jmock.Expectations;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.eodsql.MockDataSet;
+import ch.systemsx.cisd.common.filesystem.FileUtilities;
+import ch.systemsx.cisd.common.test.RecordingMatcher;
+import ch.systemsx.cisd.etlserver.registrator.AbstractJythonDataSetHandlerTest;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.IDataSourceQueryService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListMaterialCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.ExperimentBuilder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.SampleBuilder;
+import ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationResult;
+import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifierFactory;
+
+/**
+ * <pre>
+ * Things not tested
+ * - skip well creation when plate library already exists
+ * - skip material creation for preexisting materials
+ * - error cases
+ * </pre>
+ * 
+ * @author Kaloyan Enimanev
+ */
+public class SanofiDropboxJythonTest extends AbstractJythonDataSetHandlerTest
+{
+    private static final String PLATE_CODE = "plateCode";
+
+    private static final String LIBRARY_TEMPLATE_PROPNAME = "LIBRARY_TEMPLATE";
+
+    private static final String MATERIAL_TYPE = "COMPOUND_BATCH";
+
+    private static final String DATASET_DIR_NAME = "batchNr_plateCode.variant_2011.06.28";
+
+    private static final String DATA_SET_CODE = "data-set-code";
+
+    private static final DataSetType DATA_SET_TYPE = new DataSetType("DATA_SET_TYPE");
+
+    private static final String EXPERIMENT_IDENTIFIER = "/SANOFI/PROJECT/EXP";
+    private static final String PLATE_IDENTIFIER = "/SANOFI/TEST-PLATE";
+
+    private IDataSourceQueryService queryService;
+
+    @BeforeMethod
+    @Override
+    public void setUp() throws IOException
+    {
+        super.setUp();
+        queryService = context.mock(IDataSourceQueryService.class);
+    }
+
+    @Test(enabled = false)
+    public void testSimpleTransaction() throws IOException
+    {
+        setUpHomeDataBaseExpectations();
+        Properties properties = createThreadPropertiesRelativeToScriptsFolder("sanofi-dropbox.py");
+        createHandler(properties, false, true, queryService);
+        createData();
+
+        final String libraryTemplate = "1.45, H\n0.12, L";
+        final Sample plate = createPlate(libraryTemplate);
+        setUpPlateSearchExpectations(plate);
+        setUpLibraryTemplateExpectations(plate);
+
+        final MockDataSet<Map<String, Object>> queryResult = new MockDataSet<Map<String, Object>>();
+        queryResult.add(createQueryResult("A0"));
+        queryResult.add(createQueryResult("B0"));
+
+        final RecordingMatcher<ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationDetails> atomicatOperationDetails =
+                new RecordingMatcher<ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationDetails>();
+        final RecordingMatcher<ListMaterialCriteria> materialCriteria =
+                new RecordingMatcher<ListMaterialCriteria>();
+        context.checking(new Expectations()
+            {
+                {
+                    one(queryService).select(with(any(String.class)), with(any(String.class)),
+                            with(anything()));
+                    will(returnValue(queryResult));
+
+                    one(openBisService).listMaterials(with(materialCriteria), with(equal(true)));
+                    will(returnValue(Collections.emptyList()));
+                    
+                    one(openBisService).createPermId();
+                    will(returnValue("A0-permId"));
+
+                    one(openBisService).createPermId();
+                    will(returnValue("B0-permId"));
+
+                    one(openBisService).createDataSetCode();
+                    will(returnValue(DATA_SET_CODE));
+
+                    one(dataSetValidator).assertValidDataSet(DATA_SET_TYPE,
+                            new File(new File(stagingDirectory, DATA_SET_CODE), DATASET_DIR_NAME));
+
+                    SampleIdentifier sampleIdentifier =
+                            SampleIdentifierFactory.parse(plate.getIdentifier());
+                    one(openBisService).tryGetSampleWithExperiment(sampleIdentifier);
+                    will(returnValue(plate));
+
+                    one(openBisService).getPropertiesOfTopSampleRegisteredFor(sampleIdentifier);
+                    will(returnValue(new IEntityProperty[0]));
+
+                    one(openBisService).performEntityOperations(with(atomicatOperationDetails));
+                    will(returnValue(new AtomicEntityOperationResult()));
+                }
+            });
+
+        handler.handle(markerFile);
+
+        assertEquals(MATERIAL_TYPE, materialCriteria.recordedObject().tryGetMaterialType());
+        assertEquals(true, queryResult.hasCloseBeenInvoked());
+
+        assertEquals(1, atomicatOperationDetails.recordedObject().getDataSetRegistrations().size());
+
+        NewExternalData dataSet =
+                atomicatOperationDetails.recordedObject().getDataSetRegistrations().get(0);
+
+        assertEquals(DATA_SET_CODE, dataSet.getCode());
+        assertEquals(DATA_SET_TYPE, dataSet.getDataSetType());
+
+        context.assertIsSatisfied();
+    }
+
+    private void setUpPlateSearchExpectations(final Sample plate)
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    SearchCriteria sc = new SearchCriteria();
+                    sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.TYPE,
+                            "PLATE"));
+                    sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE,
+                            PLATE_CODE));
+                    oneOf(openBisService).searchForSamples(sc);
+
+                    will(returnValue(Arrays.asList(plate)));
+                }
+            });
+    }
+
+    private void setUpLibraryTemplateExpectations(final Sample plate)
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    final String identifierString = plate.getExperiment().getIdentifier();
+                    ExperimentIdentifier identifier =
+                            ExperimentIdentifierFactory.parse(identifierString);
+                    oneOf(openBisService).tryToGetExperiment(identifier);
+                    will(returnValue(plate.getExperiment()));
+                }
+            });
+    }
+
+    private void createData() throws IOException
+    {
+        File dataDirectory = new File("./sourceTest/examples/" + DATASET_DIR_NAME);
+        FileUtils.copyDirectoryToDirectory(dataDirectory, workingDirectory);
+        incomingDataSetFile = new File(workingDirectory, dataDirectory.getName());
+
+        markerFile = new File(workingDirectory, IS_FINISHED_PREFIX + dataDirectory.getName());
+        FileUtilities.writeToFile(markerFile, "");
+    }
+
+    private Sample createPlate(String libraryTemplate)
+    {
+        ExperimentBuilder experimentBuilder =
+                new ExperimentBuilder().identifier(EXPERIMENT_IDENTIFIER).property(
+                        LIBRARY_TEMPLATE_PROPNAME,
+                        libraryTemplate);
+
+        SampleBuilder sampleBuilder =
+                new SampleBuilder().identifier(PLATE_IDENTIFIER).experiment(
+                        experimentBuilder.getExperiment());
+        final Sample plate = sampleBuilder.getSample();
+        return plate;
+    }
+
+    private Map<String, Object> createQueryResult(String wellCode)
+    {
+        Map<String, Object> result = new HashMap<String, Object>();
+        result.put("WELL_CODE", wellCode);
+        result.put("MATERIAL_CODE", wellCode + "_material_code");
+        result.put("ABASE_COMPOUND_ID", wellCode + "_compound_id");
+        result.put("ABASE_COMPOUND_BATCH_ID", wellCode + "_compound_batch_id");
+        return result;
+    }
+
+    @Override
+    protected String getRegistrationScriptsFolderPath()
+    {
+        return "dist/etc/sanofi-dropbox/";
+    }
+}
\ No newline at end of file
diff --git a/sanofi/sourceTest/java/tests.xml b/sanofi/sourceTest/java/tests.xml
new file mode 100644
index 0000000000000000000000000000000000000000..082bf00ea75a27c289e1b0fe079726fd9dc54245
--- /dev/null
+++ b/sanofi/sourceTest/java/tests.xml
@@ -0,0 +1,14 @@
+<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
+
+<suite name="All" verbose="1">
+    <test name="All">
+        <groups>
+            <run>
+                <exclude name="broken" />
+            </run>
+        </groups>
+        <packages>
+            <package name="eu.basynthec.cisd.dss.*" />
+        </packages>
+    </test>
+</suite>
diff --git a/sanofi/sourceTest/python/unit-tests.py b/sanofi/sourceTest/python/unit-tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..ac84070940f3808e2195c57e543ae28e0ae09c9b
--- /dev/null
+++ b/sanofi/sourceTest/python/unit-tests.py
@@ -0,0 +1,11 @@
+import unittest
+
+class DropboxUnitTests(unittest.TestCase):
+
+    def test_parse_incoming_dir_name(self):
+        self.assertEqual(("batchName", "barCode"), parse_incoming_dirname("batchName barCode 2011-06-28"))
+        self.assertEqual(("batchName", "barCode"), parse_incoming_dirname("batchName    barCode     2011-06-28"))
+        
+    def test_parse_incoming_dir_name(self):
+        self.assertEqual(("batchName", "barCode"), parse_incoming_dirname("batchName barCode 2011-06-28"))
+        self.assertEqual(("batchName", "barCode"), parse_incoming_dirname("batchName    barCode     2011-06-28"))        
\ No newline at end of file