From c49ba87c757a6df14e6efc33a2a601518c93b5a5 Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Tue, 28 Jun 2011 12:05:46 +0000
Subject: [PATCH] LMS-2340 Updating basynthec stuff based on results of the
 leiden meeting.

SVN: 21872
---
 .../etc/growth-profiles/data-set-handler.py   |  13 +-
 .../etc/growth-profiles/data-set-validator.py | 105 +-------------
 .../etc/metabolomics/data-set-validator.py    |  92 ------------
 .../dist/etc/proteomics/data-set-validator.py |  92 ------------
 .../dist/etc/shared/shared-classes.py         | 101 ++++++++++++++
 .../etc/transcriptomics/data-set-validator.py |  92 ------------
 .../sourceTest/examples/OD600-BadData.xlsx    | Bin 0 -> 9823 bytes
 .../sourceTest/examples/OD600-Example.xlsx    | Bin 9723 -> 9914 bytes
 .../sourceTest/examples/OD600-Template.xlsx   | Bin 9303 -> 9254 bytes
 .../OD600DataSetRegistratorTest.java          | 132 ++++++++++++++++++
 .../growthprofiles/OD600ValidatorTest.java    |  22 ++-
 .../MetabolomicsValidatorTest.java            |   2 +-
 .../proteomics/ProteomicsValidatorTest.java   |   2 +-
 .../TranscriptomicsValidatorTest.java         |   2 +-
 14 files changed, 269 insertions(+), 386 deletions(-)
 create mode 100644 eu_basynthec/dist/etc/shared/shared-classes.py
 create mode 100644 eu_basynthec/sourceTest/examples/OD600-BadData.xlsx
 create mode 100644 eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/growthprofiles/OD600DataSetRegistratorTest.java

diff --git a/eu_basynthec/dist/etc/growth-profiles/data-set-handler.py b/eu_basynthec/dist/etc/growth-profiles/data-set-handler.py
index 425699d6a9b..e8462a5d0c8 100644
--- a/eu_basynthec/dist/etc/growth-profiles/data-set-handler.py
+++ b/eu_basynthec/dist/etc/growth-profiles/data-set-handler.py
@@ -9,10 +9,19 @@ def retrieve_experiment(tr, exp_id):
 		exp = tr.getExperiment(exp_id)
 	return exp
 
