From 261aabff0d90ef25a0686ffc647a923b20a48fc8 Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Fri, 18 Nov 2011 11:59:45 +0000
Subject: [PATCH] LMS-2631 Added new metabolomics format

SVN: 23714
---
 .../etc/metabolomics2/data-set-handler.py     |  82 ++++++++++++++++
 .../etc/metabolomics2/data-set-validator.py   |  91 ++++++++++++++++++
 .../dist/etc/shared/shared-classes.py         |  39 +++++---
 .../examples/Metabolomics2-BadData.xlsx       | Bin 0 -> 11000 bytes
 .../examples/Metabolomics2-Example.xlsx       | Bin 0 -> 11050 bytes
 .../examples/Metabolomics2-Template.xlsx      | Bin 0 -> 10499 bytes
 .../examples/~$Metabolomics2-Example.xlsx     | Bin 0 -> 171 bytes
 .../MetabolomicsDataSetRegistrator2Test.java  |  80 +++++++++++++++
 .../MetabolomicsValidator2Test.java           |  66 +++++++++++++
 9 files changed, 347 insertions(+), 11 deletions(-)
 create mode 100644 eu_basynthec/dist/etc/metabolomics2/data-set-handler.py
 create mode 100644 eu_basynthec/dist/etc/metabolomics2/data-set-validator.py
 create mode 100644 eu_basynthec/sourceTest/examples/Metabolomics2-BadData.xlsx
 create mode 100644 eu_basynthec/sourceTest/examples/Metabolomics2-Example.xlsx
 create mode 100644 eu_basynthec/sourceTest/examples/Metabolomics2-Template.xlsx
 create mode 100644 eu_basynthec/sourceTest/examples/~$Metabolomics2-Example.xlsx
 create mode 100644 eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsDataSetRegistrator2Test.java
 create mode 100644 eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsValidator2Test.java

