Skip to content

Commit f68bb7f

Browse files
Merge pull request #6 from qbicsoftware/feature/petab-basic-features
Petab download and upload
2 parents 46d461e + bcd2619 commit f68bb7f

20 files changed

+587
-75
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@
9494
<artifactId>picocli</artifactId>
9595
<version>4.6.2</version>
9696
</dependency>
97+
<dependency>
98+
<groupId>com.fasterxml.jackson.dataformat</groupId>
99+
<artifactId>jackson-dataformat-yaml</artifactId>
100+
<version>2.17.2</version>
101+
</dependency>
97102
<dependency>
98103
<groupId>jline</groupId>
99104
<artifactId>jline</artifactId>
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package life.qbic.io;
2+
3+
import java.io.BufferedReader;
4+
import java.io.File;
5+
import java.io.FileReader;
6+
import java.io.IOException;
7+
import java.nio.charset.Charset;
8+
import java.nio.charset.StandardCharsets;
9+
import java.nio.file.Files;
10+
import java.nio.file.Path;
11+
import java.nio.file.Paths;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.Objects;
15+
import java.util.Optional;
16+
import life.qbic.model.petab.PetabMetadata;
17+
18+
public class PetabParser {
19+
20+
private final String META_INFO_YAML = "metaInformation.yaml";
21+
22+
public PetabMetadata parse(String dataPath) {
23+
24+
File directory = new File(dataPath);
25+
List<String> sourcePetabReferences = new ArrayList<>();
26+
27+
File yaml = findYaml(directory);
28+
if (yaml != null) {
29+
BufferedReader reader = null;
30+
try {
31+
reader = new BufferedReader(new FileReader(yaml));
32+
boolean inIDBlock = false;
33+
while (true) {
34+
String line = reader.readLine();
35+
if (line == null) {
36+
break;
37+
}
38+
// the id block ends, when a new key with colon is found
39+
if (inIDBlock && line.contains(":")) {
40+
inIDBlock = false;
41+
}
42+
// if we are in the id block, we collect one dataset code per line
43+
if (inIDBlock) {
44+
parseDatasetCode(line).ifPresent(sourcePetabReferences::add);
45+
}
46+
if (line.contains("openBISSourceIds:")) {
47+
inIDBlock = true;
48+
}
49+
}
50+
reader.close();
51+
} catch (IOException e) {
52+
throw new RuntimeException(e);
53+
}
54+
}
55+
return new PetabMetadata(sourcePetabReferences);
56+
}
57+
58+
private Optional<String> parseDatasetCode(String line) {
59+
// expected input: " - 20240702093837370-684137"
60+
String[] tokens = line.split("-");
61+
if(tokens.length == 3) {
62+
return Optional.of(tokens[1].strip()+"-"+tokens[2].strip());
63+
} else {
64+
System.out.println("Could not extract dataset code from the following line:");
65+
System.out.println(line);
66+
}
67+
return Optional.empty();
68+
}
69+
70+
public void addDatasetId(String outputPath, String datasetCode) throws IOException {
71+
72+
Path path = Paths.get(Objects.requireNonNull(findYaml(new File(outputPath))).getPath());
73+
Charset charset = StandardCharsets.UTF_8;
74+
75+
final String keyWord = "openBISId";
76+
77+
String idInLine = keyWord+":(.*)?(\\r\\n|[\\r\\n])";
78+
79+
String content = Files.readString(path, charset);
80+
content = content.replaceAll(idInLine, keyWord+": "+datasetCode+"\n");
81+
Files.write(path, content.getBytes(charset));
82+
}
83+
84+
private File findYaml(File directory) {
85+
for (File file : Objects.requireNonNull(directory.listFiles())) {
86+
if (file.isFile() && file.getName().equalsIgnoreCase(META_INFO_YAML)) {
87+
return file;
88+
}
89+
if (file.isDirectory()) {
90+
return findYaml(file);
91+
}
92+
}
93+
System.out.println(META_INFO_YAML + " not found");
94+
return null;
95+
}
96+
97+
}

