Skip to content

Commit 016ba9c

Browse files
authored
Merge pull request #389 from qbicsoftware/development
Release PR
2 parents afd1882 + ab606ae commit 016ba9c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1381
-402
lines changed

database-connector/src/main/java/life/qbic/projectmanagement/experiment/persistence/SamplePreviewJpaRepository.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@ private Specification<SamplePreview> generateExperimentIdandFilterSpecification(
7373
Specification<SamplePreview> speciesSpec = SamplePreviewSpecs.speciesContains(filter);
7474
Specification<SamplePreview> specimenSpec = SamplePreviewSpecs.specimenContains(filter);
7575
Specification<SamplePreview> analyteSpec = SamplePreviewSpecs.analyteContains(filter);
76-
Specification<SamplePreview> analysisTypeSpec = SamplePreviewSpecs.analysisTypeContains(filter);
76+
Specification<SamplePreview> analysisMethodContains = SamplePreviewSpecs.analysisMethodContains(
77+
filter);
7778
Specification<SamplePreview> commentSpec = SamplePreviewSpecs.commentContains(filter);
7879
Specification<SamplePreview> containsFilterSpec = Specification.anyOf(sampleCodeSpec,
7980
sampleLabelSpec, batchLabelSpec, bioReplicateLabelSpec, conditionSpec, speciesSpec,
80-
specimenSpec, analyteSpec, analysisTypeSpec, commentSpec);
81+
specimenSpec, analyteSpec, analysisMethodContains, commentSpec);
8182
Specification<SamplePreview> isDistinctSpec = SamplePreviewSpecs.isDistinct();
8283
return Specification.where(experimentIdSpec).and(isBlankSpec)
8384
.and(containsFilterSpec)
@@ -163,9 +164,9 @@ public static Specification<SamplePreview> analyteContains(String filter) {
163164
builder.like(root.get("analyte"), "%" + filter + "%");
164165
}
165166