diff --git a/eu_basynthec/dist/etc/metabolomics2/data-set-handler.py b/eu_basynthec/dist/etc/metabolomics2/data-set-handler.py
new file mode 100644
index 00000000000..eee034223fc
--- /dev/null
+++ b/eu_basynthec/dist/etc/metabolomics2/data-set-handler.py
@@ -0,0 +1,82 @@
+from datetime import datetime
+from eu.basynthec.cisd.dss import TimeSeriesDataExcel
+
+def set_data_type(data_set):
+  data_set.setPropertyValue("DATA_TYPE", "METABOLITE_INTENSITIES")
+
+def retrieve_experiment(tr, exp_id):
+  """Get the specified experiment form the server. Return the experiment."""
+  if exp_id is None:
+    exp = None
+  else:
+    exp = tr.getExperiment(exp_id)
+  return exp
+
+def assign_properties(dataset, metadata):
+  """Assign properties to the data set from information in the data."""
+  propertyNameMap = {
+    "STRAIN":"STRAIN_NAMES", 
+    "TIMEPOINT TYPE": "TIMEPOINT_TYPE", 
+    "CELL LOCATION": "CELL_LOCATION", 
+    "VALUE TYPE": "VALUE_TYPE", 
+    "VALUE UNIT": "VALUE_UNIT", 
+    "SCALE": "SCALE"
+    }
+    
+  for prop in metadata.keySet():
+    key = propertyNameMap.get(prop)
+    if key is not None:
+      value = metadata.get(prop)
+      dataset.setPropertyValue(key, value.upper())
+      
+def convert_data_to_tsv(tr, dataset, location):
+  """Create a tsv file containing the data and add it to the data set."""
+  tr.createNewDirectory(dataset, location)
+  tsvFileName = tr.createNewFile(dataset, location, incoming.getName() + ".tsv")
+  tsv = open(tsvFileName, 'w')
+  for line in timeSeriesData.getRawDataLines():
+    for i in range(0, len(line) - 1):
+      tsv.write(line[i])
+      tsv.write("\t")
+    tsv.write(line[len(line) - 1])
+    tsv.write("\n")
+  tsv.close()
+  
+def store_original_data(tr, dataset, location):
+  """Put the original data into the data set."""
+  tr.createNewDirectory(dataset, location)
+  tr.moveFile(incoming.getAbsolutePath(), dataset, location + "/" + incoming.getName())
+
+
+tr = service.transaction(incoming)
+timeSeriesData = TimeSeriesDataExcel.createTimeSeriesDataExcel(incoming.getAbsolutePath())
+
+# create the data set and assign the metadata from the file
+dataset = tr.createNewDataSet("METABOLITE_INTENSITIES")
+metadata = timeSeriesData.getMetadataMap()
+assign_properties(dataset, metadata)
+    
+# Store the original and tsv data in data sets                                                                                                                    
+original_dataset = tr.createNewDataSet("EXCEL_ORIGINAL")
+set_data_type(original_dataset)
+store_original_data(tr, original_dataset, "xls")
+
+tsv_dataset = tr.createNewDataSet("TSV_EXPORT")
+set_data_type(tsv_dataset)
+convert_data_to_tsv(tr, tsv_dataset, "tsv")
+
+# Make the original contain these
+contained_codes = [original_dataset.getDataSetCode(), tsv_dataset.getDataSetCode()]
+dataset.setContainedDataSetCodes(contained_codes)
+
+
+# If no experiment has been set, then get the experiment from the excel file
+if dataset.getExperiment() is None:
+  exp_id = metadata.get("EXPERIMENT")
+  exp = retrieve_experiment(tr, exp_id)
+  if exp is not None:
+    dataset.setExperiment(exp)
+    original_dataset.setExperiment(exp)
+    tsv_dataset.setExperiment(exp)
+
+
diff --git a/eu_basynthec/dist/etc/metabolomics2/data-set-validator.py b/eu_basynthec/dist/etc/metabolomics2/data-set-validator.py
new file mode 100644
index 00000000000..ca28902b986
--- /dev/null
+++ b/eu_basynthec/dist/etc/metabolomics2/data-set-validator.py
@@ -0,0 +1,91 @@
+# validate the header -- row 1 contains a strainid, row 2 a value type, row 3, a value unit
+def validate_header_line(row, first_data_col, line, errors):
+  # validate the strain
+  if row is 0:
+    for i in range(first_data_col, len(line)):
+      strain = line[i]
+      if not isStrainIdValid(strain):
+        errors.append(createFileValidationError("Strain in col " + str(i + 1) + " " + strainValidationErrorMessageFragment(strain)))
+  
+  # validate the value type
+  elif row is 1:
+    for i in range(first_data_col, len(line)):
+      isControlledVocabularyPropertyValid(line[i],
+        "value type", ['VALUE', 'MEAN', 'MEDIAN', 'STD', 'VAR', 'ERROR', 'IQR'], 
+        "'Value', 'Mean', 'Median', 'Std', 'Var', 'Error', 'Iqr'",
+        errors)
+
+  # validate the value unit
+  else:
+    for i in range(first_data_col, len(line)):
+      isControlledVocabularyPropertyValid(line[i], 
+        "value unit", ['MM', 'UM', 'RATIOT1', 'RATIOCS'], "'mM', 'uM', 'RatioT1', 'RatioCs'",
+        errors)
+      
+
+def validate_data(time_series_data, first_data_row, first_data_col, errors):
+  chebiRegex = re.compile("^CHEBI:[0-9]+")
+  bsbmeRegex = re.compile("^BSBME:[0-9]+")
+  dataLines = time_series_data.getRawDataLines()
+  lineCount = 0
+  for line in dataLines:
+    # Dispatch to another function to validate the header
+    if lineCount < first_data_row:
+      validate_header_line(lineCount, first_data_col, line, errors)
+      lineCount = lineCount + 1
+      continue
+
+    # The header needs to be CompoundID    
+    if lineCount is first_data_row:
+      if line[0] != "CompoundID":
+        errors.append(createFileValidationError("The first data column must be 'CompoundID'"))
+        break
+      lineCount = lineCount + 1
+      continue
+
+    # The compound id should be one of these forms
+    compoundId = line[0]
+    if not chebiRegex.match(compoundId):
+      if not bsbmeRegex.match(compoundId):
+        errors.append(createFileValidationError("Line " + str(lineCount + 1) + ", column 1 must be of the format 'CHEBI:#' or 'BSBME:#' (instead of " + compoundId + ")."))
+    lineCount = lineCount + 1
+    
+def validate_metadata(time_series_data, errors):
+  metadata = time_series_data.getMetadataMap()
+  validationHelper = ValidationHelper(metadata, errors)
+  
+    # validate the header format
+  validationHelper.validateExplicitHeaderFormat("METABOL HYBRID")
+  
+  # validate the timepoint type
+  validationHelper.validateControlledVocabularyProperty("TIMEPOINT TYPE", 
+    "time point type", ['EX', 'IN', 'SI'], "'EX', 'IN', 'SI'")
+
+  # validate the cell location    
+  validationHelper.validateControlledVocabularyProperty("CELL LOCATION",
+     "cell location", ['CE', 'ES', 'ME', 'CY', 'NC'], "'CE', 'ES', 'ME', 'CY', 'NC'")
+     
+  # validate the scale
+  validationHelper.validateControlledVocabularyProperty("SCALE", "scale",
+    ['LIN', 'LOG2', 'LOG10', 'LN'], "'lin', 'log2', 'log10', 'ln'")
+    
+  # validate the data position specification
+  validationHelper.validateStartDataRowCol()
+
+
+def validate_data_set_file(file):
+  errors = []
+  time_series_data = create_time_series_excel(file.getAbsolutePath())
+  if time_series_data is None:
+    errors.append(createFileValidationError(file.getName() + " is not an Excel file."))
+    return errors
+    
+  # validate the metadata
+  validate_metadata(time_series_data, errors)
+  
+  data_start = getInitialDataRowAndCol(time_series_data.getMetadataMap())
+      
+  # validate the data
+  validate_data(time_series_data, data_start[0], data_start[1], errors)
+  
+  return errors
diff --git a/eu_basynthec/dist/etc/shared/shared-classes.py b/eu_basynthec/dist/etc/shared/shared-classes.py
index 5ff2e494996..85911070ab2 100644
--- a/eu_basynthec/dist/etc/shared/shared-classes.py
+++ b/eu_basynthec/dist/etc/shared/shared-classes.py
@@ -61,7 +61,8 @@ class TimeSeriesDataExcel:
       value = line[1];
       if "BLANK" == value:
         value = None