src/main/java/life/qbic/io/commandline/CommandLineOptions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
// main command with format specifiers for the usage help message
1010
@Command(name = "openbis-scripts",
11-
subcommands = { SampleHierarchyCommand.class, FindDatasetsCommand.class,
12-
UploadDatasetCommand.class, SpaceStatisticsCommand.class },
11+
subcommands = { SampleHierarchyCommand.class, FindDatasetsCommand.class, DownloadPetabCommand.class,
12+
UploadPetabResultCommand.class, UploadDatasetCommand.class, SpaceStatisticsCommand.class },
1313
description = "A client software for querying openBIS.",
1414
mixinStandardHelpOptions = true, versionProvider = ManifestVersionProvider.class)
1515
public class CommandLineOptions {

src/main/java/life/qbic/io/commandline/DownloadDatasetCommand.java

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package life.qbic.io.commandline;
2+
3+
import ch.ethz.sis.openbis.generic.OpenBIS;
4+
import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
5+
import java.io.IOException;
6+
import java.util.Collections;
7+
import java.util.List;
8+
import java.util.Optional;
9+
import life.qbic.App;
10+
import life.qbic.io.PetabParser;
11+
import life.qbic.model.DatasetWithProperties;
12+
import life.qbic.model.download.OpenbisConnector;
13+
import picocli.CommandLine.Command;
14+
import picocli.CommandLine.Mixin;
15+
import picocli.CommandLine.Parameters;
16+
17+
@Command(name = "download-petab",
18+
description = "Downloads PEtab dataset and stores some additional information from openbis in the petab.yaml")
19+
public class DownloadPetabCommand implements Runnable {
20+
21+
@Parameters(arity = "1", paramLabel = "dataset id", description = "The code of the dataset to download. Can be found via list-data.")
22+
private String datasetCode;
23+
@Parameters(arity = "1", paramLabel = "download path", description = "The local path where to store the downloaded data")
24+
private String outputPath;
25+
@Mixin
26+
AuthenticationOptions auth = new AuthenticationOptions();
27+
28+
@Override
29+
public void run() {
30+
OpenBIS authentication = App.loginToOpenBIS(auth.getPassword(), auth.getUser(), auth.getAS(), auth.getDSS());
31+
OpenbisConnector openbis = new OpenbisConnector(authentication);
32+
33+
List<DataSet> datasets = openbis.findDataSets(Collections.singletonList(datasetCode));
34+
35+
if(datasets.isEmpty()) {
36+
System.out.println(datasetCode+" not found");
37+
return;
38+
}
39+
DatasetWithProperties result = new DatasetWithProperties(datasets.get(0));
40+
Optional<String> patientID = openbis.findPropertyInSampleHierarchy("PATIENT_DKFZ_ID",
41+
result.getExperiment().getIdentifier());
42+
patientID.ifPresent(s -> result.addProperty("patientID", s));
43+
44+
System.out.println("Found dataset, downloading.");
45+
System.out.println();
46+
47+
openbis.downloadDataset(outputPath, datasetCode);
48+
49+
PetabParser parser = new PetabParser();
50+
try {
51+
System.out.println("Adding dataset identifier to metaInformation.yaml.");
52+
parser.addDatasetId(outputPath, datasetCode);
53+
} catch (IOException e) {
54+
System.out.println("Could not add dataset identifier.");
55+
throw new RuntimeException(e);
56+
}
57+
System.out.println("Done");
58+
}
59+
60+
}

src/main/java/life/qbic/io/commandline/UploadDatasetCommand.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,4 @@ private boolean pathValid(String dataPath) {
7070
return new File(dataPath).exists();
7171
}
7272

73-
private String getTimeStamp() {
74-
final String PATTERN_FORMAT = "YYYY-MM-dd_HHmmss";
75-
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_FORMAT);
76-
return LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC).format(formatter);
77-
}
7873
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package life.qbic.io.commandline;
2+
3+
import ch.ethz.sis.openbis.generic.OpenBIS;
4+
import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
5+
import java.io.File;
6+
import java.nio.file.Path;
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import life.qbic.App;
10+
import life.qbic.io.PetabParser;
11+
import life.qbic.model.download.OpenbisConnector;
12+
import picocli.CommandLine.Command;
13+
import picocli.CommandLine.Mixin;
14+
import picocli.CommandLine.Parameters;
15+
16+
@Command(name = "upload-petab",
17+
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).")
18+
public class UploadPetabResultCommand implements Runnable {
19+
20+
@Parameters(arity = "1", paramLabel = "file/folder", description = "The path to the file or folder to upload")
21+
private String dataPath;
22+
@Parameters(arity = "1", paramLabel = "experiment ID", description = "The full identifier of the experiment the data should be attached to. "
23+
+ "The identifier must be of the format: /space/project/experiment")
24+
private String experimentID;
25+
//@Option(arity = "1..*", paramLabel = "<parent_datasets>", description = "Optional list of dataset codes to act"
26+
// + " as parents for the upload. E.g. when this dataset has been generated using these datasets as input.", names = {"-pa", "--parents"})
27+
private List<String> parents = new ArrayList<>();
28+
@Mixin
29+
AuthenticationOptions auth = new AuthenticationOptions();
30+
31+
private OpenbisConnector openbis;
32+
private PetabParser petabParser = new PetabParser();
33+
34+
@Override
35+
public void run() {
36+
OpenBIS authentication = App.loginToOpenBIS(auth.getPassword(), auth.getUser(), auth.getAS(), auth.getDSS());
37+
openbis = new OpenbisConnector(authentication);
38+
39+
if(!pathValid(dataPath)) {
40+
System.out.printf("Path %s could not be found%n", dataPath);
41+
return;
42+
}
43+
if(!experimentExists(experimentID)) {
44+
System.out.printf("Experiment %s could not be found%n", experimentID);
45+
return;
46+
}
47+
System.out.println("Looking for reference datasets in metaInformation.yaml...");
48+
parents = petabParser.parse(dataPath).getSourcePetabReferences();
49+
if(parents.isEmpty()) {
50+
System.out.println("No reference datasets found in openBISSourceIds property. Assuming"
51+
+ "this is a new dataset.");
52+
} else {
53+
System.out.println("Found reference ids: " + String.join(", ", parents));
54+
if (!datasetsExist(parents)) {
55+
System.out.printf("One or more datasets %s could not be found%n", parents);
56+
return;
57+
} else {
58+
System.out.println("Referenced datasets found");
59+
}
60+
}
61+
System.out.println("Uploading dataset...");
62+
//TODO copy and remove source references here
63+
DataSetPermId result = openbis.registerDataset(Path.of(dataPath), experimentID, parents);
64+
System.out.printf("Dataset %s was successfully created%n", result.getPermId());
65+
}
66+
67+
private boolean datasetsExist(List<String> datasetCodes) {
68+
return openbis.findDataSets(datasetCodes).size() == datasetCodes.size();
69+
}
70+
71+
private boolean experimentExists(String experimentID) {
72+
return openbis.experimentExists(experimentID);
73+
}
74+
75+
private boolean pathValid(String dataPath) {
76+
return new File(dataPath).exists();
77+
}
78+
79+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package life.qbic.model.petab;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import java.util.List;
5+
6+
public class Arguments {
7+
@JsonProperty
8+
List<String> housekeeperObservableIds;
9+
10+
@Override
11+
public String toString() {
12+
return "Arguments{" +
13+
"housekeeperObservableIds=" + housekeeperObservableIds +
14+
'}';
15+
}
16+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package life.qbic.model.petab;
2+
3+
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
6+
public class CellCountInfo {
7+
@JsonProperty
8+
double seeded;
9+
@JsonProperty
10+
String ncellsCount;
11+
@JsonProperty
12+
String unit;
13+
14+
@Override
15+
public String toString() {
16+
return "CellCountInfo{" +
17+
"seeded=" + seeded +
18+
", ncellsCount='" + ncellsCount + '\'' +
19+
", unit='" + unit + '\'' +
20+
'}';
21+
}
22+
}
23+

0 commit comments

Comments
 (0)