Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/development' into feature/#720-p…
Browse files Browse the repository at this point in the history
…ossibility-to-download-the-project-information
  • Loading branch information
sven1103 committed Oct 18, 2024
2 parents 1b8f155 + 4a5aabc commit be05ae4
Show file tree
Hide file tree
Showing 87 changed files with 4,446 additions and 1,044 deletions.
1 change: 1 addition & 0 deletions docs/processes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
diagrams have been created with: https://sequencediagram.org/
1 change: 1 addition & 0 deletions docs/processes/Sample_registration_process.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions docs/processes/Sample_registration_process.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
title Sample Registration Process


note over Client, Template Service: Provide experiment ID
Client->Template Service:Request sample registration template
Template Service->Experiment Service:Fetch experimental groups
Experiment Service->Experiment Service:Load experimental groups
Template Service<-Experiment Service:Return experimental groups
Template Service->XLSXBuilder:Build template with selection choices
Template Service<-XLSXBuilder:Return template
Client<-Template Service: Return template
Client->Client: Fill out template
Client->Sample Registration Service: Register samples
Sample Registration Service->Validation Service: Requests validation
Validation Service->Validation Service: Validates
Sample Registration Service<-Validation Service: Returns validation report
Client<-Sample Registration Service: Notify about report
Client->Client: Resolve potential conflicts
Client->Sample Registration Service: Register sample batch
Sample Registration Service->Sample Registration Service: Create new sample batch
Sample Registration Service->Sample Registration Service: Create new samples with batch ID
Sample Registration Service->Sample Registration Service: Update batch with sample IDs

Client<-Sample Registration Service: Notify
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,11 @@ public List<OntologyClass> query(String searchTerm, int offset, int limit)
@Override
public Optional<OntologyClass> searchByCurie(String curie) throws LookupException {
try {
List<TibTerm> result = searchByOboId(curie, 0, 10);
if (result.isEmpty()) {
return Optional.empty();
}
return Optional.of(
result.stream().map(TIBTerminologyServiceIntegration::convert).toList().get(0));
return searchByOboIdExact(curie).map(TIBTerminologyServiceIntegration::convert);
} catch (IOException e) {
throw wrapIO(e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw wrapInterrupted(e);
} catch (Exception e) {
throw wrapUnknown(e);
Expand Down Expand Up @@ -221,9 +217,12 @@ private List<TibTerm> fullSearch(String searchTerm, int offset, int limit)
return List.of();
}
HttpRequest termSelectQuery = HttpRequest.newBuilder().uri(URI.create(
searchEndpointAbsoluteUrl.toString() + "?q=" + URLEncoder.encode(searchTerm,
StandardCharsets.UTF_8) + "&rows="
+ limit + "&start=" + offset + "&ontology=" + createOntologyFilterQueryParameter()))
searchEndpointAbsoluteUrl.toString() + "?q="
+ URLEncoder.encode(
searchTerm,
StandardCharsets.UTF_8)
+ "&rows=" + limit + "&start=" + offset
+ "&ontology=" + createOntologyFilterQueryParameter()))
.header("Content-Type", "application/json").GET().build();
var response = HTTP_CLIENT.send(termSelectQuery, BodyHandlers.ofString());
return parseResponse(response);
Expand Down Expand Up @@ -278,15 +277,47 @@ private List<TibTerm> searchByOboId(String oboId, int offset, int limit)
return List.of();
}
HttpRequest termSelectQuery = HttpRequest.newBuilder().uri(URI.create(
searchEndpointAbsoluteUrl.toString() + "?q=" + URLEncoder.encode(oboId,
StandardCharsets.UTF_8) + "&rows="
+ limit + "&start=" + offset + "&ontology=" + createOntologyFilterQueryParameter()
+ "&queryFields=obo_id"))
searchEndpointAbsoluteUrl.toString() + "?q="
+ URLEncoder.encode(
//obo_id query field requires `:` separator instead of `_`
oboId.replace("_", ":"), StandardCharsets.UTF_8)
+ "&queryFields=obo_id"
+ "&rows=" + limit + "&start=" + offset
+ "&ontology=" + createOntologyFilterQueryParameter()))
.header("Content-Type", "application/json").GET().build();
var response = HTTP_CLIENT.send(termSelectQuery, BodyHandlers.ofString());
return parseResponse(response);
}