-      metadataMap[line[0].upper()] = value
+      if line[0] is not None:
+        metadataMap[line[0].upper()] = value
     return metadataMap
     
 def create_time_series_excel(fileName):
@@ -123,14 +124,8 @@ class ValidationHelper:
       
   def validateControlledVocabularyProperty(self, property, displayName, allowedValues, allowedValuesDisplay):
     """Validate that the property is specified and in the list of allowed values"""
-    if not self.checkIsSpecified(property, displayName):
-      return
-    value = self.metadataMap.get(property).upper()
-    if value not in allowedValues:
-      if len(allowedValues) > 1:
-        self.errors.append(createFileValidationError("The " + displayName + " must be one of " + allowedValuesDisplay + " (not " + value + ")."))
-      else:
-        self.errors.append(createFileValidationError("The " + displayName + " must be " + allowedValuesDisplay + " (not " + value + ")."))
+    value = self.metadataMap.get(property)
+    isControlledVocabularyPropertyValid(value, displayName, allowedValues, allowedValuesDisplay, self.errors)
         
   def validateStartDataRowCol(self):
     if self.checkIsSpecified("START DATA ROW", "Start Data Row"):
@@ -147,6 +142,7 @@ class ValidationHelper:
 strainIdRegex = re.compile("^JJS-MGP[0-9]{1,3}|^JJS-DIN[0-9]{1,3}|^MS|CHASSIS\s*[1-3]|WT 168 TRP\+")
 def isStrainIdValid(strainId):
   """Return true if the strain id passes validation (has the form sepecified in the regex)"""
