Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/feedback #15

Merged
merged 8 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,16 @@ The data itself can be transferred and stored in SEEK using the '-d' flag.
To completely exclude some dataset information from being transferred, a file ('--blacklist')
containing dataset codes (from openBIS) can be specified. //TODO do this for samples/sample types

In order to store links to the newly created SEEK objects in the source openBIS instance, the
following sample type is needed:

Sample Type Code: EXTERNAL_LINK
Property: LINK_TYPE (VARCHAR)
Property: URL (VARCHAR)

EXTERNAL_LINK samples are added to transferred experiments and samples and point to their respective
counterparts in SEEK. If the sample type is not available, this will be logged.

### Updating nodes in SEEK based on updates in openBIS

Updating nodes in SEEK uses the same general command, parameters and options. Unless otherwise
Expand All @@ -279,4 +289,29 @@ least one sample attribute is different in openBIS and SEEK
4. assets attached to the experiment or samples will be created, if they are missing from this assay
5. no existing sample or assets are deleted from SEEK, even if they are missing from openBIS

**Example command:**

`java -jar target/openbis-scripts-1.0.0-jar-with-dependencies.jar openbis-to-seek /MYSPACE/PROJECTY/00_P_INFO_691 mystudy -d -config config.txt --openbis-pw --seek-pw`

**Example output:**

Transfer openBIS -> SEEK started.
Provided openBIS object: /MYSPACE/PROJECTY/00_P_INFO_691
Provided SEEK study title: mystudy
No SEEK project title provided, will search config file.
Transfer datasets to SEEK? true
Update existing nodes if found? true
Connecting to openBIS...
Searching for specified object in openBIS...
Search successful.
Connecting to SEEK...
Collecting information from openBIS...
Translating openBIS property codes to SEEK names...
Creating SEEK structure...
Trying to find existing corresponding assay in SEEK...
Found assay with id 64
Updating nodes...
Mismatch found in Gender attribute of /MYSPACE/PROJECTY/00_P_INFO_691. Sample will be updated.
http://localhost:3000/assays/64 was successfully updated.

## Caveats and Future Options
16 changes: 13 additions & 3 deletions src/main/java/life/qbic/io/PetabParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,22 @@ public void addDatasetId(String outputPath, String datasetCode) throws IOExcepti
Path path = Paths.get(Objects.requireNonNull(findYaml(new File(outputPath))).getPath());
Charset charset = StandardCharsets.UTF_8;

final String keyWord = "openBISId";
final String idKeyWord = "openBISId";

String idInLine = keyWord+":(.*)?(\\r\\n|[\\r\\n])";
final String endOfLine = ":(.*)?(\\r\\n|[\\r\\n])";
final String idInLine = idKeyWord+endOfLine;

String content = Files.readString(path, charset);
content = content.replaceAll(idInLine, keyWord+": "+datasetCode+"\n");
// existing property found, fill/replace with relevant dataset code
if(content.contains(idKeyWord)) {
content = content.replaceAll(idInLine, idKeyWord+": "+datasetCode+"\n");
// no existing property found, create it above the dateOfExperiment property
} else {
String dateKeyword = "dateOfExperiment";
String dateLine = dateKeyword+endOfLine;
String newLines = idKeyWord+": "+datasetCode+"\n "+dateKeyword+":\n";
content = content.replaceAll(dateLine, newLines);
}
Files.write(path, content.getBytes(charset));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import life.qbic.model.OpenbisExperimentWithDescendants;
import life.qbic.model.OpenbisSeekTranslator;
import life.qbic.model.download.SEEKConnector.SeekStructurePostRegistrationInformation;
import life.qbic.model.isa.NodeType;
import life.qbic.model.isa.SeekStructure;
import life.qbic.model.download.OpenbisConnector;
import life.qbic.model.download.SEEKConnector;
Expand Down Expand Up @@ -88,7 +89,7 @@ public void run() {
System.out.printf("No SEEK project title provided, will search config file.%n");
}
System.out.printf("Transfer datasets to SEEK? %s%n", transferData);
System.out.printf("Update existing assay if found? %s%n", !noUpdate);
System.out.printf("Update existing nodes if found? %s%n", !noUpdate);
if(blacklistFile!=null && !blacklistFile.isBlank()) {
System.out.printf("File with datasets codes that won't be transferred: %s%n", blacklistFile);
}
Expand All @@ -100,28 +101,20 @@ public void run() {

this.openbis = new OpenbisConnector(authentication);

boolean isSample = false;
boolean isDataSet = false;

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;
}
NodeType nodeType = NodeType.ASSAY;

if (!isExperiment && sampleExists(objectID)) {
isSample = true;
nodeType = NodeType.SAMPLE;
}

