From f86c027e6bd8c90d36832ffb0533cf9738af7e5c Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Mon, 14 Oct 2024 17:31:35 +0200 Subject: [PATCH 01/12] fix issue caused by mandatory assaytype --- .../life/qbic/model/OpenbisSeekTranslator.java | 7 +++++-- src/main/java/life/qbic/model/isa/ISAAssay.java | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/life/qbic/model/OpenbisSeekTranslator.java b/src/main/java/life/qbic/model/OpenbisSeekTranslator.java index 745b7d3..f83393c 100644 --- a/src/main/java/life/qbic/model/OpenbisSeekTranslator.java +++ b/src/main/java/life/qbic/model/OpenbisSeekTranslator.java @@ -160,8 +160,11 @@ public SeekStructure translate(OpenbisExperimentWithDescendants experiment, Experiment exp = experiment.getExperiment(); String expType = exp.getType().getCode(); String title = exp.getCode()+" ("+exp.getPermId().getPermId()+")"; - ISAAssay assay = new ISAAssay(title, STUDY_ID, experimentTypeToAssayClass.get(expType), - new URI(experimentTypeToAssayType.get(expType))); + ISAAssay assay = new ISAAssay(title, STUDY_ID, experimentTypeToAssayClass.get(expType)); + String assayType = experimentTypeToAssayType.get(expType); + if(assayType !=null && !assayType.isBlank()) { + assay.withAssayType(new URI(assayType)); + } SeekStructure result = new SeekStructure(assay, exp.getIdentifier().getIdentifier()); diff --git a/src/main/java/life/qbic/model/isa/ISAAssay.java b/src/main/java/life/qbic/model/isa/ISAAssay.java index 0de9e48..14a5ad5 100644 --- a/src/main/java/life/qbic/model/isa/ISAAssay.java +++ b/src/main/java/life/qbic/model/isa/ISAAssay.java @@ -7,9 +7,7 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -28,12 +26,17 @@ public class ISAAssay extends AbstractISAObject { private Relationships relationships; private String title; - public ISAAssay(String title, String studyId, String assayClass, URI assayType) { + public ISAAssay(String title, String studyId, String assayClass) { this.title = title; - this.attributes = new Attributes(title, assayClass, assayType); + this.attributes = new Attributes(title, assayClass); this.relationships = new Relationships(studyId); } + public ISAAssay withAssayType(URI assayType) { + this.attributes.withAssayType(assayType.toString()); + return this; + } + public ISAAssay withOtherCreators(String otherCreators) { this.attributes.otherCreators = otherCreators; return this; @@ -188,7 +191,7 @@ protected class Attributes { private AssayType assayType; private String otherCreators = ""; - public Attributes(String title, String assayClass, URI assayType) { + public Attributes(String title, String assayClass) { this.title = title; this.assayClass = new AssayClass(assayClass); this.assayType = new AssayType(assayType.toString()); @@ -222,6 +225,10 @@ public String getOther_creators() { return otherCreators; } + public void withAssayType(String assayType) { + this.assayType = new AssayType(assayType); + } + private class AssayClass { String key; From a6b814643019fd6a65a1dcc400849be89ff4e9df Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:01:48 +0200 Subject: [PATCH 02/12] enable search for samples and identifiers --- .../io/commandline/FindDatasetsCommand.java | 46 +++++++++++++++---- .../qbic/model/AssayWithQueuedAssets.java | 23 ---------- 2 files changed, 36 insertions(+), 33 deletions(-) delete mode 100644 src/main/java/life/qbic/model/AssayWithQueuedAssets.java diff --git a/src/main/java/life/qbic/io/commandline/FindDatasetsCommand.java b/src/main/java/life/qbic/io/commandline/FindDatasetsCommand.java index bb2072f..3f9f747 100644 --- a/src/main/java/life/qbic/io/commandline/FindDatasetsCommand.java +++ b/src/main/java/life/qbic/io/commandline/FindDatasetsCommand.java @@ -9,7 +9,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import life.qbic.App; @@ -24,15 +23,19 @@ description = "lists datasets and their details for a given experiment code") public class FindDatasetsCommand implements Runnable { - @Parameters(arity = "1", paramLabel = "experiment", description = "The code of the experiment data is attached to") - private String experimentCode; - @Option(arity = "1", paramLabel = "", description = "Optional openBIS spaces to filter samples", names = {"-s", "--space"}) + @Parameters(arity = "1", paramLabel = "openBIS obejct", description = + "The code of the experiment " + + "or sample data is attached to.") + private String objectCode; + @Option(arity = "1", paramLabel = "", description = "Optional openBIS spaces to filter " + + "found datasets by", names = {"-s", "--space"}) private String space; @Mixin OpenbisAuthenticationOptions auth = new OpenbisAuthenticationOptions(); - @Override + @Override public void run() { + App.readConfig(); List spaces = new ArrayList<>(); if (space != null) { System.out.println("Querying experiment in space: " + space + "..."); @@ -40,13 +43,36 @@ public void run() { } else { System.out.println("Querying experiment in all available spaces..."); } + if(objectCode.contains("/")) { + String[] splt = objectCode.split("/"); + objectCode = splt[splt.length-1]; + System.out.println("Query is not an object code, querying for: " + objectCode+" instead."); + } OpenBIS authentication = App.loginToOpenBIS(auth.getOpenbisPassword(), auth.getOpenbisUser(), auth.getOpenbisAS()); OpenbisConnector openbis = new OpenbisConnector(authentication); - List datasets = openbis.listDatasetsOfExperiment(spaces, experimentCode).stream() - .sorted(Comparator.comparing( - (DataSet d) -> d.getExperiment().getProject().getSpace().getCode())).collect( - Collectors.toList()); + + List datasetsOfExp = openbis.listDatasetsOfExperiment(spaces, objectCode).stream() + .sorted(Comparator.comparing( + (DataSet d) -> d.getExperiment().getProject().getSpace().getCode())) + .collect(Collectors.toList()); + + List datasets = new ArrayList<>(); + + if(!datasetsOfExp.isEmpty()) { + System.err.println("Found "+datasetsOfExp.size()+" datasets for experiment "+objectCode); + datasets.addAll(datasetsOfExp); + } + List datasetsOfSample = openbis.listDatasetsOfSample(spaces, objectCode).stream() + .sorted(Comparator.comparing( + (DataSet d) -> d.getExperiment().getProject().getSpace().getCode())) + .collect(Collectors.toList()); + + if(!datasetsOfSample.isEmpty()) { + System.err.println("Found "+datasetsOfSample.size()+" datasets for sample "+objectCode); + datasets.addAll(datasetsOfSample); + } + Map properties = new HashMap<>(); if (!datasets.isEmpty()) { Set patientIDs = openbis.findPropertiesInSampleHierarchy("PATIENT_DKFZ_ID", @@ -64,7 +90,7 @@ public void run() { }).collect(Collectors.toList()); int datasetIndex = 0; System.out.println(); - System.out.printf("Found %s datasets for experiment %s:%n", datasets.size(), experimentCode); + System.out.printf("Found %s datasets for %s:%n", datasets.size(), objectCode); for (DatasetWithProperties dataSet : datasetWithProperties) { datasetIndex++; System.out.println("["+datasetIndex+"]"); diff --git a/src/main/java/life/qbic/model/AssayWithQueuedAssets.java b/src/main/java/life/qbic/model/AssayWithQueuedAssets.java deleted file mode 100644 index 0186cfc..0000000 --- a/src/main/java/life/qbic/model/AssayWithQueuedAssets.java +++ /dev/null @@ -1,23 +0,0 @@ -package life.qbic.model; - -import java.util.List; -import life.qbic.model.download.SEEKConnector.AssetToUpload; - -public class AssayWithQueuedAssets { - - private String assayEndpoint; - private List assetsToUpload; - - public AssayWithQueuedAssets(String assayEndpoint, List assetsToUpload) { - this.assayEndpoint = assayEndpoint; - this.assetsToUpload = assetsToUpload; - } - - public String getAssayEndpoint() { - return assayEndpoint; - } - - public List getAssets() { - return assetsToUpload; - } -} From ba5c72800f08c8cb25b9c9466e506caccef1e278 Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:02:38 +0200 Subject: [PATCH 03/12] make assay type mandatory again --- src/main/java/life/qbic/model/isa/ISAAssay.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/life/qbic/model/isa/ISAAssay.java b/src/main/java/life/qbic/model/isa/ISAAssay.java index 14a5ad5..b632b55 100644 --- a/src/main/java/life/qbic/model/isa/ISAAssay.java +++ b/src/main/java/life/qbic/model/isa/ISAAssay.java @@ -26,17 +26,12 @@ public class ISAAssay extends AbstractISAObject { private Relationships relationships; private String title; - public ISAAssay(String title, String studyId, String assayClass) { + public ISAAssay(String title, String studyId, String assayClass, URI assayType) { this.title = title; - this.attributes = new Attributes(title, assayClass); + this.attributes = new Attributes(title, assayClass, assayType); this.relationships = new Relationships(studyId); } - public ISAAssay withAssayType(URI assayType) { - this.attributes.withAssayType(assayType.toString()); - return this; - } - public ISAAssay withOtherCreators(String otherCreators) { this.attributes.otherCreators = otherCreators; return this; @@ -191,7 +186,7 @@ protected class Attributes { private AssayType assayType; private String otherCreators = ""; - public Attributes(String title, String assayClass) { + public Attributes(String title, String assayClass, URI assayType) { this.title = title; this.assayClass = new AssayClass(assayClass); this.assayType = new AssayType(assayType.toString()); From c324b3c937621c5d836fa5e991bf5d129e89d6bb Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:03:29 +0200 Subject: [PATCH 04/12] fix check for config and command line parameters --- .../io/commandline/OpenbisAuthenticationOptions.java | 12 ++++++++++-- .../io/commandline/SeekAuthenticationOptions.java | 9 +++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/life/qbic/io/commandline/OpenbisAuthenticationOptions.java b/src/main/java/life/qbic/io/commandline/OpenbisAuthenticationOptions.java index 9228d0b..03a56f8 100644 --- a/src/main/java/life/qbic/io/commandline/OpenbisAuthenticationOptions.java +++ b/src/main/java/life/qbic/io/commandline/OpenbisAuthenticationOptions.java @@ -7,7 +7,6 @@ import java.net.URL; import java.util.StringJoiner; import life.qbic.App; -import life.qbic.io.PropertyReader; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import picocli.CommandLine; @@ -38,7 +37,8 @@ public class OpenbisAuthenticationOptions { public String getOpenbisUser() { if(openbisUser == null && App.configProperties.containsKey("user")) { openbisUser = App.configProperties.get("user"); - } else { + } + if(openbisUser == null) { log.error("No openBIS user provided."); System.exit(2); } @@ -49,6 +49,10 @@ public String getOpenbisDSS() { if(dss_url == null && App.configProperties.containsKey("dss")) { dss_url = App.configProperties.get("dss"); } + if(dss_url == null) { + log.error("No openBIS datastore server URL provided."); + System.exit(2); + } return dss_url; } @@ -56,6 +60,10 @@ public String getOpenbisAS() { if(as_url == null && App.configProperties.containsKey("as")) { as_url = App.configProperties.get("as"); } + if(as_url == null) { + log.error("No openBIS application server URL provided."); + System.exit(2); + } return as_url; } diff --git a/src/main/java/life/qbic/io/commandline/SeekAuthenticationOptions.java b/src/main/java/life/qbic/io/commandline/SeekAuthenticationOptions.java index 8a58d6e..545949f 100644 --- a/src/main/java/life/qbic/io/commandline/SeekAuthenticationOptions.java +++ b/src/main/java/life/qbic/io/commandline/SeekAuthenticationOptions.java @@ -5,7 +5,6 @@ import java.util.StringJoiner; import life.qbic.App; -import life.qbic.io.PropertyReader; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import picocli.CommandLine; @@ -31,7 +30,8 @@ public class SeekAuthenticationOptions { public String getSeekUser() { if(seekUser == null && App.configProperties.containsKey("seek_user")) { seekUser = App.configProperties.get("seek_user"); - } else { + } + if (seekUser == null) { log.error("No SEEK user/email provided."); System.exit(2); } @@ -39,9 +39,10 @@ public String getSeekUser() { } public String getSeekURL() { - if(seek_url == null && App.configProperties.containsKey("seek_url")) { + if(seek_url != null || App.configProperties.containsKey("seek_url")) { seek_url = App.configProperties.get("seek_url"); - } else { + } + if(seek_url == null) { log.error("No URL to the SEEK address provided."); System.exit(2); } From 19d580a3a992f0a3404a1e06c695f7ebd7aab2a0 Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:04:13 +0200 Subject: [PATCH 05/12] add checks for assay type translation, enable translation without experiment --- .../qbic/model/OpenbisSeekTranslator.java | 74 ++++++++++++++++++- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/src/main/java/life/qbic/model/OpenbisSeekTranslator.java b/src/main/java/life/qbic/model/OpenbisSeekTranslator.java index f83393c..e1fc762 100644 --- a/src/main/java/life/qbic/model/OpenbisSeekTranslator.java +++ b/src/main/java/life/qbic/model/OpenbisSeekTranslator.java @@ -32,6 +32,7 @@ import life.qbic.model.isa.ISASampleType.SampleAttribute; import life.qbic.model.isa.ISASampleType.SampleAttributeType; import life.qbic.model.isa.SeekStructure; +import org.apache.commons.lang3.tuple.Pair; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -160,11 +161,14 @@ public SeekStructure translate(OpenbisExperimentWithDescendants experiment, Experiment exp = experiment.getExperiment(); String expType = exp.getType().getCode(); String title = exp.getCode()+" ("+exp.getPermId().getPermId()+")"; - ISAAssay assay = new ISAAssay(title, STUDY_ID, experimentTypeToAssayClass.get(expType)); String assayType = experimentTypeToAssayType.get(expType); - if(assayType !=null && !assayType.isBlank()) { - assay.withAssayType(new URI(assayType)); + + if(assayType ==null || assayType.isBlank()) { + throw new RuntimeException("Could not find assay type for " + expType+". A mapping needs to " + + "be added to the respective properties file."); } + ISAAssay assay = new ISAAssay(title, STUDY_ID, experimentTypeToAssayClass.get(expType), + new URI(assayType)); SeekStructure result = new SeekStructure(assay, exp.getIdentifier().getIdentifier()); @@ -214,6 +218,69 @@ public SeekStructure translate(OpenbisExperimentWithDescendants experiment, return result; } + public SeekStructure translate(OpenbisSampleWithDatasets sampleWithDatasets, + Map sampleTypesToIds, Set blacklist, boolean transferData) { + Sample sample = sampleWithDatasets.getSample(); + SampleType sampleType = sample.getType(); + //try to put all attributes into sample properties, as they should be a 1:1 mapping + Map typeCodesToNames = new HashMap<>(); + Set propertiesLinkingSamples = new HashSet<>(); + for (PropertyAssignment a : sampleType.getPropertyAssignments()) { + String code = a.getPropertyType().getCode(); + String label = a.getPropertyType().getLabel(); + DataType type = a.getPropertyType().getDataType(); + typeCodesToNames.put(code, label); + if (type.equals(DataType.SAMPLE)) { + propertiesLinkingSamples.add(code); + } + } + Map attributes = new HashMap<>(); + for (String code : sample.getProperties().keySet()) { + String value = sample.getProperty(code); + if (propertiesLinkingSamples.contains(code)) { + value = generateOpenBISLinkFromPermID("SAMPLE", value); + } + attributes.put(typeCodesToNames.get(code), value); + } + + String sampleID = sample.getIdentifier().getIdentifier(); + attributes.put(App.configProperties.get("seek_openbis_sample_title"), sampleID); + String sampleTypeId = sampleTypesToIds.get(sampleType.getCode()); + ISASample isaSample = new ISASample(sample.getIdentifier().getIdentifier(), attributes, + sampleTypeId, Collections.singletonList(DEFAULT_PROJECT_ID)); + SeekStructure result = new SeekStructure(isaSample, sampleID); + + //create ISA files for assets. If actual data is to be uploaded is determined later based on flag + List datasets = sampleWithDatasets.getDatasets(); + for (DatasetWithProperties dataset : datasets) { + String permID = dataset.getCode(); + if (!blacklist.contains(permID)) { + for (DataSetFile file : sampleWithDatasets.getFilesForDataset(permID)) { + String datasetType = getDatasetTypeOfFile(file, datasets); + datasetFileToSeekAsset(file, datasetType, transferData) + .ifPresent(seekAsset -> result.addAsset(seekAsset, file)); + } + } + } + return result; + } + + public SeekStructure translate(Pair> datasetWithFiles, + Set blacklist, boolean transferData) { + Map assetToDatasetFiles = new HashMap<>(); + DatasetWithProperties dataset = datasetWithFiles.getLeft(); + String permID = dataset.getCode(); + String datasetType = dataset.getType().getCode(); + + if (!blacklist.contains(permID)) { + for (DataSetFile file : datasetWithFiles.getRight()) { + datasetFileToSeekAsset(file, datasetType, transferData) + .ifPresent(seekAsset -> assetToDatasetFiles.put(seekAsset, file)); + } + } + return new SeekStructure(assetToDatasetFiles); + } + private String getDatasetTypeOfFile(DataSetFile file, List dataSets) { String permId = file.getDataSetPermId().getPermId(); for(DatasetWithProperties dataset : dataSets) { @@ -260,4 +327,5 @@ public void setDefaultStudy(String studyID) { public void setDefaultInvestigation(String investigationID) { this.INVESTIGATION_ID = investigationID; } + } From a9446b233fa5425100932f80de3cc20463cbe14f Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:05:45 +0200 Subject: [PATCH 06/12] fix recursion for list of parent properties --- .../qbic/model/download/OpenbisConnector.java | 91 +++++++++++++++---- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/src/main/java/life/qbic/model/download/OpenbisConnector.java b/src/main/java/life/qbic/model/download/OpenbisConnector.java index 386e494..f4906d6 100644 --- a/src/main/java/life/qbic/model/download/OpenbisConnector.java +++ b/src/main/java/life/qbic/model/download/OpenbisConnector.java @@ -47,14 +47,15 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import life.qbic.model.DatasetWithProperties; import life.qbic.model.OpenbisExperimentWithDescendants; +import life.qbic.model.OpenbisSampleWithDatasets; import life.qbic.model.SampleTypeConnection; import life.qbic.model.SampleTypesAndMaterials; import life.qbic.model.download.SEEKConnector.SeekStructurePostRegistrationInformation; +import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -81,8 +82,8 @@ public List getSpaces() { } public DataSetPermId registerDatasetForExperiment(Path uploadPath, String experimentID, - List parentCodes) { - UploadedDataSetCreation creation = prepareDataSetCreation(uploadPath, parentCodes); + String datasetType, List parentCodes) { + UploadedDataSetCreation creation = prepareDataSetCreation(uploadPath, datasetType, parentCodes); creation.setExperimentId(new ExperimentIdentifier(experimentID)); try { @@ -94,8 +95,8 @@ public DataSetPermId registerDatasetForExperiment(Path uploadPath, String experi } public DataSetPermId registerDatasetForSample(Path uploadPath, String sampleID, - List parentCodes) { - UploadedDataSetCreation creation = prepareDataSetCreation(uploadPath, parentCodes); + String datasetType, List parentCodes) { + UploadedDataSetCreation creation = prepareDataSetCreation(uploadPath, datasetType, parentCodes); creation.setSampleId(new SampleIdentifier(sampleID)); try { @@ -106,14 +107,15 @@ public DataSetPermId registerDatasetForSample(Path uploadPath, String sampleID, return null; } - private UploadedDataSetCreation prepareDataSetCreation(Path uploadPath, List parentCodes) { + private UploadedDataSetCreation prepareDataSetCreation(Path uploadPath, String datasetType, + List parentCodes) { final String uploadId = openBIS.uploadFileWorkspaceDSS(uploadPath); final UploadedDataSetCreation creation = new UploadedDataSetCreation(); creation.setUploadId(uploadId); creation.setParentIds(parentCodes.stream().map(DataSetPermId::new).collect( Collectors.toList())); - creation.setTypeId(new EntityTypePermId("UNKNOWN", EntityKind.DATA_SET)); + creation.setTypeId(new EntityTypePermId(datasetType, EntityKind.DATA_SET)); return creation; } @@ -143,6 +145,20 @@ public List listDatasetsOfExperiment(List spaces, String experi return openBIS.searchDataSets(criteria, options).getObjects(); } + public List listDatasetsOfSample(List spaces, String sample) { + DataSetSearchCriteria criteria = new DataSetSearchCriteria(); + criteria.withSample().withCode().thatEquals(sample); + if (!spaces.isEmpty()) { + criteria.withAndOperator(); + criteria.withExperiment().withProject().withSpace().withCodes().thatIn(spaces); + } + DataSetFetchOptions options = new DataSetFetchOptions(); + options.withType(); + options.withRegistrator(); + options.withExperiment().withProject().withSpace(); + return openBIS.searchDataSets(criteria, options).getObjects(); + } + public File downloadDataset(String targetPath, String datasetID, String filePath) { DataSetFileDownloadOptions options = new DataSetFileDownloadOptions(); IDataSetFileId fileToDownload = new DataSetFilePermId(new DataSetPermId(datasetID), @@ -238,14 +254,17 @@ public Map queryFullSampleHierarchy(List private Set getPropertiesFromSampleHierarchy(String propertyName, List samples, Set foundProperties) { + if(samples.isEmpty()) { + return foundProperties; + } for(Sample s : samples) { if(s.getProperties().containsKey(propertyName)) { foundProperties.add(s.getProperties().get(propertyName)); - return foundProperties; } - return getPropertiesFromSampleHierarchy(propertyName, s.getParents(), foundProperties); } - return foundProperties; + return getPropertiesFromSampleHierarchy(propertyName, + samples.stream().map(Sample::getParents).flatMap(List::stream).collect(Collectors.toList()), + foundProperties); } public Set findPropertiesInSampleHierarchy(String propertyName, @@ -502,13 +521,15 @@ public SampleTypesAndMaterials getSampleTypesWithMaterials() { return new SampleTypesAndMaterials(sampleTypes, sampleTypesAsMaterials); } - public void createSeekLinks(SeekStructurePostRegistrationInformation postRegistrationInformation) { - Pair experimentInfo = postRegistrationInformation.getExperimentIDWithEndpoint(); - ExperimentIdentifier id = new ExperimentIdentifier(experimentInfo.getLeft()); - String endpoint = experimentInfo.getRight(); - Map props = new HashMap<>(); - props.put(EXPERIMENT_LINK_PROPERTY, endpoint); - updateExperimentProperties(id, props, false); + public void createSeekLinks(SeekStructurePostRegistrationInformation postRegInformation) { + Optional> experimentInfo = postRegInformation.getExperimentIDWithEndpoint(); + if(experimentInfo.isPresent()) { + ExperimentIdentifier id = new ExperimentIdentifier(experimentInfo.get().getLeft()); + String endpoint = experimentInfo.get().getRight(); + Map props = new HashMap<>(); + props.put(EXPERIMENT_LINK_PROPERTY, endpoint); + updateExperimentProperties(id, props, false); + } } public void updateSeekLinks(SeekStructurePostRegistrationInformation postRegistrationInformation) { @@ -583,4 +604,40 @@ private void updateDatasetProperties(DataSetPermId id, Map prope openBIS.updateDataSets(Arrays.asList(update)); } + public OpenbisSampleWithDatasets getSampleWithDatasets(String sampleID) { + SampleSearchCriteria criteria = new SampleSearchCriteria(); + criteria.withIdentifier().thatEquals(sampleID); + + DataSetFetchOptions dataSetFetchOptions = new DataSetFetchOptions(); + dataSetFetchOptions.withType(); + dataSetFetchOptions.withRegistrator(); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withType().withPropertyAssignments().withPropertyType(); + fetchOptions.withDataSetsUsing(dataSetFetchOptions); + fetchOptions.withDataSetsUsing(dataSetFetchOptions); + + Sample sample = openBIS.searchSamples(criteria, fetchOptions).getObjects().get(0); + + List datasets = new ArrayList<>(); + Map> datasetCodeToFiles = new HashMap<>(); + for(DataSet dataset : sample.getDataSets()) { + datasets.add(new DatasetWithProperties(dataset)); + datasetCodeToFiles.put(dataset.getPermId().getPermId(), getDatasetFiles(dataset)); + } + + return new OpenbisSampleWithDatasets(sample, datasets, datasetCodeToFiles); + } + + public Pair> getDataSetWithFiles(String datasetID) { + DataSetSearchCriteria criteria = new DataSetSearchCriteria(); + criteria.withPermId().thatEquals(datasetID); + + DataSetFetchOptions dataSetFetchOptions = new DataSetFetchOptions(); + dataSetFetchOptions.withType(); + dataSetFetchOptions.withRegistrator(); + + DataSet dataset = openBIS.searchDataSets(criteria, dataSetFetchOptions).getObjects().get(0); + return new ImmutablePair<>(new DatasetWithProperties(dataset), getDatasetFiles(dataset)); + } } From 983ff91f23ca5aebcea3d7bcfc24b3e746c795e0 Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:06:50 +0200 Subject: [PATCH 07/12] add dataset type as optional parameter --- .../io/commandline/UploadDatasetCommand.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/life/qbic/io/commandline/UploadDatasetCommand.java b/src/main/java/life/qbic/io/commandline/UploadDatasetCommand.java index 98e9f11..c9756fd 100644 --- a/src/main/java/life/qbic/io/commandline/UploadDatasetCommand.java +++ b/src/main/java/life/qbic/io/commandline/UploadDatasetCommand.java @@ -25,6 +25,9 @@ public class UploadDatasetCommand implements Runnable { @Option(arity = "1..*", paramLabel = "", description = "Optional list of dataset codes to act" + " as parents for the upload. E.g. when this dataset has been generated using these datasets as input.", names = {"-pa", "--parents"}) private List parents = new ArrayList<>(); + @Option(arity = "1", paramLabel = "dataset type", description = "The openBIS dataset type code the " + + "data should be stored as. UNKNOWN if no type is chosen.", names = {"-t", "--type"}) + private String datasetType = "UNKNOWN"; @Mixin OpenbisAuthenticationOptions auth = new OpenbisAuthenticationOptions(); @@ -32,6 +35,8 @@ public class UploadDatasetCommand implements Runnable { @Override public void run() { + App.readConfig(); + OpenBIS authentication = App.loginToOpenBIS(auth.getOpenbisPassword(), auth.getOpenbisUser(), auth.getOpenbisAS(), auth.getOpenbisDSS()); openbis = new OpenbisConnector(authentication); @@ -40,8 +45,8 @@ public void run() { return; } boolean attachToSample = false; - boolean attachToExperiment = experimentExists(objectID); - if(sampleExists(objectID)) { + boolean attachToExperiment = openbis.experimentExists(objectID); + if(openbis.sampleExists(objectID)) { attachToSample = true; } if(!attachToSample && !attachToExperiment) { @@ -56,26 +61,20 @@ public void run() { System.out.println("Parameters verified, uploading dataset..."); System.out.println(); if(attachToExperiment) { - DataSetPermId result = openbis.registerDatasetForExperiment(Path.of(dataPath), objectID, parents); + DataSetPermId result = openbis.registerDatasetForExperiment(Path.of(dataPath), objectID, + datasetType, parents); System.out.printf("Dataset %s was successfully attached to experiment%n", result.getPermId()); } else { - DataSetPermId result = openbis.registerDatasetForSample(Path.of(dataPath), objectID, parents); + DataSetPermId result = openbis.registerDatasetForSample(Path.of(dataPath), objectID, + datasetType, parents); System.out.printf("Dataset %s was successfully attached to sample%n", result.getPermId()); } } - private boolean sampleExists(String objectID) { - return openbis.sampleExists(objectID); - } - private boolean datasetsExist(List datasetCodes) { return openbis.findDataSets(datasetCodes).size() == datasetCodes.size(); } - private boolean experimentExists(String experimentID) { - return openbis.experimentExists(experimentID); - } - private boolean pathValid(String dataPath) { return new File(dataPath).exists(); } From 2cbf1e6b1c1ac9f83f2bd15aaf38a4dd5b344f2b Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:09:50 +0200 Subject: [PATCH 08/12] fix issue where config was not read --- .../java/life/qbic/io/commandline/SampleHierarchyCommand.java | 1 + .../java/life/qbic/io/commandline/SpaceStatisticsCommand.java | 1 + .../qbic/io/commandline/TransferSampleTypesToSeekCommand.java | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/life/qbic/io/commandline/SampleHierarchyCommand.java b/src/main/java/life/qbic/io/commandline/SampleHierarchyCommand.java index 228d615..fbd66b8 100644 --- a/src/main/java/life/qbic/io/commandline/SampleHierarchyCommand.java +++ b/src/main/java/life/qbic/io/commandline/SampleHierarchyCommand.java @@ -35,6 +35,7 @@ public class SampleHierarchyCommand implements Runnable { @Override public void run() { + App.readConfig(); List summary = new ArrayList<>(); List spaces = new ArrayList<>(); if(space!=null) { diff --git a/src/main/java/life/qbic/io/commandline/SpaceStatisticsCommand.java b/src/main/java/life/qbic/io/commandline/SpaceStatisticsCommand.java index 4ae0323..5e66d24 100644 --- a/src/main/java/life/qbic/io/commandline/SpaceStatisticsCommand.java +++ b/src/main/java/life/qbic/io/commandline/SpaceStatisticsCommand.java @@ -41,6 +41,7 @@ public class SpaceStatisticsCommand implements Runnable { @Override public void run() { + App.readConfig(); List summary = new ArrayList<>(); List blackList = new ArrayList<>(Arrays.asList("ELN_SETTINGS", "MATERIAL.GLOBAL")); List spaces = new ArrayList<>(); diff --git a/src/main/java/life/qbic/io/commandline/TransferSampleTypesToSeekCommand.java b/src/main/java/life/qbic/io/commandline/TransferSampleTypesToSeekCommand.java index 3ba3d55..53eecd1 100644 --- a/src/main/java/life/qbic/io/commandline/TransferSampleTypesToSeekCommand.java +++ b/src/main/java/life/qbic/io/commandline/TransferSampleTypesToSeekCommand.java @@ -6,7 +6,6 @@ import java.net.URISyntaxException; import javax.xml.parsers.ParserConfigurationException; import life.qbic.App; -import life.qbic.io.PropertyReader; import life.qbic.model.OpenbisSeekTranslator; import life.qbic.model.SampleTypesAndMaterials; import life.qbic.model.download.OpenbisConnector; @@ -35,6 +34,8 @@ public class TransferSampleTypesToSeekCommand implements Runnable { @Override public void run() { + App.readConfig(); + System.out.println("auth..."); OpenBIS authentication = App.loginToOpenBIS(openbisAuth.getOpenbisPassword(), From 9bdf010b6185f4250f2de1c38b567c9f85cff531 Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:10:32 +0200 Subject: [PATCH 09/12] add optional parameter for dataset type --- .../commandline/UploadPetabResultCommand.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/java/life/qbic/io/commandline/UploadPetabResultCommand.java b/src/main/java/life/qbic/io/commandline/UploadPetabResultCommand.java index 80d40bc..bffdc1e 100644 --- a/src/main/java/life/qbic/io/commandline/UploadPetabResultCommand.java +++ b/src/main/java/life/qbic/io/commandline/UploadPetabResultCommand.java @@ -11,20 +11,24 @@ import life.qbic.model.download.OpenbisConnector; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; +import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; @Command(name = "upload-petab", - description = "uploads a PETab folder and attaches it to a provided experiment and any datasets referenced in the PETab metadata (e.g. for PETab results).") + description = "uploads a PETab folder and attaches it to a provided experiment and any datasets " + + "referenced in the PETab metadata (e.g. for PETab results).") public class UploadPetabResultCommand implements Runnable { - @Parameters(arity = "1", paramLabel = "file/folder", description = "The path to the file or folder to upload") + @Parameters(arity = "1", paramLabel = "file/folder", description = "The path to the file or folder " + + "to upload") private String dataPath; - @Parameters(arity = "1", paramLabel = "experiment ID", description = "The full identifier of the experiment the data should be attached to. " + @Parameters(arity = "1", paramLabel = "experiment ID", description = "The full identifier of the " + + "+experiment the data should be attached to. " + "The identifier must be of the format: /space/project/experiment") private String experimentID; - //@Option(arity = "1..*", paramLabel = "", description = "Optional list of dataset codes to act" - // + " as parents for the upload. E.g. when this dataset has been generated using these datasets as input.", names = {"-pa", "--parents"}) - private List parents = new ArrayList<>(); + @Option(arity = "1", paramLabel = "dataset type", description = "The openBIS dataset type code the " + + "data should be stored as. UNKNOWN if no type is chosen.", names = {"-t", "--type"}) + private String datasetType = "UNKNOWN"; @Mixin OpenbisAuthenticationOptions auth = new OpenbisAuthenticationOptions(); @@ -33,6 +37,8 @@ public class UploadPetabResultCommand implements Runnable { @Override public void run() { + App.readConfig(); + OpenBIS authentication = App.loginToOpenBIS(auth.getOpenbisPassword(), auth.getOpenbisUser(), auth.getOpenbisAS(), auth.getOpenbisDSS()); openbis = new OpenbisConnector(authentication); @@ -49,9 +55,9 @@ public void run() { return; } System.out.println("Looking for reference datasets in metaInformation.yaml..."); - parents = petabParser.parse(dataPath).getSourcePetabReferences(); + List parents = petabParser.parse(dataPath).getSourcePetabReferences(); if(parents.isEmpty()) { - System.out.println("No reference datasets found in openBISSourceIds property. Assuming" + System.out.println("No reference datasets found in openBISSourceIds property. Assuming " + "this is a new dataset."); } else { System.out.println("Found reference ids: " + String.join(", ", parents)); @@ -63,7 +69,8 @@ public void run() { } } System.out.println("Uploading dataset..."); - DataSetPermId result = openbis.registerDatasetForExperiment(Path.of(dataPath), experimentID, parents); + DataSetPermId result = openbis.registerDatasetForExperiment(Path.of(dataPath), experimentID, + datasetType, parents); System.out.printf("Dataset %s was successfully created%n", result.getPermId()); } From 27eac030bc746d1f0ce77ef1e69adddce22d8714 Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:11:03 +0200 Subject: [PATCH 10/12] fix issue where config was not read --- .../java/life/qbic/io/commandline/DownloadPetabCommand.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/life/qbic/io/commandline/DownloadPetabCommand.java b/src/main/java/life/qbic/io/commandline/DownloadPetabCommand.java index a80f861..5deac91 100644 --- a/src/main/java/life/qbic/io/commandline/DownloadPetabCommand.java +++ b/src/main/java/life/qbic/io/commandline/DownloadPetabCommand.java @@ -28,6 +28,7 @@ public class DownloadPetabCommand implements Runnable { @Override public void run() { + App.readConfig(); OpenBIS authentication = App.loginToOpenBIS(auth.getOpenbisPassword(), auth.getOpenbisUser(), auth.getOpenbisAS(), auth.getOpenbisDSS()); OpenbisConnector openbis = new OpenbisConnector(authentication); @@ -35,7 +36,7 @@ public void run() { List datasets = openbis.findDataSets(Collections.singletonList(datasetCode)); if(datasets.isEmpty()) { - System.out.println(datasetCode+" not found"); + System.out.println("Dataset "+datasetCode+" not found"); return; } DatasetWithProperties result = new DatasetWithProperties(datasets.get(0)); @@ -54,6 +55,7 @@ public void run() { try { System.out.println("Adding dataset identifier to metaInformation.yaml."); parser.addDatasetId(outputPath, datasetCode); + parser.addPatientIDs(outputPath, patientIDs); } catch (IOException e) { System.out.println("Could not add dataset identifier."); throw new RuntimeException(e); From 705877563601e8f63eb514308f1c3a0cabf77988 Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:11:47 +0200 Subject: [PATCH 11/12] add test logging of patient id property --- src/main/java/life/qbic/io/PetabParser.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/life/qbic/io/PetabParser.java b/src/main/java/life/qbic/io/PetabParser.java index 220e41b..25461ed 100644 --- a/src/main/java/life/qbic/io/PetabParser.java +++ b/src/main/java/life/qbic/io/PetabParser.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import life.qbic.model.petab.PetabMetadata; public class PetabParser { @@ -94,4 +95,7 @@ private File findYaml(File directory) { return null; } -} \ No newline at end of file + public void addPatientIDs(String outputPath, Set patientIDs) { + System.err.println("found patient ids: "+patientIDs); + } +} From dd3000a1ae6c855e3820eb224793516fc112ac48 Mon Sep 17 00:00:00 2001 From: wow-such-code Date: Tue, 15 Oct 2024 02:12:42 +0200 Subject: [PATCH 12/12] add partial functionality to add and update samples and assets without assay (experiment) information --- .../TransferDataToSeekCommand.java | 256 +++++++++++++----- .../qbic/model/OpenbisSampleWithDatasets.java | 32 +++ .../qbic/model/download/SEEKConnector.java | 241 +++++++++++++++-- .../life/qbic/model/isa/SeekStructure.java | 19 +- 4 files changed, 455 insertions(+), 93 deletions(-) create mode 100644 src/main/java/life/qbic/model/OpenbisSampleWithDatasets.java diff --git a/src/main/java/life/qbic/io/commandline/TransferDataToSeekCommand.java b/src/main/java/life/qbic/io/commandline/TransferDataToSeekCommand.java index 4160d45..7c39790 100644 --- a/src/main/java/life/qbic/io/commandline/TransferDataToSeekCommand.java +++ b/src/main/java/life/qbic/io/commandline/TransferDataToSeekCommand.java @@ -2,6 +2,8 @@ import ch.ethz.sis.openbis.generic.OpenBIS; import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample; +import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; @@ -17,15 +19,18 @@ import java.util.stream.Stream; import javax.xml.parsers.ParserConfigurationException; import life.qbic.App; -import life.qbic.model.AssayWithQueuedAssets; +import life.qbic.model.DatasetWithProperties; import life.qbic.model.OpenbisExperimentWithDescendants; +import life.qbic.model.OpenbisSampleWithDatasets; import life.qbic.model.OpenbisSeekTranslator; import life.qbic.model.download.SEEKConnector.SeekStructurePostRegistrationInformation; +import life.qbic.model.isa.GenericSeekAsset; import life.qbic.model.isa.SeekStructure; import life.qbic.model.download.OpenbisConnector; import life.qbic.model.download.SEEKConnector; import life.qbic.model.download.SEEKConnector.AssetToUpload; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.tuple.Pair; import org.xml.sax.SAXException; import picocli.CommandLine.Command; import picocli.CommandLine.Mixin; @@ -48,8 +53,8 @@ public class TransferDataToSeekCommand implements Runnable { @Parameters(arity = "1", paramLabel = "openbis id", description = "The identifier of the " + "experiment, sample or dataset to transfer.") private String objectID; - @Parameters(arity = "1", paramLabel = "seek study", description = "Title of the study in SEEK" - + "where nodes should be added.") + @Option(names = "--seek-study", description = "Title of the study in SEEK where nodes should be " + + "added. Mandatory for assay creation.") private String studyTitle; @Option(names = "--seek-project", description = "Title of the project in SEEK where nodes should" + "be added. Can alternatively be provided via the config file as 'seek_default_project'.") @@ -87,7 +92,6 @@ public void run() { } else { System.out.printf("No SEEK project title provided, will search config file.%n"); } - System.out.printf("Provided SEEK study title: %s%n", studyTitle); System.out.printf("Transfer datasets to SEEK? %s%n", transferData); System.out.printf("Update existing assay if found? %s%n", !noUpdate); if(blacklistFile!=null && !blacklistFile.isBlank()) { @@ -99,7 +103,7 @@ public void run() { OpenBIS authentication = App.loginToOpenBIS(openbisAuth.getOpenbisPassword(), openbisAuth.getOpenbisUser(), openbisAuth.getOpenbisAS(), openbisAuth.getOpenbisDSS()); - openbis = new OpenbisConnector(authentication); + this.openbis = new OpenbisConnector(authentication); boolean isSample = false; boolean isDataSet = false; @@ -107,6 +111,13 @@ public void run() { System.out.println("Searching for specified object in openBIS..."); boolean isExperiment = experimentExists(objectID); + + if (isExperiment && (studyTitle == null || studyTitle.isBlank())) { + System.out.printf( + "No SEEK study title was provided. This is mandatory if an openBIS experiment is to be transferred%n"); + return; + } + if (!isExperiment && sampleExists(objectID)) { isSample = true; } @@ -140,36 +151,130 @@ public void run() { ParserConfigurationException | SAXException e) { throw new RuntimeException(e); } - + SeekStructurePostRegistrationInformation postRegInfo; try { - System.out.println("Collecting information from openBIS..."); - OpenbisExperimentWithDescendants experiment = openbis.getExperimentWithDescendants(objectID); + if (isExperiment) { + postRegInfo = handleExperimentTransfer(); + } else if (isSample) { + postRegInfo = handleSampleTransfer(); + } else { + postRegInfo = handleDatasetTransfer(); + } + } catch (URISyntaxException | IOException | InterruptedException e) { + throw new RuntimeException(e); + } + + System.out.println("Creating links to new SEEK objects in openBIS..."); + openbis.createSeekLinks(postRegInfo); + + System.out.println("Done"); + } + + private SeekStructurePostRegistrationInformation handleExperimentTransfer() + throws URISyntaxException, IOException, InterruptedException { + System.out.println("Collecting information from openBIS..."); + OpenbisExperimentWithDescendants experiment = openbis.getExperimentWithDescendants(objectID); + Set blacklist = parseBlackList(blacklistFile); + System.out.println("Translating openBIS property codes to SEEK names..."); + Map sampleTypesToIds = seek.getSampleTypeNamesToIDs(); + System.out.println("Creating SEEK structure..."); + SeekStructure nodeWithChildren = translator.translate(experiment, sampleTypesToIds, blacklist, + transferData); + if (!noUpdate) { System.out.println("Trying to find existing corresponding assay in SEEK..."); Optional assayID = getAssayIDForOpenBISExperiment(experiment.getExperiment()); - assayID.ifPresent(x -> System.out.println("Found assay with id "+assayID.get())); - Set blacklist = parseBlackList(blacklistFile); - System.out.println("Translating openBIS property codes to SEEK names..."); - Map sampleTypesToIds = seek.getSampleTypeNamesToIDs(); - System.out.println("Creating SEEK structure..."); - SeekStructure nodeWithChildren = translator.translate(experiment, sampleTypesToIds, blacklist, - transferData); - - if(assayID.isEmpty() || noUpdate) { + assayID.ifPresent(x -> System.out.println("Found assay with id " + assayID.get())); + if (assayID.isEmpty()) { System.out.println("Creating new node(s)..."); - SeekStructurePostRegistrationInformation postRegInfo = createNewNodes(nodeWithChildren); - System.out.println("Creating links to SEEK objects in openBIS..."); - openbis.createSeekLinks(postRegInfo); + return createNewAssayStructure(nodeWithChildren); } else { System.out.println("Updating nodes..."); - SeekStructurePostRegistrationInformation postRegInfo = updateNodes(nodeWithChildren, - assayID.get()); - System.out.println("Updating links to SEEK objects in openBIS..."); - openbis.updateSeekLinks(postRegInfo); + return updateAssayStructure(nodeWithChildren, assayID.get()); } - } catch (URISyntaxException | InterruptedException | IOException e) { - throw new RuntimeException(e); } - System.out.println("Done"); + System.out.println("Creating new node(s)..."); + return createNewAssayStructure(nodeWithChildren); + } + + private SeekStructurePostRegistrationInformation handleSampleTransfer() + throws URISyntaxException, IOException, InterruptedException { + System.out.println("Collecting information from openBIS..."); + OpenbisSampleWithDatasets sampleWithDatasets = openbis.getSampleWithDatasets(objectID); + Set blacklist = parseBlackList(blacklistFile); + System.out.println("Translating openBIS property codes to SEEK names..."); + Map sampleTypesToIds = seek.getSampleTypeNamesToIDs(); + System.out.println("Trying to find existing corresponding sample in SEEK..."); + Optional sampleID = getSampleIDForOpenBISSample(sampleWithDatasets.getSample()); + sampleID.ifPresent(x -> System.out.println("Found sample with id "+sampleID.get())); + SeekStructure nodeWithChildren = translator.translate(sampleWithDatasets, sampleTypesToIds, + blacklist, transferData); + + System.out.println("Creating SEEK structure..."); + if(sampleID.isEmpty() || noUpdate) { + System.out.println("Creating new node(s)..."); + return createNewSampleStructure(nodeWithChildren); + } else { + System.out.println("Updating nodes..."); + return updateSampleStructure(nodeWithChildren, sampleID.get()); + } + } + + private SeekStructurePostRegistrationInformation handleDatasetTransfer() + throws URISyntaxException, IOException, InterruptedException { + System.out.println("Collecting information from openBIS..."); + Pair> datasetWithFiles = openbis.getDataSetWithFiles( + objectID); + Set blacklist = parseBlackList(blacklistFile); + //TODO is this necessary? + //System.out.println("Trying to find existing corresponding assets in SEEK..."); + //List assetIDs = seek.searchAssetsContainingKeyword(datasetWithFiles.getLeft().getCode()); + //System.out.println("Found existing asset ids: "+assetIDs); + SeekStructure nodeWithChildren = translator.translate(datasetWithFiles, blacklist, transferData); + + System.out.println("Creating new asset(s)..."); + return createNewAssetsForDataset(nodeWithChildren.getISAFileToDatasetFiles()); + } + + private SeekStructurePostRegistrationInformation updateSampleStructure( + SeekStructure nodeWithChildren, String sampleID) + throws URISyntaxException, IOException, InterruptedException { + SeekStructurePostRegistrationInformation postRegInfo = + seek.updateSampleNode(nodeWithChildren, sampleID); + List assetsToUpload = postRegInfo.getAssetsToUpload(); + if (transferData) { + handleDataTransfer(assetsToUpload); + } + postRegInfo.getExperimentIDWithEndpoint().ifPresentOrElse( + (value) -> System.out.printf("%s was successfully updated.%n", value.getRight()), + () -> System.out.printf("Update performed, but assay id not found in post update info.%n") + ); + return postRegInfo; + } + + private SeekStructurePostRegistrationInformation createNewSampleStructure( + SeekStructure nodeWithChildren) throws URISyntaxException, IOException, InterruptedException { + SeekStructurePostRegistrationInformation postRegistrationInformation = + seek.createSampleWithAssets(nodeWithChildren); + List assetsOfAssayToUpload = postRegistrationInformation.getAssetsToUpload(); + if (transferData) { + handleDataTransfer(assetsOfAssayToUpload); + } + System.out.printf("Sample was successfully created.%n"); + return postRegistrationInformation; + } + + private SeekStructurePostRegistrationInformation createNewAssetsForDataset( + Map assets) throws URISyntaxException, IOException, + InterruptedException { + + SeekStructurePostRegistrationInformation postRegistrationInformation = + seek.createStandaloneAssets(assets); + List assetsOfAssayToUpload = postRegistrationInformation.getAssetsToUpload(); + if (transferData) { + handleDataTransfer(assetsOfAssayToUpload); + } + System.out.printf("Assets were successfully created.%n"); + return postRegistrationInformation; } private Set parseBlackList(String blacklistFile) { @@ -195,50 +300,64 @@ private Set parseBlackList(String blacklistFile) { } } - private SeekStructurePostRegistrationInformation updateNodes(SeekStructure nodeWithChildren, + private SeekStructurePostRegistrationInformation updateAssayStructure(SeekStructure nodeWithChildren, String assayID) throws URISyntaxException, IOException, InterruptedException { - SeekStructurePostRegistrationInformation postRegInfo = seek.updateNode(nodeWithChildren, assayID); - System.out.printf("%s was successfully updated.%n", postRegInfo.getExperimentIDWithEndpoint().getRight()); + SeekStructurePostRegistrationInformation postRegInfo = seek.updateAssayNode(nodeWithChildren, + assayID); + List assetsToUpload = postRegInfo.getAssetsToUpload(); + if (transferData) { + handleDataTransfer(assetsToUpload); + } + postRegInfo.getExperimentIDWithEndpoint().ifPresentOrElse( + (value) -> System.out.printf("%s was successfully updated.%n", value.getRight()), + () -> System.out.printf("Update performed, but assay id not found in post update info.%n") + ); return postRegInfo; } - private SeekStructurePostRegistrationInformation createNewNodes(SeekStructure nodeWithChildren) + private SeekStructurePostRegistrationInformation createNewAssayStructure( + SeekStructure nodeWithChildren) throws URISyntaxException, IOException, InterruptedException { - SeekStructurePostRegistrationInformation postRegistrationInformation = + SeekStructurePostRegistrationInformation postRegInfo = seek.createNode(nodeWithChildren); - AssayWithQueuedAssets assetsOfAssayToUpload = postRegistrationInformation.getAssayWithQueuedAssets(); - if(transferData) { - final String tmpFolderPath = "tmp/"; - for(AssetToUpload asset : assetsOfAssayToUpload.getAssets()) { - String filePath = asset.getFilePath(); - String dsCode = asset.getDataSetCode(); - if(asset.getFileSizeInBytes() > 1000*1024*1024) { - System.out.printf("Skipping %s due to size...%n", - filePath); - } else if (asset.getFileSizeInBytes() > 300 * 1024 * 1024) { - System.out.printf("File is %s MB...streaming might take a while%n", - asset.getFileSizeInBytes() / (1024 * 1024)); - System.out.printf("Downloading file %s from openBIS to tmp folder due to size...%n", - filePath); - File tmpFile = openbis.downloadDataset(tmpFolderPath, dsCode, filePath); - - System.out.printf("Uploading file to SEEK...%n"); - String fileURL = seek.uploadFileContent(asset.getBlobEndpoint(), tmpFile.getAbsolutePath()); - System.out.printf("File stored here: %s%n", fileURL); - } else { - System.out.printf("Streaming file %s from openBIS to SEEK...%n", asset.getFilePath()); + List assetsToUpload = postRegInfo.getAssetsToUpload(); + if (transferData) { + handleDataTransfer(assetsToUpload); + } + System.out.printf("Assay was successfully created.%n"); + return postRegInfo; + } + + private void handleDataTransfer(List assets) + throws URISyntaxException, IOException, InterruptedException { + final String tmpFolderPath = "tmp/"; + for(AssetToUpload asset : assets) { + String filePath = asset.getFilePath(); + String dsCode = asset.getDataSetCode(); + if(asset.getFileSizeInBytes() > 1000*1024*1024) { + System.out.printf("Skipping %s due to size...%n", + filePath); + } else if (asset.getFileSizeInBytes() > 300 * 1024 * 1024) { + System.out.printf("File is %s MB...streaming might take a while%n", + asset.getFileSizeInBytes() / (1024 * 1024)); + System.out.printf("Downloading file %s from openBIS to tmp folder due to size...%n", + filePath); + File tmpFile = openbis.downloadDataset(tmpFolderPath, dsCode, filePath); + + System.out.printf("Uploading file to SEEK...%n"); + String fileURL = seek.uploadFileContent(asset.getBlobEndpoint(), tmpFile.getAbsolutePath()); + System.out.printf("File stored here: %s%n", fileURL); + } else { + System.out.printf("Streaming file %s from openBIS to SEEK...%n", asset.getFilePath()); + + String fileURL = seek.uploadStreamContent(asset.getBlobEndpoint(), + () -> openbis.streamDataset(asset.getDataSetCode(), asset.getFilePath())); + System.out.printf("File stored here: %s%n", fileURL); - String fileURL = seek.uploadStreamContent(asset.getBlobEndpoint(), - () -> openbis.streamDataset(asset.getDataSetCode(), asset.getFilePath())); - System.out.printf("File stored here: %s%n", fileURL); - } } System.out.printf("Cleaning up temp folder%n"); cleanupTemp(new File(tmpFolderPath)); } - - System.out.printf("%s was successfully created.%n", assetsOfAssayToUpload.getAssayEndpoint()); - return postRegistrationInformation; } private boolean sampleExists(String objectID) { @@ -262,13 +381,26 @@ private Optional getAssayIDForOpenBISExperiment(Experiment experiment) String permID = experiment.getPermId().getPermId(); List assayIDs = seek.searchAssaysContainingKeyword(permID); if(assayIDs.isEmpty()) { - System.err.println("no assay found containing "+permID); return Optional.empty(); } if(assayIDs.size() == 1) { return Optional.of(assayIDs.get(0)); } - throw new RuntimeException("Experiment identifier "+permID+ " was found in more than one assay: "+assayIDs); + throw new RuntimeException("Experiment identifier "+permID+ " was found in more than one assay: " + +assayIDs+". Don't know which assay to update."); + } + + private Optional getSampleIDForOpenBISSample(Sample sample) + throws URISyntaxException, IOException, InterruptedException { + String id = sample.getIdentifier().getIdentifier(); + List sampleIDs = seek.searchSamplesContainingKeyword(id); + if(sampleIDs.isEmpty()) { + return Optional.empty(); + } + if(sampleIDs.size() == 1) { + return Optional.of(sampleIDs.get(0)); + } + throw new RuntimeException("Experiment identifier "+id+ " was found in more than one sample: "+sampleIDs); } private void cleanupTemp(File tmpFolder) { diff --git a/src/main/java/life/qbic/model/OpenbisSampleWithDatasets.java b/src/main/java/life/qbic/model/OpenbisSampleWithDatasets.java new file mode 100644 index 0000000..431d66c --- /dev/null +++ b/src/main/java/life/qbic/model/OpenbisSampleWithDatasets.java @@ -0,0 +1,32 @@ +package life.qbic.model; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample; +import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile; +import java.util.List; +import java.util.Map; + +public class OpenbisSampleWithDatasets { + + private Sample sample; + private List datasets; + private Map> datasetCodeToFiles; + + public OpenbisSampleWithDatasets(Sample sample, + List datasets, Map> datasetCodeToFiles) { + this.sample = sample; + this.datasets = datasets; + this.datasetCodeToFiles = datasetCodeToFiles; + } + + public Sample getSample() { + return sample; + } + + public List getDatasets() { + return datasets; + } + + public List getFilesForDataset(String permID) { + return datasetCodeToFiles.get(permID); + } +} diff --git a/src/main/java/life/qbic/model/download/SEEKConnector.java b/src/main/java/life/qbic/model/download/SEEKConnector.java index 6214c7e..10195bf 100644 --- a/src/main/java/life/qbic/model/download/SEEKConnector.java +++ b/src/main/java/life/qbic/model/download/SEEKConnector.java @@ -27,7 +27,6 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import javax.xml.parsers.ParserConfigurationException; -import life.qbic.model.AssayWithQueuedAssets; import life.qbic.model.AssetInformation; import life.qbic.model.OpenbisSeekTranslator; import life.qbic.model.SampleInformation; @@ -51,6 +50,8 @@ public class SEEKConnector { private byte[] credentials; private OpenbisSeekTranslator translator; private final String DEFAULT_PROJECT_ID; + private final List ASSET_TYPES = new ArrayList<>(Arrays.asList("data_files", "models", + "sops", "documents", "publications")); public SEEKConnector(String seekURL, byte[] httpCredentials, String openBISBaseURL, String defaultProjectTitle) throws URISyntaxException, IOException, @@ -136,6 +137,8 @@ public String addAssay(ISAAssay assay) .send(buildAuthorizedPOSTRequest(endpoint, assay.toJson()), BodyHandlers.ofString()); + System.err.println(assay.toJson()); + if(response.statusCode()!=200) { throw new RuntimeException("Failed : HTTP error code : " + response.statusCode()); } @@ -376,12 +379,14 @@ public boolean endPointExists(String endpoint) * @throws URISyntaxException * @throws InterruptedException */ - public List createAssetsForAssays(Map isaToOpenBISFile, - List assays) + public List createAssetsForAssays(Map isaToOpenBISFile, List assays) throws IOException, URISyntaxException, InterruptedException { List result = new ArrayList<>(); for (GenericSeekAsset isaFile : isaToOpenBISFile.keySet()) { - isaFile.withAssays(assays); + if(!assays.isEmpty()) { + isaFile.withAssays(assays); + } result.add(createAsset(isaToOpenBISFile.get(isaFile).getDataSetPermId().getPermId(), isaFile)); } @@ -511,6 +516,45 @@ public List searchAssaysContainingKeyword(String searchTerm) return assayIDs; } + /** + * Searches for samples containing a search term and returns a list of found sample ids + * @param searchTerm the search term that should be in the assay properties - e.g. an openBIS id + * @return + * @throws URISyntaxException + * @throws IOException + * @throws InterruptedException + */ + public List searchSamplesContainingKeyword(String searchTerm) + throws URISyntaxException, IOException, InterruptedException { + + JsonNode result = genericSearch("samples", "*"+searchTerm+"*"); + + JsonNode hits = result.path("data"); + List assayIDs = new ArrayList<>(); + for (Iterator it = hits.elements(); it.hasNext(); ) { + JsonNode hit = it.next(); + assayIDs.add(hit.get("id").asText()); + } + return assayIDs; + } + + + public List searchAssetsContainingKeyword(String searchTerm) + throws URISyntaxException, IOException, InterruptedException { + List assetIDs = new ArrayList<>(); + for(String type : ASSET_TYPES) { + JsonNode result = genericSearch(type, "*"+searchTerm+"*"); + + JsonNode hits = result.path("data"); + for (Iterator it = hits.elements(); it.hasNext(); ) { + JsonNode hit = it.next(); + assetIDs.add(hit.get("id").asText()); + } + } + return assetIDs; + } + + /** * Updates information of an existing assay, its samples and attached assets. Missing samples and * assets are created, but nothing missing from the new structure is deleted from SEEK. @@ -520,7 +564,7 @@ public List searchAssaysContainingKeyword(String searchTerm) * data to newly created assets. In the case of the update use case, only newly created objects * will be contained in the return object. */ - public SeekStructurePostRegistrationInformation updateNode(SeekStructure nodeWithChildren, + public SeekStructurePostRegistrationInformation updateAssayNode(SeekStructure nodeWithChildren, String assayID) throws URISyntaxException, IOException, InterruptedException { JsonNode assayData = fetchAssayData(assayID).get("data"); Map sampleInfos = collectSampleInformation(assayData); @@ -593,22 +637,25 @@ public SeekStructurePostRegistrationInformation updateNode(SeekStructure nodeWit } String assayEndpoint = apiURL + "/assays/" + assayID; - AssayWithQueuedAssets assayWithQueuedAssets = - new AssayWithQueuedAssets(assayEndpoint, assetsToUpload); - String expID = nodeWithChildren.getAssayWithOpenBISReference().getRight(); + if(nodeWithChildren.getAssayWithOpenBISReference().isEmpty()) { + throw new RuntimeException("No assay and openBIS reference found. Object has not been " + + "initialized using an assay object and openBIS experiment reference."); + } + String expID = nodeWithChildren.getAssayWithOpenBISReference().get().getRight(); Pair experimentIDWithEndpoint = new ImmutablePair<>(expID, assayEndpoint); - return new SeekStructurePostRegistrationInformation(assayWithQueuedAssets, - experimentIDWithEndpoint, sampleIDsWithEndpoints, datasetIDsWithEndpoints); + SeekStructurePostRegistrationInformation postRegInfo = + new SeekStructurePostRegistrationInformation(assetsToUpload, sampleIDsWithEndpoints, + datasetIDsWithEndpoints); + postRegInfo.setExperimentIDWithEndpoint(experimentIDWithEndpoint); + return postRegInfo; } private Map collectAssetInformation(JsonNode assayData) throws URISyntaxException, IOException, InterruptedException { - List assetTypes = new ArrayList<>(Arrays.asList("data_files", "models", "sops", - "documents", "publications")); Map assets = new HashMap<>(); JsonNode relationships = assayData.get("relationships"); - for(String type : assetTypes) { + for(String type : ASSET_TYPES) { for (Iterator it = relationships.get(type).get("data").elements(); it.hasNext(); ) { String assetID = it.next().get("id").asText(); AssetInformation assetInfo = fetchAssetInformation(assetID, type); @@ -674,6 +721,71 @@ private Optional tryParseDatasetPermID(String input) { return Optional.empty(); } + public SeekStructurePostRegistrationInformation updateSampleNode(SeekStructure nodeWithChildren, + String sampleID) throws URISyntaxException, IOException, InterruptedException { + SampleInformation existingSampleInfo = fetchSampleInformation(sampleID); + //TODO to be able to connect samples with assets, we need to create a new assay, here + + // compare samples + Map newSamplesWithReferences = nodeWithChildren.getSamplesWithOpenBISReference(); + + List samplesToCreate = new ArrayList<>(); + for (ISASample newSample : newSamplesWithReferences.keySet()) { + String openBisID = newSamplesWithReferences.get(newSample); + if (!existingSampleInfo.getOpenBisIdentifier().equals(openBisID)) { + samplesToCreate.add(newSample); + System.out.printf("%s not found in SEEK. It will be created.%n", openBisID); + } else { + Map newAttributes = newSample.fetchCopyOfAttributeMap(); + for (String key : newAttributes.keySet()) { + Object newValue = newAttributes.get(key); + Object oldValue = existingSampleInfo.getAttributes().get(key); + + boolean oldEmpty = oldValue == null || oldValue.toString().isEmpty(); + boolean newEmpty = newValue == null || newValue.toString().isEmpty(); + if ((!oldEmpty && !newEmpty) && !newValue.equals(oldValue)) { + System.out.printf("Mismatch found in attributes of %s. Sample will be updated.%n", + openBisID); + updateSample(newSample, sampleID); + } + } + } + } + + // compare assets + Map newAssetsToFiles = nodeWithChildren.getISAFileToDatasetFiles(); + + //TODO follow creation of assets for assay, no way to be sure these are attached to similar samples + List assetsToCreate = new ArrayList<>(); + + Map sampleIDsWithEndpoints = new HashMap<>(); + for (ISASample sample : samplesToCreate) { + String sampleEndpoint = createSample(sample); + sampleIDsWithEndpoints.put(newSamplesWithReferences.get(sample), sampleEndpoint); + } + List assetsToUpload = new ArrayList<>(); + + for (GenericSeekAsset asset : assetsToCreate) { + assetsToUpload.add(createAsset(newAssetsToFiles.get(asset).getDataSetPermId().getPermId(), + asset)); + } + Map> datasetIDsWithEndpoints = new HashMap<>(); + + for (AssetToUpload asset : assetsToUpload) { + String endpointWithoutBlob = blobEndpointToAssetURL(asset.getBlobEndpoint()); + String dsCode = asset.getDataSetCode(); + if (datasetIDsWithEndpoints.containsKey(dsCode)) { + datasetIDsWithEndpoints.get(dsCode).add(endpointWithoutBlob); + } else { + datasetIDsWithEndpoints.put(dsCode, new HashSet<>( + List.of(endpointWithoutBlob))); + } + } + + return new SeekStructurePostRegistrationInformation(assetsToUpload, sampleIDsWithEndpoints, + datasetIDsWithEndpoints); + } + private SampleInformation fetchSampleInformation(String sampleID) throws URISyntaxException, IOException, InterruptedException { String endpoint = apiURL+"/samples/"+sampleID; @@ -723,13 +835,21 @@ private JsonNode fetchAssayData(String assayID) public SeekStructurePostRegistrationInformation createNode(SeekStructure nodeWithChildren) throws URISyntaxException, IOException, InterruptedException { - Pair assayIDPair = nodeWithChildren.getAssayWithOpenBISReference(); + if(nodeWithChildren.getAssayWithOpenBISReference().isEmpty()) { + throw new RuntimeException("No assay and openBIS reference found. Object has not been " + + "initialized using an assay object and openBIS experiment reference."); + } + + Pair assayIDPair = nodeWithChildren.getAssayWithOpenBISReference().get(); String assayID = addAssay(assayIDPair.getKey()); String assayEndpoint = apiURL+"/assays/"+assayID; Pair experimentIDWithEndpoint = new ImmutablePair<>(assayIDPair.getValue(), assayEndpoint); + //wait for a bit, so we can be sure the assay that will be referenced by the samples has been created + Thread.sleep(3000); + Map sampleIDsWithEndpoints = new HashMap<>(); Map samplesWithReferences = nodeWithChildren.getSamplesWithOpenBISReference(); for(ISASample sample : samplesWithReferences.keySet()) { @@ -740,12 +860,12 @@ public SeekStructurePostRegistrationInformation createNode(SeekStructure nodeWit Map isaToFileMap = nodeWithChildren.getISAFileToDatasetFiles(); - AssayWithQueuedAssets assayWithQueuedAssets = new AssayWithQueuedAssets(assayEndpoint, - createAssetsForAssays(isaToFileMap, Collections.singletonList(assayID))); + List assetsToUpload = createAssetsForAssays(isaToFileMap, + Collections.singletonList(assayID)); Map> datasetIDsWithEndpoints = new HashMap<>(); - for(AssetToUpload asset : assayWithQueuedAssets.getAssets()) { + for(AssetToUpload asset : assetsToUpload) { String endpointWithoutBlob = blobEndpointToAssetURL(asset.getBlobEndpoint()); String dsCode = asset.getDataSetCode(); if(datasetIDsWithEndpoints.containsKey(dsCode)) { @@ -755,8 +875,61 @@ public SeekStructurePostRegistrationInformation createNode(SeekStructure nodeWit List.of(endpointWithoutBlob))); } } - return new SeekStructurePostRegistrationInformation(assayWithQueuedAssets, - experimentIDWithEndpoint, sampleIDsWithEndpoints, datasetIDsWithEndpoints); + SeekStructurePostRegistrationInformation postRegInfo = + new SeekStructurePostRegistrationInformation(assetsToUpload, sampleIDsWithEndpoints, + datasetIDsWithEndpoints); + postRegInfo.setExperimentIDWithEndpoint(experimentIDWithEndpoint); + return postRegInfo; + } + + public SeekStructurePostRegistrationInformation createSampleWithAssets(SeekStructure nodeWithChildren) + throws URISyntaxException, IOException, InterruptedException { + Map sampleIDsWithEndpoints = new HashMap<>(); + Map samplesWithReferences = nodeWithChildren.getSamplesWithOpenBISReference(); + for(ISASample sample : samplesWithReferences.keySet()) { + String sampleEndpoint = createSample(sample); + sampleIDsWithEndpoints.put(samplesWithReferences.get(sample), sampleEndpoint); + } + + Map isaToFileMap = nodeWithChildren.getISAFileToDatasetFiles(); + + List assetsToUpload = createAssetsForAssays(isaToFileMap, new ArrayList<>()); + + Map> datasetIDsWithEndpoints = new HashMap<>(); + + for(AssetToUpload asset : assetsToUpload) { + String endpointWithoutBlob = blobEndpointToAssetURL(asset.getBlobEndpoint()); + String dsCode = asset.getDataSetCode(); + if(datasetIDsWithEndpoints.containsKey(dsCode)) { + datasetIDsWithEndpoints.get(dsCode).add(endpointWithoutBlob); + } else { + datasetIDsWithEndpoints.put(dsCode, new HashSet<>( + List.of(endpointWithoutBlob))); + } + } + return new SeekStructurePostRegistrationInformation(assetsToUpload, sampleIDsWithEndpoints, + datasetIDsWithEndpoints); + } + + public SeekStructurePostRegistrationInformation createStandaloneAssets( + Map isaToFileMap) + throws IOException, URISyntaxException, InterruptedException { + + List assetsToUpload = createAssetsForAssays(isaToFileMap, new ArrayList<>()); + + Map> datasetIDsWithEndpoints = new HashMap<>(); + + for(AssetToUpload asset : assetsToUpload) { + String endpointWithoutBlob = blobEndpointToAssetURL(asset.getBlobEndpoint()); + String dsCode = asset.getDataSetCode(); + if(datasetIDsWithEndpoints.containsKey(dsCode)) { + datasetIDsWithEndpoints.get(dsCode).add(endpointWithoutBlob); + } else { + datasetIDsWithEndpoints.put(dsCode, new HashSet<>( + List.of(endpointWithoutBlob))); + } + } + return new SeekStructurePostRegistrationInformation(assetsToUpload, datasetIDsWithEndpoints); } public OpenbisSeekTranslator getTranslator() { @@ -797,25 +970,37 @@ public String getDataSetCode() { public class SeekStructurePostRegistrationInformation { - private final AssayWithQueuedAssets assayWithQueuedAssets; - private final Pair experimentIDWithEndpoint; + private final List assetsToUpload; + private Optional> experimentIDWithEndpoint; private final Map sampleIDsWithEndpoints; private final Map> datasetIDsWithEndpoints; - public SeekStructurePostRegistrationInformation(AssayWithQueuedAssets assayWithQueuedAssets, - Pair experimentIDWithEndpoint, Map sampleIDsWithEndpoints, + public SeekStructurePostRegistrationInformation(List assetsToUpload, + Map sampleIDsWithEndpoints, Map> datasetIDsWithEndpoints) { - this.assayWithQueuedAssets = assayWithQueuedAssets; - this.experimentIDWithEndpoint = experimentIDWithEndpoint; + this.assetsToUpload = assetsToUpload; this.sampleIDsWithEndpoints = sampleIDsWithEndpoints; this.datasetIDsWithEndpoints = datasetIDsWithEndpoints; + this.experimentIDWithEndpoint = Optional.empty(); + } + + public SeekStructurePostRegistrationInformation(List assetsToUpload, + Map> datasetIDsWithEndpoints) { + this.sampleIDsWithEndpoints = new HashMap<>(); + this.datasetIDsWithEndpoints = datasetIDsWithEndpoints; + this.assetsToUpload = assetsToUpload; + this.experimentIDWithEndpoint = Optional.empty(); + } + + public void setExperimentIDWithEndpoint(Pair experimentIDWithEndpoint) { + this.experimentIDWithEndpoint = Optional.of(experimentIDWithEndpoint); } - public AssayWithQueuedAssets getAssayWithQueuedAssets() { - return assayWithQueuedAssets; + public List getAssetsToUpload() { + return assetsToUpload; } - public Pair getExperimentIDWithEndpoint() { + public Optional> getExperimentIDWithEndpoint() { return experimentIDWithEndpoint; } diff --git a/src/main/java/life/qbic/model/isa/SeekStructure.java b/src/main/java/life/qbic/model/isa/SeekStructure.java index 29caeef..858e30e 100644 --- a/src/main/java/life/qbic/model/isa/SeekStructure.java +++ b/src/main/java/life/qbic/model/isa/SeekStructure.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -15,16 +16,28 @@ */ public class SeekStructure { - private final Pair assayAndOpenBISReference; + private final Optional> assayAndOpenBISReference; private final Map samplesWithOpenBISReference; private final Map isaToOpenBISFile; public SeekStructure(ISAAssay assay, String openBISReference) { - this.assayAndOpenBISReference = new ImmutablePair<>(assay, openBISReference); + this.assayAndOpenBISReference = Optional.of(new ImmutablePair<>(assay, openBISReference)); this.samplesWithOpenBISReference = new HashMap<>(); this.isaToOpenBISFile = new HashMap<>(); } + public SeekStructure(ISASample isaSample, String sampleID) { + this.samplesWithOpenBISReference = new HashMap<>(); + this.isaToOpenBISFile = new HashMap<>(); + this.assayAndOpenBISReference = Optional.empty(); + } + + public SeekStructure(Map isaToOpenBISFile) { + this.isaToOpenBISFile = isaToOpenBISFile; + this.samplesWithOpenBISReference = new HashMap<>(); + this.assayAndOpenBISReference = Optional.empty(); + } + public void addSample(ISASample sample, String openBISReference) { samplesWithOpenBISReference.put(sample, openBISReference); } @@ -33,7 +46,7 @@ public void addAsset(GenericSeekAsset asset, DataSetFile file) { isaToOpenBISFile.put(asset, file); } - public Pair getAssayWithOpenBISReference() { + public Optional> getAssayWithOpenBISReference() { return assayAndOpenBISReference; }