+  strainId = strainId.strip().upper()
   match = strainIdRegex.match(strainId)
   if match is None:
     return False
@@ -155,6 +151,21 @@ def isStrainIdValid(strainId):
 def strainValidationErrorMessageFragment(strain):
     """Return a sentence fragment describing the strain validation error."""
     return "must be either JJS-MGP[0-999], JJS-DIN[0-999], MS, CHASSIS [1-3], or WT 168 TRP+ (instead of " + strain + ")."
+
+def isControlledVocabularyPropertyValid(value, displayName, allowedValues, allowedValuesDisplay, errors):
+    """Validate that the property is specified and in the list of allowed values"""
+    if value is None:
+      errors.append(ValidationError.createFileValidationError("A " + displayName + " must be specified."))
+      return False
+    value = value.upper()
+    if value not in allowedValues:
+      if len(allowedValues) > 1:
+        errors.append(createFileValidationError("The " + displayName + " must be one of " + allowedValuesDisplay + " (not " + value + ")."))
+        return False
+      else:
+        errors.append(createFileValidationError("The " + displayName + " must be " + allowedValuesDisplay + " (not " + value + ")."))
+        return False
+    return True
   
 def getInitialDataRowAndCol(metadata):
   """Extract the initial row and column as specified in the metadata. Returns an array with [row, col]."""
@@ -166,12 +177,18 @@ def getInitialDataRowAndCol(metadata):
   if first_data_row is None:
     first_data_row = 0
   else:
-    first_data_row = int(float(first_data_row)) - 1
+    try:
+      first_data_row = int(float(first_data_row)) - 1
+    except:
+      first_data_row = 0
 
   # convert the column spreadsheet value to an int
   if first_data_col is None:
     first_data_col = 0
   else:
     # columns start at A
-    first_data_col = ord(first_data_col) - ord('A')
+    try:
+      first_data_col = ord(first_data_col) - ord('A')
+    except:
+      first_data_cal = 0
   return [first_data_row, first_data_col]
diff --git a/eu_basynthec/sourceTest/examples/Metabolomics2-BadData.xlsx b/eu_basynthec/sourceTest/examples/Metabolomics2-BadData.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..59033160ac866fc6f6e0a7dec0af4041462a9a34
GIT binary patch
literal 11000
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

literal 0
HcmV?d00001

diff --git a/eu_basynthec/sourceTest/examples/Metabolomics2-Example.xlsx b/eu_basynthec/sourceTest/examples/Metabolomics2-Example.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..c088af65b2b61a70398ce1071dba9c3254fa3bd0
GIT binary patch
literal 11050
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

literal 0
HcmV?d00001

diff --git a/eu_basynthec/sourceTest/examples/Metabolomics2-Template.xlsx b/eu_basynthec/sourceTest/examples/Metabolomics2-Template.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..bfe543475cb730817d5da050ea6235897d4c5de9
GIT binary patch
literal 10499
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
Bg4_TA

literal 0
HcmV?d00001