if (!isExperiment && !isSample && datasetsExist(Arrays.asList(objectID))) {
isDataSet = true;
if (!isExperiment && !nodeType.equals(NodeType.SAMPLE) && datasetsExist(Arrays.asList(objectID))) {
nodeType = NodeType.ASSET;
}

if (!isSample && !isExperiment && !isDataSet) {
if (nodeType.equals(NodeType.ASSAY) && !isExperiment) {
System.out.printf(
"%s could not be found in openBIS. Make sure you either specify an experiment, sample or dataset%n",
objectID);
Expand Down Expand Up @@ -150,16 +143,20 @@ public void run() {
OpenbisExperimentWithDescendants structure;
try {
System.out.println("Collecting information from openBIS...");
if (isExperiment) {
structure = openbis.getExperimentWithDescendants(objectID);
postRegInfo = handleExperimentTransfer(structure);
} else if (isSample) {
structure = openbis.getExperimentAndDataFromSample(objectID);
postRegInfo = handleExperimentTransfer(structure);
} else {
structure = openbis.getExperimentStructureFromDataset(objectID);
postRegInfo = handleExperimentTransfer(structure);
switch (nodeType) {
case ASSAY:
structure = openbis.getExperimentWithDescendants(objectID);
break;
case SAMPLE:
structure = openbis.getExperimentAndDataFromSample(objectID);
break;
case ASSET:
structure = openbis.getExperimentStructureFromDataset(objectID);
break;
default:
throw new RuntimeException("Handling of node type " + nodeType + " is not supported.");
}
postRegInfo = handleExperimentTransfer(structure, nodeType);
} catch (URISyntaxException | IOException | InterruptedException e) {
throw new RuntimeException(e);
}
Expand All @@ -171,7 +168,7 @@ public void run() {
}

private SeekStructurePostRegistrationInformation handleExperimentTransfer(
OpenbisExperimentWithDescendants experiment)
OpenbisExperimentWithDescendants experiment, NodeType nodeType)
throws URISyntaxException, IOException, InterruptedException {
Set<String> blacklist = parseBlackList(blacklistFile);
System.out.println("Translating openBIS property codes to SEEK names...");
Expand Down Expand Up @@ -302,8 +299,9 @@ private Set<String> parseBlackList(String blacklistFile) {
}
}

private SeekStructurePostRegistrationInformation updateAssayStructure(SeekStructure nodeWithChildren,
String assayID) throws URISyntaxException, IOException, InterruptedException {
private SeekStructurePostRegistrationInformation updateAssayStructure(
SeekStructure nodeWithChildren, String assayID) throws URISyntaxException,
IOException, InterruptedException {
SeekStructurePostRegistrationInformation postRegInfo = seek.updateAssayNode(nodeWithChildren,
assayID);
List<AssetToUpload> assetsToUpload = postRegInfo.getAssetsToUpload();
Expand Down Expand Up @@ -381,7 +379,7 @@ private Optional<String> getAssayIDForOpenBISExperiment(Experiment experiment)
// because if a perm id is found in the wrong SEEK node, meta-information in SEEK could be
// overwritten or samples/data added to the wrong assay.
String permID = experiment.getPermId().getPermId();
List<String> assayIDs = seek.searchAssaysContainingKeyword(permID);
List<String> assayIDs = seek.searchAssaysInStudyContainingKeyword(permID);
if(assayIDs.isEmpty()) {
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@
* otherwise the type "UNKNOWN" will be used.
*/
@Command(name = "upload-data",
description = "uploads a dataset and attaches it to an experiment and (optionally) other datasets")
description = "uploads a dataset and attaches it to an experiment or sample and (optionally) "
+ "other datasets")
public class UploadDatasetCommand implements Runnable {

@Parameters(arity = "1", paramLabel = "file/folder", description = "The path to the file or folder to upload")
private String dataPath;
@Parameters(arity = "1", paramLabel = "object ID", description = "The full identifier of the experiment or sample the data should be attached to. "
+ "The identifier must be of the format: /space/project/experiment for experiments or /space/sample for samples")
@Parameters(arity = "1", paramLabel = "object ID", description = "The full identifier of the "
+ "experiment or sample the data should be attached to. The identifier must be of the format: "
+ "/space/project/experiment for experiments or /space/sample for samples")
private String objectID;
@Option(arity = "1..*", paramLabel = "<parent_datasets>", 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"})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* The Upload PEtab command can be used to upload a PEtab Dataset to openBIS and connect it to its
* source files if these are stored in the same openBIS instance and referenced in the PEtabs meta-
* data.
* To upload a PEtab dataset, the path to the PEtab folder and the experiment ID to which it should
* To upload a PEtab dataset, the path to the PEtab folder and the object ID to which it should
* be attached need to be provided.
* The dataset type of the new dataset in openBIS can be specified using the --type option,
* otherwise the type "UNKNOWN" will be used.
Expand All @@ -38,10 +38,10 @@ public class UploadPetabResultCommand implements Runnable {
@Parameters(arity = "1", paramLabel = "PEtab folder", description = "The path to the PEtab 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. "
+ "The identifier must be of the format: /space/project/experiment")
private String experimentID;
@Parameters(arity = "1", paramLabel = "object ID", description = "The full identifier of the "
+ "experiment or sample the data should be attached to. The identifier must be of the format: "
+ "/space/project/experiment for experiments or /space/sample for samples")
private String objectID;
@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";
Expand All @@ -55,7 +55,8 @@ public class UploadPetabResultCommand implements Runnable {
public void run() {
App.readConfig();

OpenBIS authentication = App.loginToOpenBIS(auth.getOpenbisPassword(), auth.getOpenbisUser(), auth.getOpenbisAS(), auth.getOpenbisDSS());
OpenBIS authentication = App.loginToOpenBIS(auth.getOpenbisPassword(), auth.getOpenbisUser(),
auth.getOpenbisAS(), auth.getOpenbisDSS());
openbis = new OpenbisConnector(authentication);

if(!pathValid(dataPath)) {
Expand All @@ -66,8 +67,13 @@ public void run() {
System.out.printf("%s is not a directory. Please specify the PETab directory root%n", dataPath);
return;
}
if(!experimentExists(experimentID)) {
System.out.printf("Experiment %s could not be found%n", experimentID);
boolean attachToSample = false;
boolean attachToExperiment = openbis.experimentExists(objectID);
if(openbis.sampleExists(objectID)) {
attachToSample = true;
}
if(!attachToSample && !attachToExperiment) {
System.out.printf("%s could not be found in openBIS.%n", objectID);
return;
}
System.out.println("Looking for reference datasets in metaInformation.yaml...");
Expand All @@ -85,19 +91,21 @@ public void run() {
}
}
System.out.println("Uploading dataset...");
DataSetPermId result = openbis.registerDatasetForExperiment(Path.of(dataPath), experimentID,
datasetType, parents);
System.out.printf("Dataset %s was successfully created%n", result.getPermId());
if(attachToExperiment) {
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,
datasetType, parents);
System.out.printf("Dataset %s was successfully attached to sample%n", result.getPermId());
}
}

private boolean datasetsExist(List<String> 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();
}
Expand Down
43 changes: 40 additions & 3 deletions src/main/java/life/qbic/model/download/OpenbisConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.update.ExperimentUpdate;
import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.SampleType;
import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create.SampleCreation;
import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleTypeFetchOptions;
import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SampleIdentifier;
Expand Down Expand Up @@ -536,15 +537,51 @@ public SampleTypesAndMaterials getSampleTypesWithMaterials() {

public void createSeekLinks(SeekStructurePostRegistrationInformation postRegInformation) {
Optional<Pair<String, String>> experimentInfo = postRegInformation.getExperimentIDWithEndpoint();
//TODO link sample type not implemented?
final String SAMPLE_TYPE = "EXTERNAL_LINK";

SampleTypeSearchCriteria criteria = new SampleTypeSearchCriteria();
criteria.withCode().thatEquals(SAMPLE_TYPE);
SampleTypeFetchOptions typeOptions = new SampleTypeFetchOptions();
typeOptions.withPropertyAssignments().withPropertyType();
typeOptions.withPropertyAssignments().withEntityType();
if(openBIS.searchSampleTypes(criteria, typeOptions).getObjects().isEmpty()) {
System.out.printf(
"This is where links would be put into openBIS, but EXTERNAL_LINK sample was "
+ "not yet added to openBIS instance.%n");
return;
}

if(experimentInfo.isPresent()) {
ExperimentIdentifier id = new ExperimentIdentifier(experimentInfo.get().getLeft());
String endpoint = experimentInfo.get().getRight();
Map<String, String> props = new HashMap<>();
props.put(EXPERIMENT_LINK_PROPERTY, endpoint);
updateExperimentProperties(id, props, false);
SampleCreation sample = createNewLinkSample(endpoint);
sample.setExperimentId(id);
openBIS.createSamples(Arrays.asList(sample));
}
Map<String, String> sampleInfos = postRegInformation.getSampleIDsWithEndpoints();
for(String sampleID : sampleInfos.keySet()) {
SampleIdentifier id = new SampleIdentifier(sampleID);
String endpoint = sampleInfos.get(sampleID);
SampleCreation sample = createNewLinkSample(endpoint);
sample.setParentIds(Arrays.asList(id));
openBIS.createSamples(Arrays.asList(sample));
}
}

private SampleCreation createNewLinkSample(String endpoint) {
final String SAMPLE_TYPE = "EXTERNAL_LINK";
SampleCreation sample = new SampleCreation();
sample.setTypeId(new EntityTypePermId(SAMPLE_TYPE, EntityKind.SAMPLE));

Map<String, String> properties = new HashMap<>();
properties.put("LINK_TYPE", "SEEK");
properties.put("URL", endpoint);

sample.setProperties(properties);
return sample;
}

public void updateSeekLinks(SeekStructurePostRegistrationInformation postRegistrationInformation) {
}

Expand Down
Loading
Loading