166-
public static Specification<SamplePreview> analysisTypeContains(String filter) {
167+
public static Specification<SamplePreview> analysisMethodContains(String filter) {
167168
return (root, query, builder) ->
168-
builder.like(root.get("analysisType"), "%" + filter + "%");
169+
builder.like(root.get("analysisMethod"), "%" + filter + "%");
169170
}
170171

171172
public static Specification<SamplePreview> commentContains(String filter) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package life.qbic.projectmanagement.persistence.repository;
2+
3+
import java.util.Optional;
4+
5+
/**
6+
* <b>Analyte Term Mapper</b>
7+
*
8+
* <p>Enables the mapping of an analyte ontology term used in the data manager against a term
9+
* provided by the mapper implementation. </p>
10+
*
11+
* @since 1.0.0
12+
*/
13+
public interface AnalyteTermMapper {
14+
15+
/**
16+
* Tries to map a given term to a term of the mapper implementation
17+
*
18+
* @param term the term that the mapper should look-up and try to map
19+
* @return an {@link Optional<String>} mapped term, is {@link Optional#empty()} if the look-up was
20+
* unsuccessful.
21+
* @since 1.0.0
22+
*/
23+
Optional<String> mapFrom(String term);
24+
25+
}

database-connector/src/main/java/life/qbic/projectmanagement/persistence/repository/OpenbisConnector.java

+4-27
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public class OpenbisConnector implements ExperimentalDesignVocabularyRepository,
7272
private static final String DEFAULT_EXPERIMENT_TYPE = "Q_SAMPLE_PREPARATION";
7373
private static final String DEFAULT_DELETION_REASON = "Commanded by data manager app";
7474

75-
private final AnalyteToOpenbisSampleTypeMapper analyteMapper = new AnalyteToOpenbisSampleTypeMapperImpl();
75+
private final AnalyteTermMapper analyteMapper = new SimpleOpenBisTermMapper();
7676

7777
// used by spring to wire it up
7878
private OpenbisConnector(@Value("${openbis.user.name}") String userName,
@@ -207,11 +207,11 @@ public void addSamplesToProject(Project project,
207207
sampleCreation.setSpaceId(new SpacePermId(DEFAULT_SPACE_CODE));
208208
Map<String, String> props = new HashMap<>();
209209

210-
props.put("Q_SECONDARY_NAME", sample.biologicalReplicateId().toString());
211-
props.put("Q_EXTERNALDB_ID", sample.label());
210+
props.put("Q_SECONDARY_NAME", sample.label());
211+
props.put("Q_EXTERNALDB_ID", sample.sampleId().value());
212212
String analyteValue = sample.sampleOrigin().getAnalyte().value();
213213
String openBisSampleType = retrieveOpenBisAnalyteCode(analyteValue).or(
214-
() -> analyteMapper.translateSampleTypeString(analyteValue))
214+
() -> analyteMapper.mapFrom(analyteValue))
215215
.orElseThrow(() -> {
216216
logger("No mapping was found for " + analyteValue);
217217
return new MappingNotFoundException();
@@ -384,27 +384,4 @@ static class ConnectionException extends RuntimeException {
384384
static class MappingNotFoundException extends RuntimeException {
385385

386386
}
387-
388-
public interface AnalyteToOpenbisSampleTypeMapper {
389-
390-
Optional<String> translateSampleTypeString(String analyte);
391-
}
392-
393-
private class AnalyteToOpenbisSampleTypeMapperImpl implements
394-
AnalyteToOpenbisSampleTypeMapper {
395-
396-
// dummy map needed once ontologies outside openBIS are used
397-
private static final Map<String, String> analyteToSampleType;
398-
399-
static {
400-
analyteToSampleType = new HashMap<>();
401-
analyteToSampleType.put("RNA", "RNA");
402-
analyteToSampleType.put("DNA", "DNA");
403-
}
404-
405-
@Override
406-
public Optional<String> translateSampleTypeString(String analyte) {
407-
return Optional.ofNullable(analyte);
408-
}
409-
}
410387
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package life.qbic.projectmanagement.persistence.repository;
2+
3+
import static life.qbic.logging.service.LoggerFactory.logger;
4+
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
import java.util.Optional;
8+
import life.qbic.logging.api.Logger;
9+
10+
/**
11+
* <b>Simple openBIS term mapper</b>
12+
* <p>
13+
* OpenBIS term mapper implementation of the {@link AnalyteTermMapper} interface.
14+
* <p>
15+
* Enables the client to lookup a possible mapping openBIS term for a given input analyte value.
16+
*
17+
* @since 1.0.0
18+
*/
19+
public class SimpleOpenBisTermMapper implements AnalyteTermMapper {
20+
21+
private static final Logger log = logger(SimpleOpenBisTermMapper.class);
22+
private static final Map<String, String> ANALYTE_TO_SAMPLE_TYPE;
23+
24+
/*
25+
EXTEND MAPPINGS TO OPENBIS TERMS HERE
26+
*/
27+
static {
28+
ANALYTE_TO_SAMPLE_TYPE = new HashMap<>();
29+
ANALYTE_TO_SAMPLE_TYPE.put("AMPLICON", "AMPLICON");
30+
ANALYTE_TO_SAMPLE_TYPE.put("CARBOHYDRATES", "CARBOHYDRATES");
31+
ANALYTE_TO_SAMPLE_TYPE.put("CELL_LYSATE", "CELL_LYSATE");
32+
ANALYTE_TO_SAMPLE_TYPE.put("CF_DNA", "CF_DNA");
33+
ANALYTE_TO_SAMPLE_TYPE.put("DNA", "DNA");
34+
ANALYTE_TO_SAMPLE_TYPE.put("GLYCANS", "GLYCANS");
35+
ANALYTE_TO_SAMPLE_TYPE.put("GLYCOPEPTIDES", "GLYCOPEPTIDES");
36+
ANALYTE_TO_SAMPLE_TYPE.put("LIPIDS", "LIPIDS");
37+
ANALYTE_TO_SAMPLE_TYPE.put("M_RNA", "M_RNA");
38+
ANALYTE_TO_SAMPLE_TYPE.put("PEPTIDES", "PEPTIDES");
39+
ANALYTE_TO_SAMPLE_TYPE.put("PHOSPHOPEPTIDES", "PHOSPHOPEPTIDES");
40+
ANALYTE_TO_SAMPLE_TYPE.put("PHOSPHOPROTEINS", "PHOSPHOPROTEINS");
41+
ANALYTE_TO_SAMPLE_TYPE.put("PHOSPHOLIPIDS", "PHOSPHOLIPIDS");
42+
ANALYTE_TO_SAMPLE_TYPE.put("PROTEINS", "PROTEINS");
43+
ANALYTE_TO_SAMPLE_TYPE.put("RNA", "RNA");
44+
ANALYTE_TO_SAMPLE_TYPE.put("R_RNA", "R_RNA");
45+
ANALYTE_TO_SAMPLE_TYPE.put("SINGLE_NUCLEI", "SINGLE_NUCLEI");
46+
ANALYTE_TO_SAMPLE_TYPE.put("SMALLMOLECULES", "SMALLMOLECULES");
47+
}
48+
49+
@Override
50+
public Optional<String> mapFrom(String term) {
51+
try {
52+
return Optional.of(ANALYTE_TO_SAMPLE_TYPE.get(term));
53+
} catch (NullPointerException e) {
54+
log.debug("Unknown analyte term '%s', cannot map to openBIS terminology.".formatted(term));
55+
return Optional.empty();
56+
}
57+
}
58+
}

projectmanagement/src/main/java/life/qbic/projectmanagement/application/ProjectCreationService.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import life.qbic.application.commons.Result;
1212
import life.qbic.logging.api.Logger;
1313
import life.qbic.projectmanagement.domain.project.Contact;
14+
import life.qbic.projectmanagement.domain.project.Funding;
1415
import life.qbic.projectmanagement.domain.project.OfferIdentifier;
1516
import life.qbic.projectmanagement.domain.project.Project;
1617
import life.qbic.projectmanagement.domain.project.ProjectCode;
@@ -55,7 +56,8 @@ public Result<Project, ApplicationException> createProject(String sourceOffer,
5556
String objective,
5657
Contact principalInvestigator,
5758
Contact responsiblePerson,
58-
Contact projectManager) {
59+
Contact projectManager,
60+
Funding funding) {
5961
if (Objects.isNull(principalInvestigator)) {
6062
return Result.fromError(new ApplicationException("principal investigator is null"));
6163
}
@@ -65,7 +67,7 @@ public Result<Project, ApplicationException> createProject(String sourceOffer,
6567

6668
try {
6769
Project project = createProject(code, title, objective, projectManager,
68-
principalInvestigator, responsiblePerson);
70+
principalInvestigator, responsiblePerson, funding);
6971
Optional.ofNullable(sourceOffer)
7072
.flatMap(it -> it.isBlank() ? Optional.empty() : Optional.of(it))
7173
.ifPresent(offerIdentifier -> project.linkOffer(OfferIdentifier.of(offerIdentifier)));
@@ -77,7 +79,7 @@ public Result<Project, ApplicationException> createProject(String sourceOffer,
7779

7880
private Project createProject(String code, String title, String objective,
7981
Contact projectManager,
80-
Contact principalInvestigator, Contact responsiblePerson) {
82+
Contact principalInvestigator, Contact responsiblePerson, Funding funding) {
8183
ProjectIntent intent = getProjectIntent(title, objective);
8284
ProjectCode projectCode;
8385
try {
@@ -93,7 +95,7 @@ private Project createProject(String code, String title, String objective,
9395
ErrorParameters.of(code, ProjectCode.getPREFIX(), ProjectCode.getLENGTH()));
9496
}
9597

96-
var registrationResult = projectDomainService.registerProject(intent, projectCode, projectManager, principalInvestigator, responsiblePerson);
98+
var registrationResult = projectDomainService.registerProject(intent, projectCode, projectManager, principalInvestigator, responsiblePerson, funding);
9799

98100
if (registrationResult.isError() && registrationResult.getError().equals(ResponseCode.PROJECT_REGISTRATION_FAILED)) {
99101
throw new ApplicationException("Project registration failed.", ErrorCode.GENERAL, ErrorParameters.of(code));

projectmanagement/src/main/java/life/qbic/projectmanagement/application/ProjectInformationService.java

+15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import life.qbic.logging.service.LoggerFactory;
1010
import life.qbic.projectmanagement.application.api.ProjectPreviewLookup;
1111
import life.qbic.projectmanagement.domain.project.Contact;
12+
import life.qbic.projectmanagement.domain.project.Funding;
1213
import life.qbic.projectmanagement.domain.project.Project;
1314
import life.qbic.projectmanagement.domain.project.ProjectId;
1415
import life.qbic.projectmanagement.domain.project.ProjectObjective;
@@ -110,4 +111,18 @@ public void stateObjective(ProjectId projectId, String objective) {
110111
project.stateObjective(projectObjective);
111112
projectRepository.update(project);
112113
}
114+
115+
public void addFunding(ProjectId projectId, String label, String referenceId) {
116+
Funding funding = Funding.of(label, referenceId);
117+
var project = loadProject(projectId);
118+
project.setFunding(funding);
119+
projectRepository.update(project);
120+
121+
}
122+
123+
public void removeFunding(ProjectId projectId) {
124+
var project = loadProject(projectId);
125+
project.removeFunding();
126+
projectRepository.update(project);
127+
}
113128
}

projectmanagement/src/main/java/life/qbic/projectmanagement/application/sample/SamplePreview.java

+15-13
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public class SamplePreview {
4545
@Column(name = "label")
4646
private String sampleLabel;
4747
private String comment;
48-
@Column(name = "analysis_type")
49-
private String analysisType;
48+
@Column(name = "analysis_method")
49+
private String analysisMethod;
5050
@OneToOne(cascade = CascadeType.ALL)
5151
@JoinColumn(name = "experimentalGroupId")
5252
private ExperimentalGroup experimentalGroup;
@@ -61,7 +61,7 @@ protected SamplePreview() {
6161
private SamplePreview(ExperimentId experimentId, SampleId sampleId, String sampleCode,
6262
String batchLabel, String bioReplicateLabel,
6363
String sampleLabel, ExperimentalGroup experimentalGroup, String species, String specimen,
64-
String analyte, String analysisType, String comment) {
64+
String analyte, String analysisMethod, String comment) {
6565
Objects.requireNonNull(experimentId);
6666
Objects.requireNonNull(sampleId);
6767
Objects.requireNonNull(sampleCode);
@@ -82,9 +82,9 @@ private SamplePreview(ExperimentId experimentId, SampleId sampleId, String sampl
8282
this.species = species;
8383
this.specimen = specimen;
8484
this.analyte = analyte;
85+
this.analysisMethod = analysisMethod;
8586
// optional columns
8687
this.comment = comment;
87-
this.analysisType = analysisType;
8888
}
8989

9090
/**
@@ -106,17 +106,17 @@ private SamplePreview(ExperimentId experimentId, SampleId sampleId, String sampl
106106
* preview
107107
* @param analyte the {@link Analyte} for the {@link Sample} associated with this
108108
* preview
109-
* @param analysisType the type of analysis to be performed for this {@link Sample}
109+
* @param analysisMethod the analysis method to be performed for this {@link Sample}
110110
* @param comment an optional comment pertaining to the associated {@link Sample}
111111
* @return the sample preview
112112
*/
113113
public static SamplePreview create(ExperimentId experimentId, SampleId sampleId,
114114
String sampleCode,
115115
String batchLabel, String bioReplicateLabel,
116116
String sampleLabel, ExperimentalGroup experimentalGroup, String species, String specimen,
117-
String analyte, String analysisType, String comment) {
117+
String analyte, String analysisMethod, String comment) {
118118
return new SamplePreview(experimentId, sampleId, sampleCode, batchLabel, bioReplicateLabel,
119-
sampleLabel, experimentalGroup, species, specimen, analyte, analysisType, comment);
119+
sampleLabel, experimentalGroup, species, specimen, analyte, analysisMethod, comment);
120120
}
121121

122122
public ExperimentId experimentId() {
@@ -154,9 +154,11 @@ public String specimen() {
154154
public String analyte() {
155155
return analyte;
156156
}
157-
public String analysisType() {
158-
return analysisType;
157+
158+
public String analysisMethod() {
159+
return analysisMethod;
159160
}
161+
160162
public String comment() {
161163
return comment;
162164
}
@@ -181,15 +183,15 @@ public boolean equals(Object o) {
181183
that.sampleLabel)
182184
&& Objects.equals(species, that.species) && Objects.equals(specimen,
183185
that.specimen) && Objects.equals(analyte, that.analyte) && Objects.equals(
184-
experimentalGroup, that.experimentalGroup) && Objects.equals(analysisType,
185-
that.analysisType) && Objects.equals(comment, that.comment);
186+
experimentalGroup, that.experimentalGroup) && Objects.equals(analysisMethod,
187+
that.analysisMethod) && Objects.equals(comment, that.comment);
186188
}
187189

188190
@Override
189191
public int hashCode() {
190192
return Objects.hash(experimentId, sampleCode, sampleId, batchLabel, bioReplicateLabel,
191193
sampleLabel,
192-
species, specimen, analyte, experimentalGroup, analysisType, comment);
194+
species, specimen, analyte, experimentalGroup, analysisMethod, comment);
193195
}
194196

195197
@Override
@@ -204,7 +206,7 @@ public String toString() {
204206
", species='" + species + '\'' +
205207
", specimen='" + specimen + '\'' +
206208
", analyte='" + analyte + '\'' +
207-
", analysisType='" + analysisType + '\'' +
209+
", analysisType='" + analysisMethod + '\'' +
208210
", comment='" + comment + '\'' +
209211
", conditions=" + experimentalGroup +
210212
'}';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package life.qbic.projectmanagement.domain.project;
2+
3+
import jakarta.persistence.Embeddable;
4+
import java.util.Objects;
5+
6+
/**
7+
* A small business value object representing information about funding.
8+
* <p>
9+
* Funding is described by a free-text label and a funding body's reference identifier.
10+
* <p>
11+
* For example:
12+
* <ul>
13+
* <li>SFB (label)</li>
14+
* <li>SFB 1101 (reference id)</li>
15+
* </ul>
16+
*
17+
* @since 1.0.0
18+
*/
19+
@Embeddable
20+
public class Funding {
21+
22+
private String grant;
23+
24+
private String grantId;
25+
26+
protected Funding() {
27+
28+
}
29+
30+
private Funding(String grant, String grantId) {
31+
this.grant = grant;
32+
this.grantId = grantId;
33+
}
34+
35+
public static Funding of(String grant, String grantId) {
36+
return new Funding(grant, grantId);
37+
}
38+
39+
public String grant() {
40+
return this.grant;
41+
}
42+
43+
public String grantId() {
44+
return this.grantId;
45+
}
46+
47+
@Override
48+
public boolean equals(Object o) {
49+
if (this == o) {
50+
return true;
51+
}
52+
if (o == null || getClass() != o.getClass()) {
53+
return false;
54+
}
55+
Funding funding = (Funding) o;
56+
return Objects.equals(grant, funding.grant) && Objects.equals(grantId,
57+
funding.grantId);
58+
}
59+
60+
@Override
61+
public int hashCode() {
62+
return Objects.hash(grant, grantId);
63+
}
64+
}

0 commit comments

Comments
 (0)