6
6
import java .util .List ;
7
7
import java .util .Objects ;
8
8
import java .util .Optional ;
9
+ import java .util .Set ;
10
+ import java .util .function .Predicate ;
11
+ import java .util .stream .Collectors ;
9
12
import life .qbic .application .commons .ApplicationException ;
13
+ import life .qbic .application .commons .ApplicationException .ErrorCode ;
14
+ import life .qbic .application .commons .ApplicationException .ErrorParameters ;
10
15
import life .qbic .application .commons .Result ;
11
16
import life .qbic .logging .api .Logger ;
12
17
import life .qbic .logging .service .LoggerFactory ;
18
+ import life .qbic .projectmanagement .application .sample .SampleInformationService ;
13
19
import life .qbic .projectmanagement .domain .model .experiment .Experiment ;
14
20
import life .qbic .projectmanagement .domain .model .experiment .ExperimentId ;
15
21
import life .qbic .projectmanagement .domain .model .experiment .ExperimentalDesign .AddExperimentalGroupResponse .ResponseCode ;
24
30
import life .qbic .projectmanagement .domain .repository .ProjectRepository ;
25
31
import org .springframework .beans .factory .annotation .Autowired ;
26
32
import org .springframework .stereotype .Service ;
33
+ import org .springframework .transaction .annotation .Transactional ;
27
34
28
35
/**
29
36
* Service that provides an API to query basic experiment information
@@ -36,11 +43,14 @@ public class ExperimentInformationService {
36
43
private static final Logger log = LoggerFactory .logger (ExperimentInformationService .class );
37
44
private final ExperimentRepository experimentRepository ;
38
45
private final ProjectRepository projectRepository ;
46
+ private final SampleInformationService sampleInformationService ;
39
47
40
48
public ExperimentInformationService (@ Autowired ExperimentRepository experimentRepository ,
41
- @ Autowired ProjectRepository projectRepository ) {
49
+ @ Autowired ProjectRepository projectRepository ,
50
+ @ Autowired SampleInformationService sampleInformationService ) {
42
51
this .experimentRepository = experimentRepository ;
43
52
this .projectRepository = projectRepository ;
53
+ this .sampleInformationService = sampleInformationService ;
44
54
}
45
55
46
56
public Optional <Experiment > find (ExperimentId experimentId ) {
@@ -64,18 +74,35 @@ private Experiment loadExperimentById(ExperimentId experimentId) {
64
74
* @param experimentId the Id of the experiment for which to add the species
65
75
* @param experimentalGroup the experimental groups to add
66
76
*/
67
- public Result < ExperimentalGroup , ResponseCode > addExperimentalGroupToExperiment (
77
+ private void addExperimentalGroupToExperiment (
68
78
ExperimentId experimentId , ExperimentalGroupDTO experimentalGroup ) {
69
79
Objects .requireNonNull (experimentalGroup , "experimental group must not be null" );
70
80
Objects .requireNonNull (experimentId , "experiment id must not be null" );
71
81
72
- Experiment experiment = loadExperimentById ( experimentId ) ;
73
- Result < ExperimentalGroup , ResponseCode > result = experiment . addExperimentalGroup (
74
- experimentalGroup . levels (), experimentalGroup . replicateCount ());
75
- if ( result . isValue ()) {
76
- experimentRepository . update ( experiment );
82
+ List < VariableLevel > varLevels = experimentalGroup . levels ;
83
+ if ( varLevels . isEmpty ()) {
84
+ throw new ApplicationException ( "No experimental variable was selected" ,
85
+ ErrorCode . NO_CONDITION_SELECTED ,
86
+ ErrorParameters . empty () );
77
87
}
78
- return result ;
88
+
89
+ Experiment experiment = loadExperimentById (experimentId );
90
+ Result <ExperimentalGroup , ResponseCode > result = experiment .addExperimentalGroup (
91
+ experimentalGroup .name (), experimentalGroup .levels (), experimentalGroup .replicateCount ());
92
+ if (result .isValue ()) {
93
+ experimentRepository .update (experiment );
94
+ } else {
95
+ ResponseCode responseCode = result .getError ();
96
+ if (responseCode .equals (ResponseCode .CONDITION_EXISTS )) {
97
+ throw new ApplicationException ("A group with the variable levels %s already exists." .formatted (varLevels .toString ()),
98
+ ErrorCode .DUPLICATE_GROUP_SELECTED ,
99
+ ErrorParameters .empty ());
100
+ } else {
101
+ throw new ApplicationException (
102
+ "Could not save one or more experimental groups %s %nReason: %s" .formatted (
103
+ experimentalGroup .toString (), responseCode ));
104
+ }
105
+ }
79
106
}
80
107
81
108
/**
@@ -88,7 +115,7 @@ public Result<ExperimentalGroup, ResponseCode> addExperimentalGroupToExperiment(
88
115
public List <ExperimentalGroupDTO > getExperimentalGroups (ExperimentId experimentId ) {
89
116
Experiment experiment = loadExperimentById (experimentId );
90
117
return experiment .getExperimentalGroups ().stream ()
91
- .map (it -> new ExperimentalGroupDTO (it .condition ().getVariableLevels (), it .sampleSize ()))
118
+ .map (it -> new ExperimentalGroupDTO (it .id (), it . name (), it . condition ().getVariableLevels (), it .sampleSize ()))
92
119
.toList ();
93
120
}
94
121
@@ -254,42 +281,80 @@ public List<ExperimentalVariable> getVariablesOfExperiment(ExperimentId experime
254
281
255
282
/**
256
283
* Deletes all experimental groups in a given experiment.
257
- * <p>
258
- * This method does not check if samples are already.
259
284
*
260
285
* @param id the experiment identifier of the experiment the experimental groups are going to be
261
286
* deleted.
262
287
* @since 1.0.0
263
288
*/
264
- public void deleteAllExperimentalGroups (ExperimentId id ) {
289
+ public void deleteExperimentalGroupsWithIds (ExperimentId id , List <Long > groupIds ) {
290
+ var queryResult = sampleInformationService .retrieveSamplesForExperiment (id );
291
+ if (queryResult .isError ()) {
292
+ throw new ApplicationException ("experiment (%s) converting %s to %s" .formatted (id ,
293
+ queryResult .getError (), DeletionService .ResponseCode .QUERY_FAILED ),
294
+ ErrorCode .GENERAL ,
295
+ ErrorParameters .empty ());
296
+ }
297
+ if (queryResult .isValue () && !queryResult .getValue ().isEmpty ()) {
298
+ throw new ApplicationException ("Could not edit experimental groups because samples are already registered." ,
299
+ ErrorCode .SAMPLES_ATTACHED_TO_EXPERIMENT ,
300
+ ErrorParameters .empty ());
301
+ }
265
302
Experiment experiment = loadExperimentById (id );
266
- experiment .removeAllExperimentalGroups ( );
303
+ experiment .removeExperimentalGroups ( groupIds );
267
304
experimentRepository .update (experiment );
268
305
}
269
306
307
+ @ Transactional
270
308
/**
271
- * Adds experimental groups to an experiment
309
+ * Updates experimental groups in a given experiment.
310
+ *
311
+ * Compares the provided list of experimental groups of an experiment with the persistent state.
312
+ * Removes groups from the experiment that are not in the new list, adds groups that are not in
313
+ * the experiment yet and updates the other groups of the experiment.
272
314
*
273
- * @param experimentId the experiment to add the groups to
274
- * @param experimentalGroupDTOS the group information
275
- * @return either the collection of added groups or an appropriate response code
315
+ * @param id the experiment identifier of the experiment whose groups should be updated
316
+ * @param experimentalGroupDTOS the new list of experimental groups including all updates
317
+ * @since 1.0.0
276
318
*/
277
- public Result <Collection <ExperimentalGroup >, ResponseCode > addExperimentalGroupsToExperiment (
278
- ExperimentId experimentId , List <ExperimentalGroupDTO > experimentalGroupDTOS ) {
279
- Experiment experiment = loadExperimentById (experimentId );
280
- List <ExperimentalGroup > addedGroups = new ArrayList <>();
281
- for (ExperimentalGroupDTO experimentalGroupDTO : experimentalGroupDTOS ) {
282
- Result <ExperimentalGroup , ResponseCode > result = experiment .addExperimentalGroup (
283
- experimentalGroupDTO .levels (),
284
- experimentalGroupDTO .replicateCount ());
285
- if (result .isError ()) {
286
- return Result .fromError (result .getError ());
319
+ public void updateExperimentalGroupsOfExperiment (ExperimentId experimentId ,
320
+ List <ExperimentalGroupDTO > experimentalGroupDTOS ) {
321
+
322
+ // check for duplicates
323
+ List <List <VariableLevel >> distinctLevels = experimentalGroupDTOS .stream ()
324
+ .map (ExperimentalGroupDTO ::levels ).distinct ().toList ();
325
+ if (distinctLevels .size () < experimentalGroupDTOS .size ()) {
326
+ throw new ApplicationException ("Duplicate experimental group was selected" ,
327
+ ErrorCode .DUPLICATE_GROUP_SELECTED ,
328
+ ErrorParameters .empty ());
329
+ }
330
+
331
+ List <ExperimentalGroup > existingGroups = experimentalGroupsFor (experimentId );
332
+ List <Long > idsToDelete = getGroupIdsToDelete (existingGroups , experimentalGroupDTOS );
333
+ if (!idsToDelete .isEmpty ()) {
334
+ deleteExperimentalGroupsWithIds (experimentId , idsToDelete );
335
+ }
336
+
337
+ for (ExperimentalGroupDTO group : experimentalGroupDTOS ) {
338
+ if (group .id () == -1 ) {
339
+ addExperimentalGroupToExperiment (experimentId , group );
287
340
} else {
288
- addedGroups . add ( result . getValue () );
341
+ updateExperimentalGroupOfExperiment ( experimentId , group );
289
342
}
290
343
}
291
- experimentRepository .update (experiment );
292
- return Result .fromValue (addedGroups );
344
+ }
345
+
346
+ private void updateExperimentalGroupOfExperiment (ExperimentId experimentId , ExperimentalGroupDTO group ) {
347
+ Experiment experiment = loadExperimentById (experimentId );
348
+ experiment .updateExperimentalGroup (group .id (), group .name (), group .levels (), group .replicateCount ());
349
+ }
350
+
351
+ private List <Long > getGroupIdsToDelete (List <ExperimentalGroup > existingGroups ,
352
+ List <ExperimentalGroupDTO > newGroups ) {
353
+ Set <Long > newIds = newGroups .stream ().map (ExperimentalGroupDTO ::id ).collect (Collectors .toSet ());
354
+ return existingGroups .stream ()
355
+ .map (ExperimentalGroup ::id )
356
+ .filter (Predicate .not (newIds ::contains ))
357
+ .toList ();
293
358
}
294
359
295
360
public void editExperimentInformation (ExperimentId experimentId , String experimentName ,
@@ -305,10 +370,12 @@ public void editExperimentInformation(ExperimentId experimentId, String experime
305
370
/**
306
371
* Information about an experimental group
307
372
*
373
+ * @param id id, -1 for new groups
374
+ * @param name the name of the group - can be empty
308
375
* @param levels the levels in the condition of the group
309
376
* @param replicateCount the number of biological replicates
310
377
*/
311
- public record ExperimentalGroupDTO (List <VariableLevel > levels , int replicateCount ) {
378
+ public record ExperimentalGroupDTO (long id , String name , List <VariableLevel > levels , int replicateCount ) {
312
379
313
380
}
314
381
}
0 commit comments