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

Introduce spreadsheet-based sample registration #813

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
1d3f8ac
Provide docs for the process
sven1103 Aug 29, 2024
5166d5a
Provide readme
sven1103 Aug 29, 2024
5031100
Merge branch 'development' into feature/#680-move-from-the-spreadshee…
sven1103 Sep 3, 2024
650303f
Save work
sven1103 Sep 3, 2024
8484dba
Checkin progress
sven1103 Sep 4, 2024
9e396ac
Merge branch 'feature/#680-move-from-the-spreadsheet-component-for-sa…
sven1103 Sep 4, 2024
88bb7c2
Finish registration template
sven1103 Sep 5, 2024
94e24bb
Checkin spreadsheet update feature
sven1103 Sep 5, 2024
ccd03f2
Introduce constant for max row
sven1103 Sep 5, 2024
1c6fbb6
Add JDs
sven1103 Sep 5, 2024
cb6560b
Checkin Progress
sven1103 Sep 6, 2024
0da5b50
Merge remote-tracking branch 'origin/development' into feature/#680-m…
sven1103 Sep 6, 2024
f41dfd5
Validation for new samples
sven1103 Sep 6, 2024
ca8b126
Merge branch 'development' into feature/#680-move-from-the-spreadshee…
sven1103 Sep 6, 2024
dcc5cd9
use the FileRemovedListener
KochTobi Sep 17, 2024
c21c928
use the power of enums
KochTobi Sep 17, 2024
4d80e45
Provide async service methods
sven1103 Sep 23, 2024
2d80188
Update process diagram
sven1103 Sep 23, 2024
079210c
Provide registration API
sven1103 Sep 23, 2024
e63e983
Improve registration api
sven1103 Sep 23, 2024
635304d
Update API
sven1103 Sep 23, 2024
2de29fe
Remove gibberish code line
sven1103 Sep 23, 2024
8bba670
Add registration exception
sven1103 Sep 23, 2024
d3bbc83
Provide validation result with payload
sven1103 Sep 24, 2024
ae7c0bb
Fix broken class import
sven1103 Sep 24, 2024
828d45d
Update registration service
sven1103 Sep 24, 2024
131fcb1
Update registration service implementation
sven1103 Sep 24, 2024
975eddc
Update service method name
sven1103 Sep 24, 2024
3ae370d
Merge branch 'development' into feature/#680-move-from-the-spreadshee…
KochTobi Sep 24, 2024
264de0e
Update sample registration
sven1103 Sep 24, 2024
8071b4c
Merge branch 'feature/#680-move-from-the-spreadsheet-component-for-sa…
sven1103 Sep 24, 2024
c6bff06
fix method signature
KochTobi Sep 24, 2024
c900260
make accessing parsing result values easier
KochTobi Sep 24, 2024
7548ea6
make the option headers bold
KochTobi Sep 24, 2024
f4ff171
fix indexing
KochTobi Sep 24, 2024
e081813
move methods to helper class
KochTobi Sep 24, 2024
0950783
move measurement templates
KochTobi Sep 24, 2024
0be790e
split sample registration tempaltes
KochTobi Sep 24, 2024
c95c94f
fix imports
KochTobi Sep 24, 2024
151633c
add column enums
KochTobi Sep 24, 2024
8070b61
fix javadoc reference
KochTobi Sep 24, 2024
04d8955
fix imports
KochTobi Sep 24, 2024
6e1c00d
rename enums
KochTobi Sep 24, 2024
375d0db
use correct enum
KochTobi Sep 24, 2024
27c8c53
remove unused field
KochTobi Sep 24, 2024
11404c2
Use sanitizer
KochTobi Sep 25, 2024
8a8121b
Add SampleInformationExtractor
KochTobi Sep 25, 2024
c90a27c
remove misc address stuff
KochTobi Sep 25, 2024
0f2500a
Switch to automatic push mode instead of manual
KochTobi Sep 25, 2024
581e70e
Enable async execution for sample methods
sven1103 Sep 27, 2024
f223239
push dialog rough forms
KochTobi Sep 27, 2024
4ef242c
Fix search by Obo ID
KochTobi Sep 27, 2024
548d7f3
Show user input back to the user instead of only ontology term
KochTobi Sep 27, 2024
aafb324
handle exceptions
KochTobi Sep 27, 2024
1fdbb9a
add toString method to OntologyTerm for development log output
KochTobi Sep 27, 2024
81b13f1
Do not swallow comments
KochTobi Sep 27, 2024
c79116a
fix sample name being ignored
KochTobi Sep 30, 2024
8203d95
Remove unused methods in ValidationResult
KochTobi Sep 30, 2024
6bad40b
working but ugly dialog
KochTobi Sep 30, 2024
c753443
Merge branch 'development' into feature/#680-move-from-the-spreadshee…
KochTobi Sep 30, 2024
93e4723
inline method again
KochTobi Sep 30, 2024
086234c
Move functionality to register button
KochTobi Oct 1, 2024
1b3110f
deprecate BatchRegistrationDialog.java
KochTobi Oct 1, 2024
06171f6
move to EditSampleBatchDialog
KochTobi Oct 1, 2024
aea6947
delete template service field
KochTobi Oct 1, 2024
f986486
remove exception
KochTobi Oct 7, 2024
a8d0f40
add return
KochTobi Oct 7, 2024
729533f
Throw registration exception
sven1103 Oct 7, 2024
b8133d6
add states to wizard window
KochTobi Oct 7, 2024
99ae007
add asterix
KochTobi Oct 7, 2024
27b39d2
remove pilot checkbox
KochTobi Oct 7, 2024
786a887
check for required batch name input
KochTobi Oct 7, 2024
4a84fb7
extract constant
KochTobi Oct 8, 2024
5e4dc87
extract method
KochTobi Oct 8, 2024
441b731
improve handling of the optional upload data
KochTobi Oct 8, 2024
6884a72
Change template name to start with project code
KochTobi Oct 8, 2024
3469631
Fix TIBTerminologyServiceIntegration.java
KochTobi Oct 8, 2024
3935ef0
Fix sample batch registration
KochTobi Oct 8, 2024
15ba06c
Fix batch
sven1103 Oct 8, 2024
8efcb32
Merge branch 'feature/#680-move-from-the-spreadsheet-component-for-sa…
sven1103 Oct 8, 2024
691551b
add `*` to update sample headers
KochTobi Oct 8, 2024
242e79b
remove session scope from ClientDetailsProviderImpl
KochTobi Oct 8, 2024
3a71df0
Handle exceptions in registration future
KochTobi Oct 8, 2024
974acf7
Merge remote-tracking branch 'origin/feature/#680-move-from-the-sprea…
KochTobi Oct 8, 2024
7009be7
Fix Async for SampleValidation.java
KochTobi Oct 9, 2024
85f2cc3
Simplify result aggregation
KochTobi Oct 9, 2024
162f9f7
Add edit functionality
KochTobi Oct 11, 2024
ba893e6
apply css
KochTobi Oct 11, 2024
2e43846
show progress in edit dialog
KochTobi Oct 11, 2024
124abf1
make it look nicer
KochTobi Oct 11, 2024
b3c79f3
add css classes for headings
KochTobi Oct 14, 2024
ed35e23
Add missing condition check
KochTobi Oct 14, 2024
680c2d4
Add unknown condition information
KochTobi Oct 14, 2024
cc173ac
Change heading
KochTobi Oct 14, 2024
bf99059
Format template columns width based on the max content
sven1103 Oct 14, 2024
2eb6671
Merge branch 'feature/#680-move-from-the-spreadsheet-component-for-sa…
sven1103 Oct 14, 2024
0ac70e0
add javadoc
KochTobi Oct 14, 2024
3206fed
switch to analysis method
KochTobi Oct 14, 2024
e236060
Fix filtering
KochTobi Oct 14, 2024
0050146
style the steps
KochTobi Oct 15, 2024
ee46765
Prevent empty batch creation
KochTobi Oct 15, 2024
007a842
Fix upload remove case
KochTobi Oct 15, 2024
909ab7c
Show errors nicely
KochTobi Oct 15, 2024
a92a180
Merge branch 'development' into feature/#680-move-from-the-spreadshee…
KochTobi Oct 15, 2024
e481f80
Fix line breaks
KochTobi Oct 15, 2024
d04f986
Merge remote-tracking branch 'origin/feature/#680-move-from-the-sprea…
KochTobi Oct 15, 2024
8391c9b
fix analysis method failure message
KochTobi Oct 15, 2024
eecf8c7
change failure display
KochTobi Oct 15, 2024
283bfae
remove parenthesis
KochTobi Oct 15, 2024
5c847be
Handle empty sheet case
sven1103 Oct 15, 2024
4c070d8
Merge branch 'feature/#680-move-from-the-spreadsheet-component-for-sa…
sven1103 Oct 15, 2024
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
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