From 2d0ff9dde427acad8139667599fb86dc77caea4b Mon Sep 17 00:00:00 2001 From: kaloyane <kaloyane> Date: Wed, 27 Jul 2011 13:35:08 +0000 Subject: [PATCH] [LMS-2407] additional methods exposed to Matlab. Data set properties are now supplied by the loadDataSet***() methods. SVN: 22269 --- screening/source/java/OpenBISScreeningML.java | 142 +++++++++++++-- .../v1/IScreeningOpenbisServiceFacade.java | 13 ++ .../api/v1/ScreeningOpenbisServiceFacade.java | 30 ++++ .../java/OpenBISScreeningMLTest.java | 169 ++++++++++++++---- 4 files changed, 306 insertions(+), 48 deletions(-) diff --git a/screening/source/java/OpenBISScreeningML.java b/screening/source/java/OpenBISScreeningML.java index 350fdc3dd7c..a787546f3f4 100644 --- a/screening/source/java/OpenBISScreeningML.java +++ b/screening/source/java/OpenBISScreeningML.java @@ -33,6 +33,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet; import ch.systemsx.cisd.openbis.dss.client.api.v1.IDataSetDss; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetMetadataDTO; @@ -462,6 +463,37 @@ public class OpenBISScreeningML return listPlates(experimentPlates); } + /** + * Lists the plates of <var>experiment</var> and analysis procedure. Each returned plate has at + * least one data set with the specified analysis procedure. + * <p> + * Matlab example: + * + * <pre> + * % Get all plates having data sets with analysis procedure 'PROC' in the experiment MYEXP in project PROJ of space SPACE + * plates = OpenBISScreeningML.listPlates('/SPACE/PROJ/MYEXP', 'PROC'); + * % Get all information about plate 2 + * plate2 = plates(2,:) + * % Get the augmented plate codes for all plates + * acodes = plates(:,1) + * </pre> + * + * @param experiment The augmented code of the experiment to list the plates for + * @param analysisProcedure The analysis procedure + * @return Each row contains information about one plate: + * <p> + * <code>{ plate augmented code, plate perm id, plate space code, plate code, + * experiment augmented code, experiment perm id, experiment space code, + * experiment project code, experiment code }</code> + */ + public static Object[][] listPlates(String experiment, String analysisProcedure) + { + checkLoggedIn(); + ExperimentIdentifier experimentIdentifier = getExperimentIdentifierOrFail(experiment); + List<Plate> resultPlates = openbis.listPlates(experimentIdentifier, analysisProcedure); + return listPlates(resultPlates); + } + private static Object[][] listPlates(final List<Plate> list) { final Object[][] result = new Object[list.size()][9]; @@ -481,6 +513,44 @@ public class OpenBISScreeningML return result; } + /** + * Returns an alphabetically sorted list of analysis procedure codes of all data sets of a + * specified experiment. + * <p> + * Matlab example: + * + * <pre> + * % Get the analysis procedures for experiment MYEXP in project PROJ of space SPACE + * analysisProcedures = OpenBISScreeningML.listAnalysisProcedures('/SPACE/PROJ/MYEXP'); + * % How many analysis procedures do we have? + * length(analysisProcedures) + * % Get all the analysis procedure codes + * acodes = analysisProcedures(:,1) + * </pre> + * + * @param experiment The augmented code of the experiment to list analysis procedures for + * @return Each row contains information about one analysis procedure: + * <p> + * <code>{ analysis procedure code }</code> + */ + public static Object[][] listAnalysisProcedures(String experiment) + { + checkLoggedIn(); + ExperimentIdentifier experimentIdentifier = getExperimentIdentifierOrFail(experiment); + List<String> proceduresList = openbis.listAnalysisProcedures(experimentIdentifier); + return listAnalysisProcedures(proceduresList); + } + + private static Object[][] listAnalysisProcedures(final List<String> list) + { + final Object[][] result = new Object[list.size()][1]; + for (int i = 0; i < list.size(); ++i) + { + result[i][0] = list.get(i); + } + return result; + } + /** * Returns the properties of specified well for specified plate. * <p> @@ -506,16 +576,8 @@ public class OpenBISScreeningML checkLoggedIn(); WellPosition wellPosition = new WellPosition(row, column); WellIdentifier wellIdentifier = getWell(augmentedPlateCode, wellPosition); - List<Map.Entry<String, String>> list = - new ArrayList<Map.Entry<String, String>>(openbis.getWellProperties(wellIdentifier) - .entrySet()); - Object[][] result = new Object[list.size()][2]; - for (int i = 0; i < list.size(); i++) - { - result[i] = new Object[] - { list.get(i).getKey(), list.get(i).getValue() }; - } - return result; + Map<String, String> wellProperties = openbis.getWellProperties(wellIdentifier); + return listProperties(wellProperties); } /** @@ -696,7 +758,7 @@ public class OpenBISScreeningML } return result; } - + /** * Loads data sets for specified plate code. For each data set the path to the root of the data * set is returned. If it is possible the path points directly into the data set store. No data @@ -713,6 +775,12 @@ public class OpenBISScreeningML * dsinfo(:,1) * % Get root path of first data set (assuming there is at least one) * dsginfo(1,2) + * % Get the properties for the first data set + * props = dsginfo(1,3) + * % Get property key of first property + * props(1,1) + * % Get property value of first property + * props(1,2) * </pre> * * @param augmentedPlateCode The augmented plate code. @@ -723,7 +791,7 @@ public class OpenBISScreeningML * system mounts. * @return Each row contains information about one data set: * <p> - * <code>{ data set code, data set root path }</code> + * <code>{ data set code, data set root path, { {key1, value1}, {key2, value2} ...} }</code> */ public static Object[][] loadDataSets(String augmentedPlateCode, String dataSetTypeCodePattern, String overrideStoreRootPathOrNull) @@ -749,6 +817,12 @@ public class OpenBISScreeningML * dsinfo(:,1) * % Get root path of first data set (assuming there is at least one) * dsginfo(1,2) + * % Get the properties for the first data set + * props = dsginfo(1,3) + * % Get property key of first property + * props(1,1) + * % Get property value of first property + * props(1,2) * </pre> * * @param augmentedPlateCode The augmented plate code. @@ -761,8 +835,8 @@ public class OpenBISScreeningML * @param overrideStoreRootPathOrNull A path, in the context of the local file system mounts, to * the DSS' store root. If null, paths are returned in the context of the DSS' file * system mounts. - * <p> - * <code>{ data set code, data set root path }</code> + * <p> + * <code>{ data set code, data set root path, { {key1, value1}, {key2, value2} ...} }</code> */ public static Object[][] loadDataSets(String augmentedPlateCode, final String dataSetTypeCodePattern, final Object[][] properties, @@ -770,8 +844,9 @@ public class OpenBISScreeningML { checkLoggedIn(); Plate plateIdentifier = getPlate(augmentedPlateCode); - List<IDataSetDss> dataSets = - openbis.getDataSets(plateIdentifier, new AndDataSetFilter(new TypeBasedDataSetFilter( + List<DataSet> dataSets = + openbis.getFullDataSets(plateIdentifier, new AndDataSetFilter( + new TypeBasedDataSetFilter( dataSetTypeCodePattern), new PropertiesBasedDataSetFilter( createMap(properties)))); Object[][] result = new Object[dataSets.size()][]; @@ -779,7 +854,7 @@ public class OpenBISScreeningML { for (int i = 0; i < dataSets.size(); i++) { - IDataSetDss dataSet = dataSets.get(i); + DataSet dataSet = dataSets.get(i); String code = dataSet.getCode(); File file = new File(dataSetsDir, code); if (file.exists() == false) @@ -788,8 +863,9 @@ public class OpenBISScreeningML dataSet.getLinkOrCopyOfContents(overrideStoreRootPathOrNull, dataSetsDir); } + Object[][] dataSetProperties = listProperties(dataSet.getProperties()); result[i] = new Object[] - { code, file.getPath() }; + { code, file.getPath(), dataSetProperties }; } return result; } catch (Exception ex) @@ -1870,6 +1946,24 @@ public class OpenBISScreeningML return result; } + private static Object[][] listProperties(Map<String, String> properties) + { + if (properties == null || properties.isEmpty()) + { + return new Object[0][]; + } + + List<Map.Entry<String, String>> list = + new ArrayList<Map.Entry<String, String>>(properties.entrySet()); + Object[][] result = new Object[list.size()][2]; + for (int i = 0; i < list.size(); i++) + { + result[i] = new Object[] + { list.get(i).getKey(), list.get(i).getValue() }; + } + return result; + } + private static Plate getPlate(String augmentedPlateCode) { Plate plateIdentifier = plateCodeToPlateMap.get(augmentedPlateCode); @@ -1881,6 +1975,17 @@ public class OpenBISScreeningML return plateIdentifier; } + private static ExperimentIdentifier getExperimentIdentifierOrFail(String experiment) + { + ExperimentIdentifier experimentIdentifier = experimentCodeToExperimentMap.get(experiment); + if (experimentIdentifier == null) + { + String errorMessage = String.format("No experiment with code '%s' found.", experiment); + throw new RuntimeException(errorMessage); + } + return experimentIdentifier; + } + private static String createPlateWellDescription(PlateIdentifier p, FeatureVector f) { return createPlateWellDescription(p, f.getWellPosition().getWellRow(), f.getWellPosition() @@ -1957,4 +2062,5 @@ public class OpenBISScreeningML } } } + } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java index 5565eb0ba56..b029222220d 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java @@ -23,6 +23,7 @@ import java.util.Map; import ch.systemsx.cisd.base.image.IImageTransformerFactory; import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet; import ch.systemsx.cisd.openbis.dss.client.api.v1.IDataSetDss; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetMetadataDTO; import ch.systemsx.cisd.openbis.dss.screening.shared.api.v1.LoadImageConfiguration; @@ -261,6 +262,18 @@ public interface IScreeningOpenbisServiceFacade public List<IDataSetDss> getDataSets(PlateIdentifier plateIdentifier, IDataSetFilter dataSetFilter) throws IllegalStateException, EnvironmentFailureException; + /** + * A list of data sets owned by specified plate and passing specified filter. The data set + * objects provide metadata (e.g. code, properties etc. from the openBIS AS) as well as data + * (e.g. files from from openBIS DSS). + * + * @throws IllegalStateException Thrown if the user has not yet been authenticated. + * @throws EnvironmentFailureException Thrown in cases where it is not possible to connect to + * the server. + */ + public List<DataSet> getFullDataSets(PlateIdentifier plateIdentifier, + IDataSetFilter dataSetFilter) throws IllegalStateException, EnvironmentFailureException; + /** * Upload a new data set to the DSS for a plate. * diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java index 04c5c2f8958..6e5f803c6d0 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java @@ -558,6 +558,36 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa return result; } + public List<ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet> getFullDataSets( + PlateIdentifier plateIdentifier, + IDataSetFilter dataSetFilter) throws IllegalStateException, EnvironmentFailureException + { + checkASMinimalMinorVersion("getPlateSample", PlateIdentifier.class); + Sample sample = openbisScreeningServer.getPlateSample(sessionToken, plateIdentifier); + return getFullDataSets(sample, dataSetFilter); + } + + private List<ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet> getFullDataSets( + final Sample sample, IDataSetFilter filter) + { + final List<DataSet> dataSets = + generalInformationService.listDataSetsForSample(sessionToken, sample, true); + final List<ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet> result = + new ArrayList<ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet>(); + for (DataSet dataSet : dataSets) + { + if (filter.pass(dataSet)) + { + ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet fullDataset = + new ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet( + openbisServiceFacade, dssComponent, dataSet, null); + + result.add(fullDataset); + } + } + return result; + } + /** * Upload a new data set to the DSS for a well. * diff --git a/screening/sourceTest/java/OpenBISScreeningMLTest.java b/screening/sourceTest/java/OpenBISScreeningMLTest.java index d0b2daea5b5..11df591f5b2 100644 --- a/screening/sourceTest/java/OpenBISScreeningMLTest.java +++ b/screening/sourceTest/java/OpenBISScreeningMLTest.java @@ -23,9 +23,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; @@ -40,10 +42,14 @@ import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase; import ch.systemsx.cisd.common.filesystem.FileUtilities; import ch.systemsx.cisd.common.test.RecordingMatcher; +import ch.systemsx.cisd.openbis.dss.client.api.v1.DataSet; import ch.systemsx.cisd.openbis.dss.client.api.v1.IDataSetDss; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetMetadataDTO; import ch.systemsx.cisd.openbis.generic.client.cli.Login; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet.DataSetInitializer; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.EntityRegistrationDetails; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.EntityRegistrationDetails.EntityRegistrationDetailsInitializer; import ch.systemsx.cisd.openbis.generic.shared.api.v1.filter.IDataSetFilter; import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.IScreeningOpenbisServiceFacade; import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.IScreeningOpenbisServiceFacadeFactory; @@ -300,7 +306,6 @@ public class OpenBISScreeningMLTest extends AbstractFileSystemTestCase Object[][] plates = OpenBISScreeningML.listPlates("/S/P/E1"); - sort(plates); assertEquals("/S/PLATE-1", plates[0][0]); assertEquals("s-1", plates[0][1]); assertEquals("S", plates[0][2]); @@ -314,6 +319,35 @@ public class OpenBISScreeningMLTest extends AbstractFileSystemTestCase context.assertIsSatisfied(); } + @Test + public void testListPlatesByExperimentAndAnalysisProcedure() + { + + final String analysisProcedure = "PROCEDURE"; + + context.checking(new Expectations() + { + { + one(openbis).listPlates(eId1, analysisProcedure); + will(returnValue(Arrays.asList(p1))); + } + }); + + Object[][] plates = OpenBISScreeningML.listPlates("/S/P/E1", analysisProcedure); + + assertEquals("/S/PLATE-1", plates[0][0]); + assertEquals("s-1", plates[0][1]); + assertEquals("S", plates[0][2]); + assertEquals("PLATE-1", plates[0][3]); + assertEquals("/S/P/E1", plates[0][4]); + assertEquals("e-1", plates[0][5]); + assertEquals("S", plates[0][6]); + assertEquals("P", plates[0][7]); + assertEquals("E1", plates[0][8]); + assertEquals(1, plates.length); + context.assertIsSatisfied(); + } + @Test public void testListFeatures() { @@ -607,21 +641,20 @@ public class OpenBISScreeningMLTest extends AbstractFileSystemTestCase final String datasetTypePattern = "blablaCode"; final String mountPoint = "/mount/openbis/store"; final RecordingMatcher<IDataSetFilter> filterMatcher = new RecordingMatcher<IDataSetFilter>(); + + final DataSet dataSet1 = createDataSet("ds-1", ds1); + final DataSet dataSet2 = createDataSet("ds-2", ds2); + context.checking(new Expectations() { { - one(openbis).getDataSets(with(new Plate("PLATE-1", "S", "s-1", eId1)), + one(openbis).getFullDataSets(with(new Plate("PLATE-1", "S", "s-1", eId1)), with(filterMatcher)); - will(returnValue(Arrays.asList(ds1, ds2))); - - one(ds1).getCode(); - will(returnValue("ds-1")); + will(returnValue(Arrays.asList(dataSet1, dataSet2))); one(ds1).getLinkOrCopyOfContents(mountPoint, dataSetFolder); will(returnValue(ds1Folder)); - one(ds2).getCode(); - will(returnValue("ds-2")); } }); @@ -631,8 +664,10 @@ public class OpenBISScreeningMLTest extends AbstractFileSystemTestCase assertEquals("Type:blablaCode AND Properties:[]", filterMatcher.recordedObject().toString()); assertEquals("ds-1", result[0][0]); assertEquals(ds1Folder.getPath(), result[0][1]); + assertEqualProperties(dataSet1.getProperties(), (Object[][]) result[0][2]); assertEquals("ds-2", result[1][0]); assertEquals(ds2Folder.getPath(), result[1][1]); + assertEqualProperties(dataSet2.getProperties(), (Object[][]) result[1][2]); assertEquals(2, result.length); context.assertIsSatisfied(); } @@ -641,40 +676,46 @@ public class OpenBISScreeningMLTest extends AbstractFileSystemTestCase public void testLoadDataSetsFilteredOnProperties() { final File dataSetFolder = - new File(OpenBISScreeningML.tempDir, OpenBISScreeningML.DATASETS_FOLDER); + new File(OpenBISScreeningML.tempDir, OpenBISScreeningML.DATASETS_FOLDER); final File ds1Folder = new File(dataSetFolder, "ds-1"); File ds2Folder = new File(dataSetFolder, "ds-2"); ds2Folder.mkdirs(); final String datasetTypePattern = "blablaCode"; final String mountPoint = "/mount/openbis/store"; - final RecordingMatcher<IDataSetFilter> filterMatcher = new RecordingMatcher<IDataSetFilter>(); + final RecordingMatcher<IDataSetFilter> filterMatcher = + new RecordingMatcher<IDataSetFilter>(); + + final DataSet dataSet1 = createDataSet("ds-1", ds1); + final DataSet dataSet2 = createDataSet("ds-2", ds2); + context.checking(new Expectations() - { { - one(openbis).getDataSets(with(new Plate("PLATE-1", "S", "s-1", eId1)), - with(filterMatcher)); - will(returnValue(Arrays.asList(ds1, ds2))); - - one(ds1).getCode(); - will(returnValue("ds-1")); - - one(ds1).getLinkOrCopyOfContents(mountPoint, dataSetFolder); - will(returnValue(ds1Folder)); - - one(ds2).getCode(); - will(returnValue("ds-2")); - } - }); - - Object[][] properties = new Object[][] {new Object[] {"a", "alpha"}, new Object[] {"b", "beta"}}; + { + one(openbis).getFullDataSets(with(new Plate("PLATE-1", "S", "s-1", eId1)), + with(filterMatcher)); + will(returnValue(Arrays.asList(dataSet1, dataSet2))); + + one(ds1).getLinkOrCopyOfContents(mountPoint, dataSetFolder); + will(returnValue(ds1Folder)); + } + }); + + Object[][] properties = new Object[][] + { new Object[] + { "a", "alpha" }, new Object[] + { "b", "beta" } }; Object[][] result = - OpenBISScreeningML.loadDataSets("/S/PLATE-1", datasetTypePattern, properties, mountPoint); - - assertEquals("Type:blablaCode AND Properties:[a=alpha, b=beta]", filterMatcher.recordedObject().toString()); + OpenBISScreeningML.loadDataSets("/S/PLATE-1", datasetTypePattern, properties, + mountPoint); + + assertEquals("Type:blablaCode AND Properties:[a=alpha, b=beta]", filterMatcher + .recordedObject().toString()); assertEquals("ds-1", result[0][0]); assertEquals(ds1Folder.getPath(), result[0][1]); + assertEqualProperties(dataSet1.getProperties(), (Object[][]) result[0][2]); assertEquals("ds-2", result[1][0]); assertEquals(ds2Folder.getPath(), result[1][1]); + assertEqualProperties(dataSet2.getProperties(), (Object[][]) result[1][2]); assertEquals(2, result.length); context.assertIsSatisfied(); } @@ -896,6 +937,44 @@ public class OpenBISScreeningMLTest extends AbstractFileSystemTestCase context.assertIsSatisfied(); } + @Test + public void testListAnalysisProcedures() + { + + final String[] analysisProcedures = new String[] + { "PROC-1", "PROC-2", "PROC-3" }; + + context.checking(new Expectations() + { + { + one(openbis).listAnalysisProcedures(eId1); + will(returnValue(Arrays.asList(analysisProcedures))); + } + }); + + Object[][] result = OpenBISScreeningML.listAnalysisProcedures("/S/P/E1"); + String[] returnedAnalysisProcedures = new String[result.length]; + for (int i = 0; i < result.length; i++) + { + returnedAnalysisProcedures[i] = (String) result[i][0]; + } + + assertEquals(Arrays.toString(analysisProcedures), + Arrays.toString(returnedAnalysisProcedures)); + context.assertIsSatisfied(); + } + + private void assertEqualProperties(Map<String, String> properties, Object[][] matlabProps) + { + TreeMap<String, String> expected = new TreeMap<String, String>(properties); + TreeMap<String, String> actual = new TreeMap<String, String>(); + for (Object[] prop : matlabProps) + { + actual.put((String) prop[0], (String) prop[1]); + } + assertEquals(expected.toString(), actual.toString()); + } + private String getImagePath(Object[][][] result, int i) { return result[0][i][0].toString().substring(tempDir.getPath().length()); @@ -912,4 +991,34 @@ public class OpenBISScreeningMLTest extends AbstractFileSystemTestCase }); } + private DataSet createDataSet(String code, IDataSetDss dataSetDss) + { + EntityRegistrationDetailsInitializer entityRegInitializer = + new EntityRegistrationDetailsInitializer(); + EntityRegistrationDetails regDetails = new EntityRegistrationDetails(entityRegInitializer); + + DataSetInitializer dsInitializer = new DataSetInitializer(); + dsInitializer.setRegistrationDetails(regDetails); + dsInitializer.setCode(code); + dsInitializer.setExperimentIdentifier("EXPERIMENT"); + dsInitializer.setDataSetTypeCode("ds-type"); + + Map<String, String> properties = createProperties(code); + for (String propKey : properties.keySet()) + { + dsInitializer.putProperty(propKey, properties.get(propKey)); + } + + ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet metadata = + new ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet(dsInitializer); + return new DataSet(null, null, metadata, dataSetDss); + } + + private Map<String, String> createProperties(String dataSetCode) + { + Map<String, String> properties = new HashMap<String, String>(); + properties.put(dataSetCode + "-key1", dataSetCode + "value1"); + properties.put(dataSetCode + "-key2", dataSetCode + "value2"); + return properties; + } } -- GitLab