diff --git a/eu_basynthec/sourceTest/examples/~$Metabolomics2-Example.xlsx b/eu_basynthec/sourceTest/examples/~$Metabolomics2-Example.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..24f29bb860948c7c5f82a89ff6bfd4ff17ce621a
GIT binary patch
literal 171
mcmZQe(M>8YPE{ZgurZ`C=rSZR6a!%@g944Az^uDIin#y}(h}+b

literal 0
HcmV?d00001

diff --git a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsDataSetRegistrator2Test.java b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsDataSetRegistrator2Test.java
new file mode 100644
index 00000000000..abefb863e13
--- /dev/null
+++ b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsDataSetRegistrator2Test.java
@@ -0,0 +1,80 @@
+/*
+ * 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.metabolomics;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.test.RecordingMatcher;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
+import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
+import ch.systemsx.cisd.openbis.generic.shared.dto.NewProperty;
+
+import eu.basynthec.cisd.dss.AbstractBaSynthecDataSetRegistratorTest;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class MetabolomicsDataSetRegistrator2Test extends AbstractBaSynthecDataSetRegistratorTest
+{
+    private static final DataSetType DATA_SET_TYPE = new DataSetType("METABOLITE_INTENSITIES");
+
+    @Test
+    public void testSimpleTransaction() throws IOException
+    {
+        setUpHomeDataBaseExpectations();
+        Properties properties = createThreadProperties();
+        createHandler(properties, false, true);
+        createData("Metabolomics-Example.xlsx");
+
+        final RecordingMatcher<ch.systemsx.cisd.openbis.generic.shared.dto.AtomicEntityOperationDetails> atomicOperationDetails =
+                setUpDataSetRegistrationExpectations(DATA_SET_TYPE, TSV_DATA_SET_TYPE);
+
+        handler.handle(markerFile);
+
+        assertEquals(3, atomicOperationDetails.recordedObject().getDataSetRegistrations().size());
+
+        checkDataTypeProperty(atomicOperationDetails.recordedObject().getDataSetRegistrations()
+                .get(1), "METABOLITE_INTENSITIES");
+        checkDataTypeProperty(atomicOperationDetails.recordedObject().getDataSetRegistrations()
+                .get(2), "METABOLITE_INTENSITIES");
+
+        NewExternalData dataSet =
+                atomicOperationDetails.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("CHASSIS 1", strainProperty.getValue());
+        context.assertIsSatisfied();
+    }
+
+    @Override
+    protected String getRegistrationScriptsFolderPath()
+    {
+        return "dist/etc/metabolomics/";
+    }
+}
diff --git a/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsValidator2Test.java b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsValidator2Test.java
new file mode 100644
index 00000000000..270c82379de
--- /dev/null
+++ b/eu_basynthec/sourceTest/java/eu/basynthec/cisd/dss/metabolomics/MetabolomicsValidator2Test.java
@@ -0,0 +1,66 @@
+/*
+ * 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.metabolomics;
+
+import java.io.File;
+import java.util.List;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationError;
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.validation.ValidationScriptRunner;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class MetabolomicsValidator2Test extends AssertJUnit
+{
+    private static final String[] VALIDATION_SCRIPT_PATH = new String[]
+        { "dist/etc/shared/shared-classes.py", "dist/etc/metabolomics2/data-set-validator.py" };
+
+    @Test
+    public void testGoodData()
+    {
+        ValidationScriptRunner scriptRunner =
+                ValidationScriptRunner.createValidatorFromScriptPaths(VALIDATION_SCRIPT_PATH);
+        List<ValidationError> errors =
+                scriptRunner.validate(new File("sourceTest/examples/Metabolomics2-Example.xlsx"));
+        assertTrue("The example should have no errors", errors.isEmpty());
+    }
+
+    @Test
+    public void testTemplate()
+    {
+        ValidationScriptRunner scriptRunner =
+                ValidationScriptRunner.createValidatorFromScriptPaths(VALIDATION_SCRIPT_PATH);
+        List<ValidationError> errors =
+                scriptRunner.validate(new File("sourceTest/examples/Metabolomics2-Template.xlsx"));
+        System.out.println(errors);
+        assertEquals("The template should have seven errors", 7, errors.size());
+    }
+
+    @Test
+    public void testBadData()
+    {
+        ValidationScriptRunner scriptRunner =
+                ValidationScriptRunner.createValidatorFromScriptPaths(VALIDATION_SCRIPT_PATH);
+        List<ValidationError> errors =
+                scriptRunner.validate(new File("sourceTest/examples/Metabolomics2-BadData.xlsx"));
+        assertEquals("The bad data should have seven errors", 7, errors.size());
+    }
+}
-- 
GitLab