View Javadoc
1   package fr.ifremer.tutti.persistence.service;
2   
3   /*
4    * #%L
5    * Tutti :: Persistence
6    * %%
7    * Copyright (C) 2012 - 2014 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  import com.google.common.base.Preconditions;
26  import com.google.common.collect.ArrayListMultimap;
27  import com.google.common.collect.Lists;
28  import fr.ifremer.adagio.core.dao.administration.programStrategy.Program;
29  import fr.ifremer.adagio.core.dao.administration.user.DepartmentId;
30  import fr.ifremer.adagio.core.dao.administration.user.DepartmentImpl;
31  import fr.ifremer.adagio.core.dao.administration.user.PersonId;
32  import fr.ifremer.adagio.core.dao.administration.user.PersonImpl;
33  import fr.ifremer.adagio.core.dao.data.batch.Batch;
34  import fr.ifremer.adagio.core.dao.data.batch.CatchBatch;
35  import fr.ifremer.adagio.core.dao.data.batch.SortingBatch;
36  import fr.ifremer.adagio.core.dao.data.operation.FishingOperationImpl;
37  import fr.ifremer.adagio.core.dao.data.sample.Sample;
38  import fr.ifremer.adagio.core.dao.referential.QualityFlagCode;
39  import fr.ifremer.adagio.core.dao.referential.QualityFlagImpl;
40  import fr.ifremer.adagio.core.dao.referential.pmfm.Matrix;
41  import fr.ifremer.adagio.core.dao.referential.pmfm.MatrixId;
42  import fr.ifremer.adagio.core.dao.referential.pmfm.MatrixImpl;
43  import fr.ifremer.adagio.core.dao.referential.taxon.ReferenceTaxonImpl;
44  import fr.ifremer.tutti.persistence.entities.CaracteristicMap;
45  import fr.ifremer.tutti.persistence.entities.TuttiEntities;
46  import fr.ifremer.tutti.persistence.entities.data.CopyIndividualObservationMode;
47  import fr.ifremer.tutti.persistence.entities.data.FishingOperation;
48  import fr.ifremer.tutti.persistence.entities.data.IndividualObservationBatch;
49  import fr.ifremer.tutti.persistence.entities.data.IndividualObservationBatchs;
50  import fr.ifremer.tutti.persistence.entities.referential.Caracteristic;
51  import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue;
52  import fr.ifremer.tutti.persistence.entities.referential.Species;
53  import fr.ifremer.tutti.persistence.service.referential.CaracteristicPersistenceService;
54  import fr.ifremer.tutti.persistence.service.referential.SpeciesPersistenceService;
55  import fr.ifremer.tutti.persistence.service.util.BatchPersistenceHelper;
56  import fr.ifremer.tutti.persistence.service.util.SamplePersistenceHelper;
57  import fr.ifremer.tutti.persistence.service.util.SynchronizationStatusHelper;
58  import org.apache.commons.collections4.CollectionUtils;
59  import org.apache.commons.lang3.StringUtils;
60  import org.apache.commons.logging.Log;
61  import org.apache.commons.logging.LogFactory;
62  import org.hibernate.type.IntegerType;
63  import org.springframework.stereotype.Service;
64  
65  import javax.annotation.Resource;
66  import java.io.Serializable;
67  import java.util.ArrayList;
68  import java.util.Collection;
69  import java.util.Collections;
70  import java.util.Iterator;
71  import java.util.LinkedHashSet;
72  import java.util.List;
73  import java.util.Map;
74  import java.util.Objects;
75  import java.util.Set;
76  import java.util.stream.Collectors;
77  
78  /**
79   * @author Tony Chemit - chemit@codelutin.com
80   * @since 1.4
81   */
82  @Service("individualObservationBatchPersistenceService")
83  public class IndividualObservationBatchPersistenceServiceImpl extends AbstractPersistenceService implements IndividualObservationBatchPersistenceService {
84  
85      /** Logger. */
86      private static final Log log =
87              LogFactory.getLog(IndividualObservationBatchPersistenceServiceImpl.class);
88  
89      @Resource(name = "caracteristicPersistenceService")
90      private CaracteristicPersistenceService caracteristicService;
91  
92      @Resource(name = "speciesPersistenceService")
93      private SpeciesPersistenceService speciesService;
94  
95      @Resource(name = "attachmentPersistenceService")
96      protected AttachmentPersistenceService attachmentPersistenceService;
97  
98      @Resource(name = "samplePersistenceHelper")
99      protected SamplePersistenceHelper samplePersistenceHelper;
100 
101     @Resource(name = "batchPersistenceHelper")
102     protected BatchPersistenceHelper batchHelper;
103 
104     @Resource(name = "synchronizationStatusHelper")
105     protected SynchronizationStatusHelper synchronizationStatusHelper;
106 
107     @Resource(name = "fishingOperationPersistenceService")
108     protected FishingOperationPersistenceService fishingOperationPersistenceService;
109 
110     protected Caracteristic sampleCodeCaracteristic;
111 
112     @Override
113     public void init() {
114         super.init();
115 
116         sampleCodeCaracteristic = caracteristicService.getSampleCodeCaracteristic();
117     }
118 
119     @Override
120     public List<IndividualObservationBatch> getAllIndividualObservationBatchsForCruise(Integer cruiseId) {
121         Preconditions.checkNotNull(cruiseId);
122 
123         List<IndividualObservationBatch> result = new ArrayList<>();
124         List<Integer> allFishingOperationIds = fishingOperationPersistenceService.getAllFishingOperationIds(cruiseId);
125         allFishingOperationIds.forEach(fishingOperationId -> {
126 
127             Iterator<Object[]> list = queryList("allFishingOperationSamplesWithBatch",
128                                                 "fishingOperationId", IntegerType.INSTANCE, fishingOperationId);
129 
130             List<IndividualObservationBatch> resultForFishingOperation = toBeanListWithFishingOperation(list, fishingOperationId);
131             result.addAll(resultForFishingOperation);
132 
133         });
134         return Collections.unmodifiableList(result);
135 
136     }
137 
138     @Override
139     public boolean isSamplingCodeAvailable(Integer cruiseId, Integer referenceTaxonId, String samplingCodeSuffix) {
140 
141         Preconditions.checkNotNull(cruiseId);
142         Preconditions.checkNotNull(referenceTaxonId);
143         Preconditions.checkNotNull(samplingCodeSuffix);
144 
145         List<Integer> allFishingOperationIds = fishingOperationPersistenceService.getAllFishingOperationIds(cruiseId);
146         for (Integer fishingOperationId : allFishingOperationIds) {
147 
148             Iterator<Integer> list = queryListTyped("allFishingOperationSampleIdsWithBatchForSpecies",
149                                                     "fishingOperationId", IntegerType.INSTANCE, fishingOperationId,
150                                                     "referenceTaxonId", IntegerType.INSTANCE, referenceTaxonId);
151 
152             while (list.hasNext()) {
153 
154                 Integer sampleId = list.next();
155 
156                 Serializable sampleMeasurementValue = samplePersistenceHelper.getSampleMeasurementValue(sampleId, sampleCodeCaracteristic);
157                 if (sampleMeasurementValue != null) {
158 
159                     if (sampleMeasurementValue.toString().endsWith(samplingCodeSuffix)) {
160 
161                         // sampling code suffix found, stop NOW!
162                         return false;
163 
164                     }
165 
166                 }
167 
168             }
169 
170         }
171 
172         // Free to use
173         return true;
174 
175     }
176 
177     @Override
178     public List<IndividualObservationBatch> getAllIndividualObservationBatchsForFishingOperation(Integer fishingOperationId) {
179         Preconditions.checkNotNull(fishingOperationId);
180 
181         Iterator<Object[]> list = queryList("allFishingOperationSamplesWithBatch",
182                                             "fishingOperationId", IntegerType.INSTANCE, fishingOperationId);
183 
184         List<IndividualObservationBatch> result = toBeanListWithFishingOperation(list, fishingOperationId);
185 
186         return Collections.unmodifiableList(result);
187 
188     }
189 
190     @Override
191     public List<IndividualObservationBatch> getAllIndividualObservationBatchsForBatch(Integer batchId) {
192         Preconditions.checkNotNull(batchId);
193 
194         Iterator<Object[]> list = queryList("allFishingOperationSamplesForBatch",
195                                             "batchId", IntegerType.INSTANCE, batchId);
196 
197         List<IndividualObservationBatch> result = toBeanList(list);
198         return Collections.unmodifiableList(result);
199 
200     }
201 
202     @Override
203     public List<IndividualObservationBatch> createIndividualObservationBatches(FishingOperation fishingOperation, Collection<IndividualObservationBatch> individualObservations) {
204 
205         Preconditions.checkNotNull(individualObservations);
206 
207         // remove null observations (says with no length step caracteristic)
208         List<IndividualObservationBatch> notNullObservations = individualObservations.stream()
209                                                                                      .filter(source -> source.getLengthStepCaracteristic() != null)
210                                                                                      .collect(Collectors.toList());
211 
212         ArrayListMultimap<Integer, IndividualObservationBatch> individualObservationBatchesByBatchId = ArrayListMultimap.create();
213 
214         notNullObservations.forEach(individualObservationBatch -> individualObservationBatchesByBatchId.put(individualObservationBatch.getBatchId(), individualObservationBatch));
215 
216         for (Map.Entry<Integer, Collection<IndividualObservationBatch>> entry : individualObservationBatchesByBatchId.asMap().entrySet()) {
217 
218             Integer speciesBatchId = entry.getKey();
219             Collection<IndividualObservationBatch> individualObservationBatches = entry.getValue();
220 
221             createOrSave(speciesBatchId, individualObservationBatches);
222 
223         }
224 
225         return Collections.unmodifiableList(notNullObservations);
226 
227     }
228 
229     @Override
230     public List<IndividualObservationBatch> saveBatchIndividualObservation(Integer speciesBatchId,
231                                                                            List<IndividualObservationBatch> individualObservation) {
232 
233         Preconditions.checkNotNull(speciesBatchId);
234         Preconditions.checkNotNull(individualObservation);
235 
236         // remove null observations (says with no length step caracteristic)
237         List<IndividualObservationBatch> notNullObservations = individualObservation.stream()
238                                                                                     .filter(source -> source.getLengthStepCaracteristic() != null)
239                                                                                     .collect(Collectors.toList());
240 
241         createOrSave(speciesBatchId, individualObservation);
242 
243         return Collections.unmodifiableList(notNullObservations);
244 
245     }
246 
247     @Override
248     public void deleteAllIndividualObservationsForFishingOperation(Integer fishingOperationId) {
249 
250         Set<Integer> individualObservationIds = getAllIndividualObservationBatchIds(fishingOperationId);
251 
252         if (log.isInfoEnabled()) {
253             log.info(String.format("[Fishing Operation: %d] Delete %d individual observations.", fishingOperationId, individualObservationIds.size()));
254         }
255 
256         individualObservationIds.forEach(samplePersistenceHelper::deleteSample);
257 
258     }
259 
260     @Override
261     public void deleteAllIndividualObservationsForBatch(Integer speciesBatchId) {
262 
263         Set<Integer> allSpeciesBatchIds = batchHelper.getBatchIds(speciesBatchId);
264 
265         for (Integer aSpeciesBatchId : allSpeciesBatchIds) {
266 
267             Set<Integer> individualObservationIds = getAllIndividualObservationBatchIdsForBatch(aSpeciesBatchId);
268 
269             if (log.isInfoEnabled()) {
270                 log.info(String.format("[Species batch: %d] Delete %d individual observations.", aSpeciesBatchId, individualObservationIds.size()));
271             }
272             individualObservationIds.forEach(samplePersistenceHelper::deleteSample);
273 
274         }
275 
276     }
277 
278     // ------------------------------------------------------------------------//
279     // -- Internal methods                                                   --//
280     // ------------------------------------------------------------------------//
281 
282     protected void beanToEntity(IndividualObservationBatch source, Sample target, Batch batch) {
283 
284         // operation
285         fr.ifremer.adagio.core.dao.data.operation.FishingOperation fishingOperation;
286 
287         fishingOperation = load(FishingOperationImpl.class, source.getFishingOperation().getIdAsInt());
288 
289         // Link to fishing operation
290         target.setFishingOperation(fishingOperation);
291 
292         // Link to parent batch
293         target.setBatch(batch);
294         target.setFishingOperation(fishingOperation);
295 
296         if (TuttiEntities.isNew(source)) {
297 
298             // Label
299             String label = batch.getId() + "_" + source.getSpecies().getReferenceTaxonId();
300             target.setLabel(label);
301 
302             // Matrix (product / batch)
303             Matrix matrix = load(MatrixImpl.class, MatrixId.PRODUCE_BATCH.getValue());
304             target.setMatrix(matrix);
305 
306             // IndividualCount
307             target.setIndividualCount((short) 1);
308 
309             // Quality Flag
310             target.setQualityFlag(load(QualityFlagImpl.class, QualityFlagCode.NOTQUALIFIED.getValue()));
311 
312             // Sample Date
313             if (target.getSampleDate() == null) {
314                 target.setSampleDate(fishingOperation.getFishingStartDateTime());
315             }
316 
317             // Create Date
318             target.setCreationDate(fishingOperation.getFishingStartDateTime());
319 
320             // Recorder Departement
321             target.setRecorderDepartment(load(DepartmentImpl.class, DepartmentId.UNKNOWN_RECORDER_DEPARTMENT.getValue()));
322 
323             // Recorder Person
324             target.setRecorderPerson(load(PersonImpl.class, PersonId.UNKNOWN_RECORDER_PERSON.getValue()));
325 
326             // Program
327             Program program = fishingOperation.getFishingTrip().getProgram();
328             target.setProgram(program);
329         }
330 
331         // Id
332         target.setId(source.getIdAsInt());
333 
334         // Comment
335         target.setComments(source.getComment());
336 
337         // ReferenceTaxon
338         Species species = source.getSpecies();
339         Integer referenceTaxonId = species.getReferenceTaxonId();
340         target.setReferenceTaxon(load(ReferenceTaxonImpl.class, referenceTaxonId));
341 
342         // Taxongroup TODO
343 
344         // FishingAreas TODO
345 
346         // Prepare sample measurements
347 
348         CaracteristicMap caracteristics = samplePersistenceHelper.extractCommonSampleCaracteristics(source);
349 
350         CopyIndividualObservationMode copyIndividualObservationMode = source.getCopyIndividualObservationMode();
351         Objects.requireNonNull(copyIndividualObservationMode, "Any individual observation requires a copy mode");
352 
353         Caracteristic copyIndividualObservationModeCaracteristic = caracteristicService.getCopyIndividualObservationModeCaracteristic();
354         CaracteristicQualitativeValue qualitativeValue = copyIndividualObservationMode.getQualitativeValue(copyIndividualObservationModeCaracteristic);
355         caracteristics.put(copyIndividualObservationModeCaracteristic, qualitativeValue);
356 
357         if (StringUtils.isNotBlank(source.getSamplingCode())) {
358             Caracteristic sampleCodeCaracteristic = caracteristicService.getSampleCodeCaracteristic();
359             caracteristics.put(sampleCodeCaracteristic, source.getSamplingCode());
360         }
361 
362         samplePersistenceHelper.setSampleMeasurements(target, caracteristics);
363 
364     }
365 
366     protected IndividualObservationBatch toBean(Object[] source) {
367 
368         IndividualObservationBatch batch = IndividualObservationBatchs.newIndividualObservationBatch();
369 
370         int colIndex = 0;
371 
372         // Id
373         batch.setId((Integer) source[colIndex++]);
374 
375         // BatchId
376         batch.setBatchId((Integer) source[colIndex++]);
377 
378         // TaxonId
379         Integer taxonId = (Integer) source[colIndex++];
380         Species species = speciesService.getSpeciesByReferenceTaxonId(taxonId);
381         batch.setSpecies(species);
382 
383         // Comment
384         batch.setComment((String) source[colIndex]);
385 
386         // synchronizationStatus
387         batch.setSynchronizationStatus((String) source[colIndex]);
388 
389         // Sample Measurements
390         batch.setCaracteristics(new CaracteristicMap());
391 
392         // fill all measurements
393         fillSampleMeasurements(batch);
394 
395         return batch;
396 
397     }
398 
399     protected List<IndividualObservationBatch> toBeanList(Iterator<Object[]> list) {
400 
401         List<IndividualObservationBatch> result = Lists.newArrayList();
402 
403         while (list.hasNext()) {
404 
405             IndividualObservationBatch individualObservationBatch = toBean(list.next());
406             result.add(individualObservationBatch);
407 
408         }
409 
410         return result;
411 
412     }
413 
414     protected List<IndividualObservationBatch> toBeanListWithFishingOperation(Iterator<Object[]> list, Integer fishingOperationId) {
415 
416         List<IndividualObservationBatch> result = toBeanList(list);
417         FishingOperation fishingOperation = fishingOperationPersistenceService.getFishingOperation(fishingOperationId);
418         result.forEach(individualObservationBatch -> individualObservationBatch.setFishingOperation(fishingOperation));
419         return result;
420     }
421 
422     protected Batch getBatch(Integer operationId) {
423         Preconditions.checkNotNull(operationId);
424 
425         return batchHelper.getRootCatchBatchByFishingOperationId(operationId, false);
426     }
427 
428     protected void fillSampleMeasurements(IndividualObservationBatch batch) {
429 
430         samplePersistenceHelper.loadSampleMeasurements(batch);
431 
432         CaracteristicMap caracteristics = batch.getCaracteristics();
433 
434         Caracteristic copyIndividualObservationModeCaracteristic = caracteristicService.getCopyIndividualObservationModeCaracteristic();
435 
436         CaracteristicQualitativeValue copyIndividualObservationMode = caracteristics.removeQualitativeValue(copyIndividualObservationModeCaracteristic);
437         if (copyIndividualObservationMode != null) {
438             batch.setCopyIndividualObservationMode(CopyIndividualObservationMode.valueOf(copyIndividualObservationMode.getIdAsInt()));
439         } else {
440             batch.setCopyIndividualObservationMode(CopyIndividualObservationMode.NOTHING);
441         }
442 
443         Caracteristic sampleCodeCaracteristic = caracteristicService.getSampleCodeCaracteristic();
444         String sampleCode = caracteristics.removeStringValue(sampleCodeCaracteristic);
445         batch.setSamplingCode(sampleCode);
446     }
447 
448     protected Set<Integer> getAllIndividualObservationBatchIdsForBatch(Integer batchId) {
449 
450         Preconditions.checkNotNull(batchId);
451         Iterator<Integer> list = queryListTyped("allFishingOperationSampleIdsForBatch",
452                                                 "batchId", IntegerType.INSTANCE, batchId);
453 
454         return toIds(list);
455 
456     }
457 
458     protected Set<Integer> getAllIndividualObservationBatchIds(Integer fishingOperationId) {
459 
460         Preconditions.checkNotNull(fishingOperationId);
461 
462         Iterator<Integer> list = queryListTyped("allFishingOperationSampleIds",
463                                                 "fishingOperationId", IntegerType.INSTANCE, fishingOperationId);
464 
465         return toIds(list);
466 
467     }
468 
469     protected Set<Integer> toIds(Iterator<Integer> list) {
470 
471         Set<Integer> result = new LinkedHashSet<>();
472 
473         while (list.hasNext()) {
474             Integer id = list.next();
475             result.add(id);
476         }
477         return result;
478 
479     }
480 
481     protected void createOrSave(Integer speciesBatchId, Collection<IndividualObservationBatch> notNullObservations) {
482 
483         CatchBatch catchBatch = batchHelper.getRootCatchBatchByBatchId(speciesBatchId);
484 
485         if (catchBatch == null) {
486 
487             throw new IllegalStateException("Can't find catch batch from batchId: " + speciesBatchId);
488 
489         }
490 
491         // Synchronization status
492         synchronizationStatusHelper.setDirty(catchBatch);
493 
494         // Retrieve batch
495         SortingBatch batch = batchHelper.getSortingBatchById(catchBatch, speciesBatchId);
496         if (batch == null) {
497 
498             throw new IllegalStateException("Can't find batch from batchId: " + speciesBatchId);
499 
500         }
501 
502         // Remember child ids, to remove unchanged item (see at bottom in this method)
503         Set<Integer> notUpdatedChildIds = getAllIndividualObservationBatchIdsForBatch(speciesBatchId);
504 
505         for (IndividualObservationBatch source : notNullObservations) {
506 
507             Sample target;
508             if (TuttiEntities.isNew(source)) {
509 
510                 // Not existing batch
511                 target = Sample.Factory.newInstance();
512 
513                 // Fill the sample from the source
514                 beanToEntity(source, target, batch);
515 
516                 // Create the targeted sample, then update the source id
517                 samplePersistenceHelper.create(target);
518                 source.setId(String.valueOf(target.getId()));
519 
520                 if (log.isInfoEnabled()) {
521                     log.info("Create individual observation (" + source.getRankOrder() + "): " + target.getId());
522                 }
523 
524             } else {
525 
526                 // Existing batch
527                 target = samplePersistenceHelper.load(source.getIdAsInt());
528 
529                 // Fill the sorting batch from the source
530                 beanToEntity(source, target, batch);
531                 samplePersistenceHelper.update(target);
532 
533                 // Remove id from id to remove
534                 notUpdatedChildIds.remove(target.getId());
535 
536                 if (log.isInfoEnabled()) {
537                     log.info("Update individual observation (" + source.getRankOrder() + "): " + target.getId());
538                 }
539             }
540 
541             synchronizationStatusHelper.setDirty(source);
542 
543         }
544 
545         if (CollectionUtils.isNotEmpty(notUpdatedChildIds)) {
546 
547             // Remove obsolete individual observations
548             for (Integer observationId : notUpdatedChildIds) {
549 
550                 if (log.isInfoEnabled()) {
551                     log.info("Remove obsolete individual observation: " + observationId);
552                 }
553                 samplePersistenceHelper.deleteSample(observationId);
554             }
555 
556         }
557 
558     }
559 }