/**
* Queries the /search endpoint of the TIB terminology service, but filters any results by the
* terms `obo_id` property.
* <p>
*
* @param oboId the obo id to match exactly
* @return a list of matching terms.
* @throws IOException if e.g. the service cannot be reached
* @throws InterruptedException the query is interrupted before succeeding
* @since 1.4.0
*/
private Optional<TibTerm> searchByOboIdExact(String oboId)
throws IOException, InterruptedException {
if (oboId.isBlank()) { // avoid unnecessary API calls
return Optional.empty();
}
HttpRequest termSelectQuery = HttpRequest.newBuilder().uri(URI.create(
searchEndpointAbsoluteUrl.toString() + "?q="
+ URLEncoder.encode(
//obo_id query field requires `:` separator instead of `_`
oboId.replace("_", ":"), StandardCharsets.UTF_8)
+ "&queryFields=obo_id"
+ "&exact=true"
+ "&ontology=" + createOntologyFilterQueryParameter()))
.header("Content-Type", "application/json").GET().build();
var response = HTTP_CLIENT.send(termSelectQuery, BodyHandlers.ofString());
return parseResponse(response).stream().findFirst();
}

/**
* Parses the TIB service response object and returns the wrapped terms.
*
Expand All @@ -310,5 +341,3 @@ private List<TibTerm> parseResponse(HttpResponse<String> response) {
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Predicate;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import life.qbic.application.commons.OffsetBasedRequest;
import life.qbic.application.commons.SortOrder;
import life.qbic.projectmanagement.application.sample.SamplePreview;
import life.qbic.projectmanagement.application.sample.SamplePreviewLookup;
import life.qbic.projectmanagement.domain.model.experiment.ExperimentId;
import life.qbic.projectmanagement.domain.model.sample.AnalysisMethod;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
Expand Down Expand Up @@ -176,8 +180,13 @@ public static Specification<SamplePreview> analyteContains(String filter) {
}

public static Specification<SamplePreview> analysisMethodContains(String filter) {
return (root, query, builder) ->
builder.like(root.get("analysisMethod"), "%" + filter + "%");
return (root, query, builder) -> {
Set<String> matchingValues = Arrays.stream(AnalysisMethod.values())
.filter(method -> method.label().toUpperCase().contains(filter.toUpperCase()))
.map(AnalysisMethod::abbreviation)
.collect(Collectors.toUnmodifiableSet());
return root.get("analysisMethod").in(matchingValues);
};
}

public static Specification<SamplePreview> commentContains(String filter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
import life.qbic.projectmanagement.domain.model.batch.BatchId;
import life.qbic.projectmanagement.domain.model.experiment.ExperimentId;
import life.qbic.projectmanagement.domain.model.project.Project;
import life.qbic.projectmanagement.domain.model.project.ProjectId;
import life.qbic.projectmanagement.domain.model.sample.Sample;
import life.qbic.projectmanagement.domain.model.sample.SampleCode;
import life.qbic.projectmanagement.domain.model.sample.SampleId;
import life.qbic.projectmanagement.domain.repository.ProjectRepository;
import life.qbic.projectmanagement.domain.repository.SampleRepository;
import life.qbic.projectmanagement.domain.service.SampleDomainService.ResponseCode;
import life.qbic.projectmanagement.infrastructure.sample.openbis.OpenbisConnector.SampleNotDeletedException;
Expand Down Expand Up @@ -48,34 +50,46 @@ public class SampleRepositoryImpl implements SampleRepository {
private static final Logger log = logger(SampleRepositoryImpl.class);
private final QbicSampleRepository qbicSampleRepository;
private final QbicSampleDataRepo sampleDataRepo;
private final ProjectRepository projectRepository;

@Autowired
public SampleRepositoryImpl(QbicSampleRepository qbicSampleRepository,
QbicSampleDataRepo sampleDataRepo) {
this.qbicSampleRepository = qbicSampleRepository;
this.sampleDataRepo = sampleDataRepo;
QbicSampleDataRepo sampleDataRepo, ProjectRepository projectRepository) {
this.qbicSampleRepository = Objects.requireNonNull(qbicSampleRepository);
this.sampleDataRepo = Objects.requireNonNull(sampleDataRepo);
this.projectRepository = Objects.requireNonNull(projectRepository);
}

@Override
public Result<Collection<Sample>, ResponseCode> addAll(Project project,
Collection<Sample> samples) {
String commaSeperatedSampleIds = buildCommaSeparatedSampleIds(
samples.stream().map(Sample::sampleId).toList());
List<Sample> savedSamples;
try {
this.qbicSampleRepository.saveAll(samples);
savedSamples = this.qbicSampleRepository.saveAll(samples);
} catch (Exception e) {
log.error("The samples:" + commaSeperatedSampleIds + "could not be saved", e);
return Result.fromError(ResponseCode.REGISTRATION_FAILED);
}
try {
sampleDataRepo.addSamplesToProject(project, samples.stream().toList());
sampleDataRepo.addSamplesToProject(project, savedSamples);
} catch (Exception e) {
log.error("The samples:" + commaSeperatedSampleIds + "could not be stored in openBIS", e);
log.error("Removing samples from repository, as well.");
qbicSampleRepository.deleteAll(samples);
qbicSampleRepository.deleteAll(savedSamples);
return Result.fromError(ResponseCode.REGISTRATION_FAILED);
}
return Result.fromValue(samples);
return Result.fromValue(savedSamples);
}

@Override
public Result<Collection<Sample>, ResponseCode> addAll(ProjectId projectId, Collection<Sample> samples) {
var projectQuery = projectRepository.find(projectId);
if (projectQuery.isPresent()) {
return addAll(projectQuery.get(), samples);
}
return Result.fromError(ResponseCode.REGISTRATION_FAILED);
}

private String buildCommaSeparatedSampleIds(Collection<SampleId> sampleIds) {
Expand All @@ -100,7 +114,7 @@ public void deleteAll(Project project,

@Override
public boolean isSampleRemovable(SampleId sampleId) {
SampleCode sampleCode = qbicSampleRepository.findById(sampleId).get().sampleCode();
SampleCode sampleCode = qbicSampleRepository.findById(sampleId).orElseThrow().sampleCode();
return sampleDataRepo.canDeleteSample(sampleCode);
}

Expand Down Expand Up @@ -134,6 +148,17 @@ public void updateAll(Project project,
sampleDataRepo.updateAll(project, updatedSamples);
}

@Transactional
@Override
public void updateAll(ProjectId projectId, Collection<Sample> updatedSamples) {
var projectQuery = projectRepository.find(projectId);
if (projectQuery.isPresent()) {
updateAll(projectQuery.get(), updatedSamples);
} else {
throw new SampleRepositoryException("Could not find project with id " + projectId.value());
}
}

@Override
public List<Sample> findSamplesBySampleId(List<SampleId> sampleId) {
return qbicSampleRepository.findAllById(sampleId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ public Optional<Project> find(String projectId) throws IllegalArgumentException
return find(ProjectId.parse(projectId));
}

@PreAuthorize("hasPermission(#projectId,'life.qbic.projectmanagement.domain.model.project.Project','READ')")
public Optional<ProjectOverview> findOverview(ProjectId projectId) {
Objects.requireNonNull(projectId);
return projectOverviewLookup.query("", 0, 1, List.of(), List.of(projectId)).stream()
.findFirst();
}

public boolean isProjectCodeUnique(String projectCode) throws IllegalArgumentException {
return !projectRepository.existsProjectByProjectCode(ProjectCode.parse(projectCode));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package life.qbic.projectmanagement.application.measurement.validation;
package life.qbic.projectmanagement.application;

public class ValidationException extends RuntimeException {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package life.qbic.projectmanagement.application.measurement.validation;
package life.qbic.projectmanagement.application;

import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -16,43 +16,32 @@
*/
public class ValidationResult {

private final int validatedEntries;

private final List<String> warnings;

private final List<String> failures;

private ValidationResult() {
this.validatedEntries = 0;
this.warnings = Collections.emptyList();
this.failures = Collections.emptyList();
}

private ValidationResult(int validatedEntries) {
this.validatedEntries = validatedEntries;
this.warnings = Collections.emptyList();
this.failures = Collections.emptyList();
}

private ValidationResult(int validatedEntries, Collection<String> warnings,
private ValidationResult(Collection<String> warnings,
Collection<String> failures) {
this.validatedEntries = validatedEntries;
this.warnings = warnings.stream().toList();
this.failures = failures.stream().toList();
}

public static ValidationResult successful(int validatedEntries) {
return new ValidationResult(validatedEntries);
public static ValidationResult successful() {
return new ValidationResult();
}

public static ValidationResult withFailures(int validatedEntries,
Collection<String> failureReports) {
return new ValidationResult(validatedEntries, new ArrayList<>(), failureReports);
public static ValidationResult withFailures(Collection<String> failureReports) {
return new ValidationResult(new ArrayList<>(), failureReports);
}

public static ValidationResult successful(int validatedEntries,
Collection<String> warnings) {
return new ValidationResult(validatedEntries, warnings, new ArrayList<>());
public static ValidationResult successful(Collection<String> warnings) {
return new ValidationResult(warnings, new ArrayList<>());
}


Expand All @@ -79,20 +68,12 @@ public Collection<String> failures() {
return failures.stream().toList();
}

public int failedEntries() {
return failures.size();
}

public int validatedEntries() {
return validatedEntries;
}

public boolean containsWarnings() {
return !warnings.isEmpty();
}

public ValidationResult combine(ValidationResult otherResult) {
return new ValidationResult(this.validatedEntries + otherResult.validatedEntries,
return new ValidationResult(
Stream.concat(this.warnings.stream(), otherResult.warnings.stream()).toList(),
Stream.concat(this.failures.stream(), otherResult.failures.stream()).toList());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package life.qbic.projectmanagement.application;

import java.util.Objects;

/**
* <b><class short description - 1 Line!></b>
*
* <p><More detailed description - When to use, what it solves, etc.></p>
*
* @since <version tag>
*/
public record ValidationResultWithPayload<T>(ValidationResult validationResult, T payload) {

public ValidationResultWithPayload {
Objects.requireNonNull(validationResult);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ public Result<BatchId, ResponseCode> registerBatch(String label, boolean isPilot
return Result.fromValue(result.getValue());
}

@PreAuthorize("hasPermission(#projectId, 'life.qbic.projectmanagement.domain.model.project.Project', 'WRITE')")
public Result<BatchId, ResponseCode> addSamplesToBatch(Collection<SampleId> sampleIds, BatchId batchId, ProjectId projectId) {
var batchQuery = batchRepository.find(batchId);
if (batchQuery.isEmpty()) {
log.error("No batch with id found: " + batchId);
return Result.fromError(ResponseCode.BATCH_UPDATE_FAILED);
}
var batch = batchQuery.get();
for (SampleId sampleId : sampleIds) {
batch.addSample(sampleId);
}
batchRepository.update(batch);
return Result.fromValue(batchId);
}

public Result<BatchId, ResponseCode> addSampleToBatch(SampleId sampleId, BatchId batchId) {
var random = new Random();
while (true) {
Expand Down Expand Up @@ -197,6 +212,10 @@ public Result<BatchId, ResponseCode> editBatch(BatchId batchId, String batchLabe
return Result.fromValue(batch.batchId());
}

public void deleteBatch(BatchId batchId) {
batchRepository.deleteById(batchId);
}

private void dispatchSuccessfulBatchUpdate(BatchId batchId, ProjectId projectId) {
BatchUpdated batchUpdated = BatchUpdated.create(batchId, projectId);
DomainEventDispatcher.instance().dispatch(batchUpdated);
Expand Down
Loading

0 comments on commit be05ae4

Please sign in to comment.