+def extract_strains():
+	"""Extract the strains from the data sheet"""
+	strains = []
+	lines = timeSeriesData.getRawDataLines()
+	for i in range(1, len(lines)):
+		line = lines[i]
+		strains.append(line[0])
+	return ",".join(strains)
+
 def assign_properties(dataset, metadata):
 	"""Assign properties to the data set from information in the data."""
 	propertyNameMap = {
-		"STRAIN":"STRAIN", 
+		"STRAIN_NAMES": "STRAIN_NAMES",
 		"TIMEPOINT TYPE": "TIMEPOINT_TYPE", 
 		"CELL LOCATION": "CELL_LOCATION", 
 		"VALUE TYPE": "VALUE_TYPE", 
@@ -53,6 +62,8 @@ timeSeriesData = TimeSeriesDataExcel.createTimeSeriesDataExcel(incoming.getAbsol
 # create the data set and assign the metadata from the file
 dataset = tr.createNewDataSet("OD600")
 metadata = timeSeriesData.getMetadataMap()
+# Strains are not in the metadata, but in the data, so extract them
+metadata["STRAIN_NAMES"] = extract_strains()
 assign_properties(dataset, metadata)
 		
 # Convert the data into a tsv file, and put that and the original data into the data set
diff --git a/eu_basynthec/dist/etc/growth-profiles/data-set-validator.py b/eu_basynthec/dist/etc/growth-profiles/data-set-validator.py
index c09a86937a6..c3c470a0a61 100644
--- a/eu_basynthec/dist/etc/growth-profiles/data-set-validator.py
+++ b/eu_basynthec/dist/etc/growth-profiles/data-set-validator.py
@@ -1,120 +1,25 @@
-import os
-import re
-import sys
-import java.io.File
-from java.io import IOException
-from java.lang import IllegalArgumentException
-from ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation import ValidationError, ValidationScriptRunner
-from ch.systemsx.cisd.openbis.dss.generic.shared.utils import ExcelFileReader
-from ch.systemsx.cisd.common.logging import LogFactory, LogCategory
-
-operationLog = LogFactory.getLogger(LogCategory.OPERATION, ValidationScriptRunner)
-OPENBIS_METADATA_SHEET_NAME = "openbis-metadata"
-OPENBIS_DATA_SHEET_NAME = "openbis-data"
-
-class TimeSeriesDataExcel:
-	"""
-		An abstraction for accessing time series data following the BaSynthec conventions 
-		from an Excel file. This class ported from Java, thus the camelCase naming.
-	"""
-	def __init__(self, file, fileReader):
-		self.file = file
-		self.fileReader = fileReader
-		
-	def getRawMetadataLines(self):
-		"""Get the raw lines of the metadata sheet."""
-		try:
-			return self.fileReader.readLines(OPENBIS_METADATA_SHEET_NAME);
-		except IOException, ex:
-			operationLog.error("Could not read data from [file: " + self.file.getPath() + ", sheet: "
-					+ OPENBIS_METADATA_SHEET_NAME + "]", ex)
-		return []
-
-	def getRawDataLines(self):
-		"""Get the raw lines of the data sheet."""
-		try:
-			return self.fileReader.readLines(OPENBIS_DATA_SHEET_NAME)
-		except IOException, ex:
-			operationLog.error("Could not read data from [file: " + file.getPath() + ", sheet: "
-					+ OPENBIS_DATA_SHEET_NAME + "]", ex)
-		return []
-
-	def getMetadataMap(self):
-		"""
-			Return the metadata has a hashmap, with all keys uppercased.
-			
-			Assumes the metadata sheet corresponds to the following format: [Property] [Value] [... stuff
-			that can be ignored], that is the property name is in column 1 and property value is in
-			column 2, and everything else can be ignored.
-		"""
-		metadataMap = {}
-		metadataLines = self.getRawMetadataLines()
-		
-		# Skip the first line, this is just the header
-		for i in range(1, metadataLines.size()):
-			line = metadataLines.get(i)
-			value = line[1];
-			if "BLANK" == value:
-				value = None
-			metadataMap[line[0].upper()] = value
-		return metadataMap
-		
-def create_time_series_excel(fileName):
-	"""Factory method for the TimeSeriesData object. Returns None if it cannot be created"""
-	file = java.io.File(fileName)
-	try:
-		workbook = ExcelFileReader.getExcelWorkbook(file)
-		fileReader = ExcelFileReader(workbook, True)
-		return TimeSeriesDataExcel(file, fileReader)
-	except IllegalArgumentException, ex:
-		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
-	except IOException, ex:
-		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
-	return None
-
-
-		
-class ValidationHelper:
-	"""
-		Methods for simplifying validation in BaSynthec.
-		This class ported from Java, thus the camelCase naming. 
-	"""
-	def __init__(self, metadataMap, errors):
-		self.metadataMap = metadataMap
-		self.errors = errors
-
-	def checkIsSpecified(self, property, displayName):
-		if self.metadataMap.get(property) is None:
-			self.errors.append(ValidationError.createFileValidationError("A " + displayName
-					+ " must be specified."))
-			return False
-		return True
-
 def validate_data(timeSeriesData, errors):
 	dataLines = timeSeriesData.getRawDataLines()
 	lineCount = 0
 	for line in dataLines:
 		# The header needs to be Abs
 		if lineCount is 0:
-			if line[0] != "Abs":
-				errors.append(createFileValidationError("The first data column must be 'Abs'"))
+			if line[0] != "Strain":
+				errors.append(createFileValidationError("The first data column must be 'Strain'"))
 				break
 			lineCount = lineCount + 1
 			continue
 
 		# The compound id should be one of these forms
-		od600 = line[0]
-		if od600 != "OD600":
-			errors.append(createFileValidationError("Line " + str(lineCount + 1) + ", column 1 must be OD600 (instead of " + od600 + ")."))
+		strain = line[0]
+		if not isStrainIdValid(strain):
+			errors.append(createFileValidationError("Line " + str(lineCount + 1) + ", column 1 must be MGP[0-999] (instead of " + strain + ")."))
 		lineCount = lineCount + 1
 
 def validate_metadata(time_series_data, errors):
 	metadata = time_series_data.getMetadataMap()
 	validationHelper = ValidationHelper(metadata, errors)
 	
-	# validate the strain
-	validationHelper.checkIsSpecified("STRAIN", "strain")
-	
 	# validate the timepoint type
 	if validationHelper.checkIsSpecified("TIMEPOINT TYPE", "time point type"):
 		if metadata.get("TIMEPOINT TYPE").upper() not in ['EX', 'IN', 'SI']:
diff --git a/eu_basynthec/dist/etc/metabolomics/data-set-validator.py b/eu_basynthec/dist/etc/metabolomics/data-set-validator.py
index e6e549068a4..d6ef4b4cb27 100644
--- a/eu_basynthec/dist/etc/metabolomics/data-set-validator.py
+++ b/eu_basynthec/dist/etc/metabolomics/data-set-validator.py
@@ -1,95 +1,3 @@
-import os
-import re
-import sys
-import java.io.File
-from java.io import IOException
-from java.lang import IllegalArgumentException
-from ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation import ValidationError, ValidationScriptRunner
-from ch.systemsx.cisd.openbis.dss.generic.shared.utils import ExcelFileReader
-from ch.systemsx.cisd.common.logging import LogFactory, LogCategory
-
-operationLog = LogFactory.getLogger(LogCategory.OPERATION, ValidationScriptRunner)
-OPENBIS_METADATA_SHEET_NAME = "openbis-metadata"
-OPENBIS_DATA_SHEET_NAME = "openbis-data"
-
-class TimeSeriesDataExcel:
-	"""
-		An abstraction for accessing time series data following the BaSynthec conventions 
-		from an Excel file. This class ported from Java, thus the camelCase naming.
-	"""
-	def __init__(self, file, fileReader):
-		self.file = file
-		self.fileReader = fileReader
-		
-	def getRawMetadataLines(self):
-		"""Get the raw lines of the metadata sheet."""
-		try:
-			return self.fileReader.readLines(OPENBIS_METADATA_SHEET_NAME);
-		except IOException, ex:
-			operationLog.error("Could not read data from [file: " + self.file.getPath() + ", sheet: "
-					+ OPENBIS_METADATA_SHEET_NAME + "]", ex)
-		return []
-
-	def getRawDataLines(self):
-		"""Get the raw lines of the data sheet."""
-		try:
-			return self.fileReader.readLines(OPENBIS_DATA_SHEET_NAME)
-		except IOException, ex:
-			operationLog.error("Could not read data from [file: " + file.getPath() + ", sheet: "
-					+ OPENBIS_DATA_SHEET_NAME + "]", ex)
-		return []
-
-	def getMetadataMap(self):
-		"""
-			Return the metadata has a hashmap, with all keys uppercased.
-			
-			Assumes the metadata sheet corresponds to the following format: [Property] [Value] [... stuff
-			that can be ignored], that is the property name is in column 1 and property value is in
-			column 2, and everything else can be ignored.
-		"""
-		metadataMap = {}
-		metadataLines = self.getRawMetadataLines()
-		
-		# Skip the first line, this is just the header
-		for i in range(1, metadataLines.size()):
-			line = metadataLines.get(i)
-			value = line[1];
-			if "BLANK" == value:
-				value = None
-			metadataMap[line[0].upper()] = value
-		return metadataMap
-		
-def create_time_series_excel(fileName):
-	"""Factory method for the TimeSeriesData object. Returns None if it cannot be created"""
-	file = java.io.File(fileName)
-	try:
-		workbook = ExcelFileReader.getExcelWorkbook(file)
-		fileReader = ExcelFileReader(workbook, True)
-		return TimeSeriesDataExcel(file, fileReader)
-	except IllegalArgumentException, ex:
-		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
-	except IOException, ex:
-		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
-	return None
-
-
-		
-class ValidationHelper:
-	"""
-		Methods for simplifying validation in BaSynthec.
-		This class ported from Java, thus the camelCase naming. 
-	"""
-	def __init__(self, metadataMap, errors):
-		self.metadataMap = metadataMap
-		self.errors = errors
-
-	def checkIsSpecified(self, property, displayName):
-		if self.metadataMap.get(property) is None:
-			self.errors.append(ValidationError.createFileValidationError("A " + displayName
-					+ " must be specified."))
-			return False
-		return True
-
 
 def validate_data(time_series_data, errors):
 	chebiRegex = re.compile("^CHEBI:[0-9]+")
diff --git a/eu_basynthec/dist/etc/proteomics/data-set-validator.py b/eu_basynthec/dist/etc/proteomics/data-set-validator.py
index c1150d736eb..3b4b73e074b 100644
--- a/eu_basynthec/dist/etc/proteomics/data-set-validator.py
+++ b/eu_basynthec/dist/etc/proteomics/data-set-validator.py
@@ -1,95 +1,3 @@
-import os
-import re
-import sys
-import java.io.File
-from java.io import IOException
-from java.lang import IllegalArgumentException
-from ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation import ValidationError, ValidationScriptRunner
-from ch.systemsx.cisd.openbis.dss.generic.shared.utils import ExcelFileReader
-from ch.systemsx.cisd.common.logging import LogFactory, LogCategory
-
-operationLog = LogFactory.getLogger(LogCategory.OPERATION, ValidationScriptRunner)
-OPENBIS_METADATA_SHEET_NAME = "openbis-metadata"
-OPENBIS_DATA_SHEET_NAME = "openbis-data"
-
-class TimeSeriesDataExcel:
-	"""
-		An abstraction for accessing time series data following the BaSynthec conventions 
-		from an Excel file. This class ported from Java, thus the camelCase naming.
-	"""
-	def __init__(self, file, fileReader):
-		self.file = file
-		self.fileReader = fileReader
-		
-	def getRawMetadataLines(self):
-		"""Get the raw lines of the metadata sheet."""
-		try:
-			return self.fileReader.readLines(OPENBIS_METADATA_SHEET_NAME);
-		except IOException, ex:
-			operationLog.error("Could not read data from [file: " + self.file.getPath() + ", sheet: "
-					+ OPENBIS_METADATA_SHEET_NAME + "]", ex)
-		return []
-
-	def getRawDataLines(self):
-		"""Get the raw lines of the data sheet."""
-		try:
-			return self.fileReader.readLines(OPENBIS_DATA_SHEET_NAME)
-		except IOException, ex:
-			operationLog.error("Could not read data from [file: " + file.getPath() + ", sheet: "
-					+ OPENBIS_DATA_SHEET_NAME + "]", ex)
-		return []
-
-	def getMetadataMap(self):
-		"""
-			Return the metadata has a hashmap, with all keys uppercased.
-			
-			Assumes the metadata sheet corresponds to the following format: [Property] [Value] [... stuff
-			that can be ignored], that is the property name is in column 1 and property value is in
-			column 2, and everything else can be ignored.
-		"""
-		metadataMap = {}
-		metadataLines = self.getRawMetadataLines()
-		
-		# Skip the first line, this is just the header
-		for i in range(1, metadataLines.size()):
-			line = metadataLines.get(i)
-			value = line[1];
-			if "BLANK" == value:
-				value = None
-			metadataMap[line[0].upper()] = value
-		return metadataMap
-		
-def create_time_series_excel(fileName):
-	"""Factory method for the TimeSeriesData object. Returns None if it cannot be created"""
-	file = java.io.File(fileName)
-	try:
-		workbook = ExcelFileReader.getExcelWorkbook(file)
-		fileReader = ExcelFileReader(workbook, True)
-		return TimeSeriesDataExcel(file, fileReader)
-	except IllegalArgumentException, ex:
-		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
-	except IOException, ex:
-		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
-	return None
-
-
-		
-class ValidationHelper:
-	"""
-		Methods for simplifying validation in BaSynthec.
-		This class ported from Java, thus the camelCase naming. 
-	"""
-	def __init__(self, metadataMap, errors):
-		self.metadataMap = metadataMap
-		self.errors = errors
-
-	def checkIsSpecified(self, property, displayName):
-		if self.metadataMap.get(property) is None:
-			self.errors.append(ValidationError.createFileValidationError("A " + displayName
-					+ " must be specified."))
-			return False
-		return True
-
 def validate_data(time_series_data, errors):
 	gene_locus_regex = re.compile("^BSU[0-9]+|^BSU_misc_RNA_[0-9]+|^VMG_[0-9]+_[0-9]+(_c)?")
 	column_header_regex = re.compile("(\+|-)?[0-9]+::(value|mean|median|std|var|error|iqr)")
diff --git a/eu_basynthec/dist/etc/shared/shared-classes.py b/eu_basynthec/dist/etc/shared/shared-classes.py
new file mode 100644
index 00000000000..07f7cecb4e3
--- /dev/null
+++ b/eu_basynthec/dist/etc/shared/shared-classes.py
@@ -0,0 +1,101 @@
+import os
+import re
+import sys
+import java.io.File
+from java.io import IOException
+from java.lang import IllegalArgumentException
+from ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation import ValidationError, ValidationScriptRunner
+from ch.systemsx.cisd.openbis.dss.generic.shared.utils import ExcelFileReader
+from ch.systemsx.cisd.common.logging import LogFactory, LogCategory
+
+operationLog = LogFactory.getLogger(LogCategory.OPERATION, ValidationScriptRunner)
+OPENBIS_METADATA_SHEET_NAME = "openbis-metadata"
+OPENBIS_DATA_SHEET_NAME = "openbis-data"
+
+class TimeSeriesDataExcel:
+	"""
+		An abstraction for accessing time series data following the BaSynthec conventions 
+		from an Excel file. This class ported from Java, thus the camelCase naming.
+	"""
+	def __init__(self, file, fileReader):
+		self.file = file
+		self.fileReader = fileReader
+		
+	def getRawMetadataLines(self):
+		"""Get the raw lines of the metadata sheet."""
+		try:
+			return self.fileReader.readLines(OPENBIS_METADATA_SHEET_NAME);
+		except IOException, ex:
+			operationLog.error("Could not read data from [file: " + self.file.getPath() + ", sheet: "
+					+ OPENBIS_METADATA_SHEET_NAME + "]", ex)
+		return []
+
+	def getRawDataLines(self):
+		"""Get the raw lines of the data sheet."""
+		try:
+			return self.fileReader.readLines(OPENBIS_DATA_SHEET_NAME)
+		except IOException, ex:
+			operationLog.error("Could not read data from [file: " + file.getPath() + ", sheet: "
+					+ OPENBIS_DATA_SHEET_NAME + "]", ex)
+		return []
+
+	def getMetadataMap(self):
+		"""
+			Return the metadata has a hashmap, with all keys uppercased.
+			
+			Assumes the metadata sheet corresponds to the following format: [Property] [Value] [... stuff
+			that can be ignored], that is the property name is in column 1 and property value is in
+			column 2, and everything else can be ignored.
+		"""
+		metadataMap = {}
+		metadataLines = self.getRawMetadataLines()
+		
+		# Skip the first line, this is just the header
+		for i in range(1, metadataLines.size()):
+			line = metadataLines.get(i)
+			value = line[1];
+			if "BLANK" == value:
+				value = None
+			metadataMap[line[0].upper()] = value
+		return metadataMap
+		
+def create_time_series_excel(fileName):
+	"""Factory method for the TimeSeriesData object. Returns None if it cannot be created."""
+	file = java.io.File(fileName)
+	try:
+		workbook = ExcelFileReader.getExcelWorkbook(file)
+		fileReader = ExcelFileReader(workbook, True)
+		return TimeSeriesDataExcel(file, fileReader)
+	except IllegalArgumentException, ex:
+		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
+	except IOException, ex:
+		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
+	return None
+
+
+		
+class ValidationHelper:
+	"""
+		Methods for simplifying validation in BaSynthec.
+		This class is ported from Java, thus the camelCase naming. 
+	"""
+	def __init__(self, metadataMap, errors):
+		self.metadataMap = metadataMap
+		self.errors = errors
+
+	def checkIsSpecified(self, property, displayName):
+		"""Verify that a property is specified; if not, add a validation error to the list."""
+		if self.metadataMap.get(property) is None:
+			self.errors.append(ValidationError.createFileValidationError("A " + displayName
+					+ " must be specified."))
+			return False
+		return True
+		
+	
+strainIdRegex = re.compile("^MGP[0-9]{1,3}")
+def isStrainIdValid(strainId):
+	"""Return true if the strain id passes validation (has the form MGP[:digit:]{1,3})"""
+	match = strainIdRegex.match(strainId)
+	if match is None:
+		return False
+	return match.end() == len(strainId)
diff --git a/eu_basynthec/dist/etc/transcriptomics/data-set-validator.py b/eu_basynthec/dist/etc/transcriptomics/data-set-validator.py
index 5cdf0840db0..b42c2fdb592 100644
--- a/eu_basynthec/dist/etc/transcriptomics/data-set-validator.py
+++ b/eu_basynthec/dist/etc/transcriptomics/data-set-validator.py
@@ -1,95 +1,3 @@
-import os
-import re
-import sys
-import java.io.File
-from java.io import IOException
-from java.lang import IllegalArgumentException
-from ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation import ValidationError, ValidationScriptRunner
-from ch.systemsx.cisd.openbis.dss.generic.shared.utils import ExcelFileReader
-from ch.systemsx.cisd.common.logging import LogFactory, LogCategory
-
-operationLog = LogFactory.getLogger(LogCategory.OPERATION, ValidationScriptRunner)
-OPENBIS_METADATA_SHEET_NAME = "openbis-metadata"
-OPENBIS_DATA_SHEET_NAME = "openbis-data"
-
-class TimeSeriesDataExcel:
-	"""
-		An abstraction for accessing time series data following the BaSynthec conventions 
-		from an Excel file. This class ported from Java, thus the camelCase naming.
-	"""
-	def __init__(self, file, fileReader):
-		self.file = file
-		self.fileReader = fileReader
-		
-	def getRawMetadataLines(self):
-		"""Get the raw lines of the metadata sheet."""
-		try:
-			return self.fileReader.readLines(OPENBIS_METADATA_SHEET_NAME);
-		except IOException, ex:
-			operationLog.error("Could not read data from [file: " + self.file.getPath() + ", sheet: "
-					+ OPENBIS_METADATA_SHEET_NAME + "]", ex)
-		return []
-
-	def getRawDataLines(self):
-		"""Get the raw lines of the data sheet."""
-		try:
-			return self.fileReader.readLines(OPENBIS_DATA_SHEET_NAME)
-		except IOException, ex:
-			operationLog.error("Could not read data from [file: " + file.getPath() + ", sheet: "
-					+ OPENBIS_DATA_SHEET_NAME + "]", ex)
-		return []
-
-	def getMetadataMap(self):
-		"""
-			Return the metadata has a hashmap, with all keys uppercased.
-			
-			Assumes the metadata sheet corresponds to the following format: [Property] [Value] [... stuff
-			that can be ignored], that is the property name is in column 1 and property value is in
-			column 2, and everything else can be ignored.
-		"""
-		metadataMap = {}
-		metadataLines = self.getRawMetadataLines()
-		
-		# Skip the first line, this is just the header
-		for i in range(1, metadataLines.size()):
-			line = metadataLines.get(i)
-			value = line[1];
-			if "BLANK" == value:
-				value = None
-			metadataMap[line[0].upper()] = value
-		return metadataMap
-		
-def create_time_series_excel(fileName):
-	"""Factory method for the TimeSeriesData object. Returns None if it cannot be created"""
-	file = java.io.File(fileName)
-	try:
-		workbook = ExcelFileReader.getExcelWorkbook(file)
-		fileReader = ExcelFileReader(workbook, True)
-		return TimeSeriesDataExcel(file, fileReader)
-	except IllegalArgumentException, ex:
-		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
-	except IOException, ex:
-		operationLog.error("Could not open file [" + fileName + "] as Excel data.", ex)
-	return None
-
-
-		
-class ValidationHelper:
-	"""
-		Methods for simplifying validation in BaSynthec.
-		This class ported from Java, thus the camelCase naming. 
-	"""
-	def __init__(self, metadataMap, errors):
-		self.metadataMap = metadataMap
-		self.errors = errors
-
-	def checkIsSpecified(self, property, displayName):
-		if self.metadataMap.get(property) is None:
-			self.errors.append(ValidationError.createFileValidationError("A " + displayName
-					+ " must be specified."))
-			return False
-		return True
-
 def validate_data(time_series_data, errors):
 	gene_locus_regex = re.compile("^BSU[0-9]+|^BSU_misc_RNA_[0-9]+|^VMG_[0-9]+_[0-9]+(_c)?")
 	dataLines = time_series_data.getRawDataLines()
diff --git a/eu_basynthec/sourceTest/examples/OD600-BadData.xlsx b/eu_basynthec/sourceTest/examples/OD600-BadData.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..77472c04d3163f7920b325721eaa0ad7c375796a
GIT binary patch
literal 9823
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

literal 0
HcmV?d00001

diff --git a/eu_basynthec/sourceTest/examples/OD600-Example.xlsx b/eu_basynthec/sourceTest/examples/OD600-Example.xlsx
index d6e35b4b893639449069f18023af3fb499c40204..4f83f9ce28149ce85353f1e0456bfaddba7aa093 100644
GIT binary patch
delta 3581
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

delta 3384
zcmY*cX*d)N*PcOP?2KgwH8e9S6=i42zVA~e2H6<~L&%oKmL=I{ki8h$iiE5g#E_?j
zl%|X&Ta<m@dF%c0KHqnKo$I>q>-;$9y3e^!i|M{`&2u*5j}&JUDi8orVg>+s002OU
zw@fhMHr|;)z+VdS@y<t^5*V_aZ9H>C-ax{1NFV9M%Oqd>mjG=`ux&WTRaK~%N4wZ#
zdpxx^@1D0Br<x_4rcc%Pl{tz3X3l0Aesg8_!W3uzTIQYj=;NJx5!9a-iUTv{$22=n
zOROcWEf7Z=wkMjlkKVF%(4v(R7WER!p*2@N-sMQS@`?u|**pPr7S+JK1Zv=Q3f)e}
zW)82iI|&?}=%T;E_{~^(bWX7FcNmncx=<Kz-}zK=jhH{o_GXBBdM<VPxx04DWa^cJ
zs4)8W3X->F>w1~#KbPtd!ZUJxjE?h(vd7sM4Tx}b$?D-pahZa3<*iG~>{4nQkJ}Kr
zm&d&Bi(f@YN0QLn(7m1L96Lo6?&fZx#=3$8s&Hfzb*Aw&nWyDV>5pBn9Q9sm^fmeH
ze=r=N;*s&EDj;f!>TjuA3Sxp~Jrp<Jj5AdpFQ%GLWOSP#jp`qX`%8s6QGW5=8A3T7
zR$-SKa}TUcuWhL-SPh;<%jEDKL^Qm`{9@(Yxj2b?%-z9Fw2K@%zcYu=KS!X|-&KVo
z94%zW2KIi%wLCd)Zy6bO3V__GYjvjUox<S-IH06@RPuQ<C5I!Xx+su86Z^@!uZr*f
z%$Gpk=M*P?BIL>P_KZs{5|wvzhTuFy^1vaz?6`~)tD>Q;xekOiQ{J$<*U`SX{O@7<
z=q>O2Z`GIfsjbR=AJzxD2!Xe3$q$CCE&xnjY<<isLdx5Tdlx!b-LONpF%`-bVWscA
zJc--Dx#z1Db6O(p2EX#SS!Z4>B*alwuuA0`r$>o~d!LHDBH~5jZdFh1V7ZM%fyo6X
z2DIF<E09BZr=vZX@#~MIv{5Kacx;s-pZB6?+w*72O!JO-en_qBLV3fJJT+E*yw-OT
zKX3wt?(F&b)s6*}gScHvJu|DzqAS>$vUNazGde4_RB(pFPB}nCbR+q*%M)wmY%Gs5
z)`!USW%^`%U}Dh@M6=n!q|%66rk7fV5~XGCxIi<S%eK(*N4gVv2}vGC(J0xd>v%!2
zAnEv(@;&zNkQ>P@1_!+<FVu1o(0+p3i5)Mlxh&2>{p1vie_Xrr3AcAZd#umu!ybM?
z(<5%%tIJlnGa)xp^uDSXiyne9%%8WkpVC8>VQqx14udD291MC$a~F>Um<bEcZt#nU
z0(kW5N`vJ3_wPR5oU`ex-5e;Oy>wCfBp$A@Lx}cOqWizENC2j7#7pcmlcPXjvKNB4
z4#*?U3fh}?_9gvM?<IEfJ`_T{@?Y6zF?<=yym8CI--3Aqn<InjxrMxiD+=U2$vq#6
z+Dbi#g(M$fNm&Jr<LPI;F1BbS?Y$}4@xS-cA_iiro{w`HicQMTlw$;=7?KG0yw@Wi
z7U+-iEWK>Cif_k&Fr=JAv)DtYqiu)JXQl|w<_Yy0MPRvb8Mn-4Vs%HjR=S_Ht`W?(
z=NxI}ZE$tDvS!}b1R>uap)ZXsZw(tsOtn)w8yI3_L>H@(QH753xipqz+O-$47|KrY
z^w~*5yjLuwO2fH8xcM$YH#{fI{d7o9kW(@)MMc0{S~TyWWd2Zn&BX_Gt@eG&SYbcI
zdf%Ive6~0E+XzHIk<M!PBMVn>=+a-7CpJP@8kuxet9x;<pincqTi=$ytfKOuu6bx=
zGxufV$q^s-23Alsm_^CbmMt##f`R4xg{SIq+nWUG;rE(@iwga-*yGX&b!p$KbQlNb
z;H}bao<u-sjf8FB*=Yf_G1A?}M>SBz=xjkpu4ADOdHGS+#7`CCsRgUuh7_DwnW3xg
z4h&AgHq_nB()3`bG(61j-bPNyaCUv<)$_?#o`M;n)pvfSSFbg<B_c?;YcmTt%{Ni&
z#agC&dp1A1UQN1hg0dv+fc={zsinSLH{;cIR>hFo4LJDk-M5cJqLg%ulSv&qbf~VA
zxAiCv+*3*%l1e`KqSFWs74{toI@`7~8K6(%$deI=n7@x%QscL4S{dLw)>?dqEd54d
zN^GX0=kHBJ9<7^3$tn^S4S@!lU6VFZ-{d-znEV=?Rx3NY)tE2A%^z!~y;gq*vtE)r
zD_c7~1?cHQ*|7M4LQ_1ZN6}H=ATcr?>c?SVP6V-f=wo-HRM$A|qv$KSpuvfr9p_o8
z#jIt>r;)AhhwIf+>3r{I3*nzurk&sp8&pRW%QiH2f)|C8RqUXjGKmvPh3fPT2u{(S
zZ&1Gh#^tl&Q&C!~s5!&M^&&J^X8;c;{T7%_tRFCNAy-vEweuK#UfSx_O@-=V?RQx!
zam3$Lx2N8!`qx{>q;ZxA+nU<>eN)ToffN*s`p(!%(3PWkUd2@KXXV;6*1k%N^9T5s
zjXN#zDP~Vx!??Z2t{^v=aU}9<;ac%lA@d@0k#Y72?Vu+`EtPFu9>6<&?Bp$JiA5?u
zH|jWGemA4AcIYs8KFSAn7e`1%*k}$c3Lo^!>6uP#4ac$H({7`WwVH1GsP!JGlOsWb
zAeuv1X!jqfy8V@^BykD}j(G9q*m2d`b27CL4tclbHTkJhda;sX*1B)PW@howAE=gd
zxY-uz67Sl8-K6!<<GY$!o6;hbS5@aKZkWicJn-NH;k3K66k5d&59z(Ff}o6^4?^_f
zB9$--DB|qBLxdVA)FRv{2*8>A&!=1Sid@>dTNy420vCOL5X}+rUg))%OE;=YLhx#<
zswTxazT5Ai(<10yIkDnOk)r<Sz<kBwbOWpl*WOU!tKaW?`uYd_FYwrh>$4D|oV&bA
z$1nUc_&i%qHdQSN>V>3hB^u>jJJ$<e3{7S-8#Hg#Gk9Im2_Iv;O)7ctPvJGsi{DE>
zgY70G_C(LuUL-Q__N;3Rp4T_`$5vZdyME1o@$l4;uG&Dzx)I(;e)Nr4QAbES-=t&Y
zV;C4nsReW;B)VU{=xINTE9lrbbm@&ma`L8}v1{n3MAX-9O2%+u>cN{z4MX7~{1{pm
zrs>({<H;R0x7N0}aw`)ZuC@t?pF4)vHO1U)W+3!(IMG^f+kedwGnlu^s!?(syCT<a
zsfdnrH3lwEX}rv*t>gSVWsl9{_M?Df%w5hZ+}xdvPVGl=3bPQrohw?5**YC&_3GR6
zW##*Y$mQ;DrtkZu)|K=*z#7(?^)jRj3!OGsU|+d`6!V1e(`Z$;g*v_~5?X1Oy)Fl+
zc&gxKiI&d-7N~2iHF%i5<8eZQ@U@aa_)0$PI<k;Wc0!yF`8z3OL*H60R?7L8wh`;9
z&Orxz_EE4-cDao6Qfkc|HuLUGo2M~$4?;u?QD@%YrWSAsY%18<wEaQ4GH?Dtyb{Lg
z0O@6MF63`~0_A19eG{Iw8JupX`L~)0@lKdoc3aSVY~tQ|(m9T@H<~EH&y{s)gPNDD
z;Of~t0T!c`vX&0IcdPy)*Ne^W$JkY7kKD#Iwfr{HZ;hv*<(Pu%`I8j|p<7M&@I@O6
z#o!NqW4V>|N%6zznG}Wd<(?-PGPxHulL8gKM&LeSA0!W0$omBFNS(XS5B@*|*s&M&
zpJtww9<mp$Z+g0x8KI;g?A4UU$!!{r`B^TAHhwP;R!AVS7B$`TwMqHeH+A6u(&Utc
zg9RvY<j8L`jB9pR^Mp<y7hAlTim$32!!MLu=)2x2rjy8yqdOkj@(x$C;XvOOU`|DQ
zO4ZvrLn3CF8lMM@XP)^TcDYcqOHMgnJ6JlB!NUG4(IULrn(I-LnU>jcV{rQF^}V%S
z{EDu3r_RbsY8<sA|NB<s&6*xXQ_vH})v7(e*D%3jroV^y#8F?QzHS}m6cR{uFz%8C
z)5cG<YxjlcxgO^iEo!)#gQ5j#joBkUG$fZ{J)=>{rxBY2n?`KsA1pOLDtXpcSu9gi
z{X8fRr)9x{{Zjjd!Fg49J|t0cwmIu{rGo1mfV*n#UD6E^q!&kOgQ8;u`y~*3F<TZg
z97+)BF<a2^f5Z~cJz3iDV4vvs%GSGS=Z9^FfR>PT)GR4H#n#dLq@=3kQ<1cvpE2SZ
zAWp%v17l9P76((Cj+y)Gnq?33D>qmL+*NzT>z>ZhI@^M09KCaqo~8B=(;iRK<M+#a
zzchVWtY&$F%8X{`7@;dZT5SzTo3yO3cc2vz+49CRU<B@%c{}#!m%`L}SRnUhpQqkK
z-zsekra@-}v^-6Iu4V`YesqsD6(aadIV>~$A~eMr$HsQu)0Hr~cLS$~4hpyiejJf-
zAo{nRb)piP%<TXBO32M{GhiZ_4FMDTpOa++0Kk7fm4D+WCy7glj73=fUoZ3uffD<7
z-vR(4|MLH2-yq~;D3mOP<Q4nx{Qfv(dnN#Y@Bfho#mU!^SDDOUWD-*H<moei!T$jJ
CRzaQs

diff --git a/eu_basynthec/sourceTest/examples/OD600-Template.xlsx b/eu_basynthec/sourceTest/examples/OD600-Template.xlsx
index 08dbb1ba136006d62f943514eda36cfbc8d33a88..be43feb2f882179020b274e7f3ca47119135cad8 100644
GIT binary patch
delta 2922
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

delta 3005
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
W=`4)=MoNpNjJz);#g3Ew{q|q{et%v7

diff --git a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/growthprofiles/OD600DataSetRegistratorTest.java b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/growthprofiles/OD600DataSetRegistratorTest.java
new file mode 100644
index 00000000000..eaa8a49f422
--- /dev/null
+++ b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/growthprofiles/OD600DataSetRegistratorTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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 eu.basynthec.cisd.dss.growthprofiles;
+
+import static ch.systemsx.cisd.common.Constants.IS_FINISHED_PREFIX;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.io.FileUtils;
+import org.jmock.Expectations;
+import org.testng.annotations.Test;
+
+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.openbis.generic.shared.basic.dto.DataSetType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.ExperimentBuilder;
+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.NewProperty;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class OD600DataSetRegistratorTest extends AbstractJythonDataSetHandlerTest
+{
+    private static final String STRAIN_NAMES_PROP = "STRAIN_NAMES";
+
+    private static final String DATA_SET_CODE = "data-set-code";
+
+    private static final DataSetType DATA_SET_TYPE = new DataSetType("OD600");
+
+    private static final String EXPERIMENT_IDENTIFIER = "/TEST/TEST/TEST";
+
+    @Test
+    public void testSimpleTransaction() throws IOException
+    {
+        setUpHomeDataBaseExpectations();
+        Properties properties =
+                createThreadPropertiesRelativeToScriptsFolder("data-set-handler.py",
+                        "dist/etc/shared/shared-classes.py,dist/etc/growth-profiles/data-set-validator.py");
+        createHandler(properties, false, true);
+        createData();
+
+        ExperimentBuilder builder = new ExperimentBuilder().identifier(EXPERIMENT_IDENTIFIER);
+        final Experiment experiment = builder.getExperiment();
+        final RecordingMatcher<ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationDetails> atomicatOperationDetails =
+                new RecordingMatcher<ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationDetails>();
+        context.checking(new Expectations()
+            {
+                {
+                    one(openBisService).createDataSetCode();
+                    will(returnValue(DATA_SET_CODE));
+                    atLeast(1).of(openBisService).tryToGetExperiment(
+                            new ExperimentIdentifierFactory(experiment.getIdentifier())
+                                    .createIdentifier());
+                    will(returnValue(experiment));
+
+                    one(dataSetValidator).assertValidDataSet(DATA_SET_TYPE,
+                            new File(new File(stagingDirectory, DATA_SET_CODE), "data"));
+                    one(openBisService).performEntityOperations(with(atomicatOperationDetails));
+                    will(returnValue(new AtomicEntityOperationResult()));
+                }
+            });
+
+        handler.handle(markerFile);
+
+        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());
+
+        HashMap<String, NewProperty> propertyMap =
+                getDataSetPropertiesMap(dataSet.getDataSetProperties());
+        NewProperty strainProperty = propertyMap.get(STRAIN_NAMES_PROP);
+
+        assertNotNull(strainProperty);
+        assert null != strainProperty;
+        assertEquals("MGP1,MGP100,MGP20,MGP999", strainProperty.getValue());
+        context.assertIsSatisfied();
+    }
+
+    private void createData() throws IOException
+    {
+        File dataFile = new File("sourceTest/examples/OD600-Example.xlsx");
+        FileUtils.copyFileToDirectory(dataFile, workingDirectory);
+        incomingDataSetFile = new File(workingDirectory, dataFile.getName());
+
+        markerFile = new File(workingDirectory, IS_FINISHED_PREFIX + dataFile.getName());
+        FileUtilities.writeToFile(markerFile, "");
+    }
+
+    protected HashMap<String, NewProperty> getDataSetPropertiesMap(
+            List<NewProperty> dataSetProperties)
+    {
+        HashMap<String, NewProperty> propertyMap = new HashMap<String, NewProperty>();
+        for (NewProperty prop : dataSetProperties)
+        {
+            propertyMap.put(prop.getPropertyCode(), prop);
+        }
+        return propertyMap;
+    }
+
+    @Override
+    protected String getRegistrationScriptsFolderPath()
+    {
+        return "dist/etc/growth-profiles/";
+    }
+}
diff --git a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/growthprofiles/OD600ValidatorTest.java b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/growthprofiles/OD600ValidatorTest.java
index de98a23d192..3d4059fb036 100644
--- a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/growthprofiles/OD600ValidatorTest.java
+++ b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/growthprofiles/OD600ValidatorTest.java
@@ -31,7 +31,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationS
 public class OD600ValidatorTest extends AssertJUnit
 {
     private static final String[] VALIDATION_SCRIPT_PATH = new String[]
-        { "dist/etc/growth-profiles/data-set-validator.py" };
+        { "dist/etc/shared/shared-classes.py", "dist/etc/growth-profiles/data-set-validator.py" };
 
     @Test
     public void testGoodData()
@@ -43,6 +43,20 @@ public class OD600ValidatorTest extends AssertJUnit
         assertTrue("The example should have no errors", errors.isEmpty());
     }
 
+    @Test
+    public void testBadData()
+    {
+        ValidationScriptRunner scriptRunner =
+                ValidationScriptRunner.createValidatorFromScriptPaths(VALIDATION_SCRIPT_PATH);
+        List<ValidationError> errors =
+                scriptRunner.validate(new File("sourceTest/examples/OD600-BadData.xlsx"));
+        assertEquals("The bad data should have two errors", 2, errors.size());
+        assertEquals("Line 2, column 1 must be MGP[0-999] (instead of OD600).", errors.get(0)
+                .getErrorMessage());
+        assertEquals("Line 3, column 1 must be MGP[0-999] (instead of MGP1000).", errors.get(1)
+                .getErrorMessage());
+    }
+
     @Test
     public void testTemplate()
     {
@@ -50,11 +64,7 @@ public class OD600ValidatorTest extends AssertJUnit
                 ValidationScriptRunner.createValidatorFromScriptPaths(VALIDATION_SCRIPT_PATH);
         List<ValidationError> errors =
                 scriptRunner.validate(new File("sourceTest/examples/OD600-Template.xlsx"));
-        if (errors.size() > 0)
-        {
-            System.out.println(errors);
-        }
-        assertEquals("The template should have five errors", 5, errors.size());
+        assertEquals("The template should have five errors", 4, errors.size());
     }
 
 }
diff --git a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsValidatorTest.java b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsValidatorTest.java
index 6d2fcf11532..8dc2ba733bd 100644
--- a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsValidatorTest.java
+++ b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsValidatorTest.java
@@ -31,7 +31,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationS
 public class MetabolomicsValidatorTest extends AssertJUnit
 {
     private static final String[] VALIDATION_SCRIPT_PATH = new String[]
-        { "dist/etc/metabolomics/data-set-validator.py" };
+        { "dist/etc/shared/shared-classes.py", "dist/etc/metabolomics/data-set-validator.py" };
 
     @Test
     public void testGoodData()
diff --git a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/proteomics/ProteomicsValidatorTest.java b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/proteomics/ProteomicsValidatorTest.java
index d75f588fc76..2a41d436116 100644
--- a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/proteomics/ProteomicsValidatorTest.java
+++ b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/proteomics/ProteomicsValidatorTest.java
@@ -31,7 +31,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationS
 public class ProteomicsValidatorTest extends AssertJUnit
 {
     private static final String[] VALIDATION_SCRIPT_PATH = new String[]
-        { "dist/etc/proteomics/data-set-validator.py" };
+        { "dist/etc/shared/shared-classes.py", "dist/etc/proteomics/data-set-validator.py" };
 
     @Test
     public void testGoodData()
diff --git a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/transcriptomics/TranscriptomicsValidatorTest.java b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/transcriptomics/TranscriptomicsValidatorTest.java
index e6c76a7c283..1b009c17245 100644
--- a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/transcriptomics/TranscriptomicsValidatorTest.java
+++ b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/transcriptomics/TranscriptomicsValidatorTest.java
@@ -31,7 +31,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationS
 public class TranscriptomicsValidatorTest extends AssertJUnit
 {
     private static final String[] VALIDATION_SCRIPT_PATH = new String[]
-        { "dist/etc/transcriptomics/data-set-validator.py" };
+        { "dist/etc/shared/shared-classes.py", "dist/etc/transcriptomics/data-set-validator.py" };
 
     @Test
     public void testGoodData()
-- 
GitLab