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