1 package fr.ifremer.tutti.service.pupitri;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import com.google.common.collect.ArrayListMultimap;
26 import com.google.common.collect.ListMultimap;
27 import com.google.common.collect.Lists;
28 import com.google.common.collect.Maps;
29 import fr.ifremer.adagio.core.dao.referential.ObjectTypeCode;
30 import fr.ifremer.adagio.core.dao.referential.pmfm.PmfmId;
31 import fr.ifremer.adagio.core.dao.referential.pmfm.QualitativeValueId;
32 import fr.ifremer.tutti.persistence.entities.data.Attachment;
33 import fr.ifremer.tutti.persistence.entities.data.Attachments;
34 import fr.ifremer.tutti.persistence.entities.data.BatchContainer;
35 import fr.ifremer.tutti.persistence.entities.data.CatchBatch;
36 import fr.ifremer.tutti.persistence.entities.data.FishingOperation;
37 import fr.ifremer.tutti.persistence.entities.data.SpeciesBatch;
38 import fr.ifremer.tutti.persistence.entities.data.SpeciesBatchs;
39 import fr.ifremer.tutti.persistence.entities.protocol.SpeciesProtocol;
40 import fr.ifremer.tutti.persistence.entities.protocol.SpeciesProtocols;
41 import fr.ifremer.tutti.persistence.entities.protocol.TuttiProtocol;
42 import fr.ifremer.tutti.persistence.entities.referential.Caracteristic;
43 import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValue;
44 import fr.ifremer.tutti.persistence.entities.referential.CaracteristicQualitativeValues;
45 import fr.ifremer.tutti.persistence.entities.referential.Species;
46 import fr.ifremer.tutti.persistence.entities.referential.Speciess;
47 import fr.ifremer.tutti.persistence.entities.referential.TaxonCache;
48 import fr.ifremer.tutti.persistence.entities.referential.TaxonCaches;
49 import fr.ifremer.tutti.service.AbstractTuttiService;
50 import fr.ifremer.tutti.service.DecoratorService;
51 import fr.ifremer.tutti.service.PdfGeneratorService;
52 import fr.ifremer.tutti.service.PersistenceService;
53 import fr.ifremer.tutti.service.TuttiDataContext;
54 import fr.ifremer.tutti.service.TuttiServiceContext;
55 import fr.ifremer.tutti.service.csv.ImportModelWithHeader;
56 import fr.ifremer.tutti.service.pupitri.csv.CarrouselRow;
57 import fr.ifremer.tutti.service.pupitri.csv.CarrouselRowModel;
58 import fr.ifremer.tutti.service.pupitri.csv.TrunkRow;
59 import fr.ifremer.tutti.service.pupitri.csv.TrunkRowModel;
60 import fr.ifremer.tutti.service.pupitri.report.PupitriImportReportModel;
61 import fr.ifremer.tutti.service.pupitri.report.PupitriImportReportRow;
62 import fr.ifremer.tutti.type.WeightUnit;
63 import org.apache.commons.collections4.CollectionUtils;
64 import org.apache.commons.io.FileUtils;
65 import org.apache.commons.lang3.StringUtils;
66 import org.apache.commons.logging.Log;
67 import org.apache.commons.logging.LogFactory;
68 import org.nuiton.csv.Import;
69 import org.nuiton.decorator.Decorator;
70 import org.nuiton.jaxx.application.ApplicationIOUtil;
71 import org.nuiton.jaxx.application.ApplicationTechnicalException;
72
73 import java.io.File;
74 import java.io.IOException;
75 import java.io.Reader;
76 import java.io.Serializable;
77 import java.text.DateFormat;
78 import java.text.SimpleDateFormat;
79 import java.util.Collection;
80 import java.util.Collections;
81 import java.util.HashSet;
82 import java.util.List;
83 import java.util.Locale;
84 import java.util.Map;
85 import java.util.Objects;
86 import java.util.function.Predicate;
87 import java.util.stream.Collectors;
88
89 import static org.nuiton.i18n.I18n.n;
90 import static org.nuiton.i18n.I18n.t;
91
92
93
94
95
96 public class PupitriImportService extends AbstractTuttiService {
97
98 private static final Log log =
99 LogFactory.getLog(PupitriImportService.class);
100
101 protected DecoratorService decoratorService;
102
103 protected PersistenceService persistenceService;
104
105 protected PdfGeneratorService pdfGeneratorService;
106
107 protected TuttiDataContext dataContext;
108
109 protected CaracteristicQualitativeValue sortedCaracteristic;
110
111 protected CaracteristicQualitativeValue unsortedCaracteristic;
112
113 protected Map<Signs, CaracteristicQualitativeValue> signsToCaracteristicValue;
114
115 @Override
116 public void setServiceContext(TuttiServiceContext context) {
117 super.setServiceContext(context);
118 persistenceService = getService(PersistenceService.class);
119 decoratorService = getService(DecoratorService.class);
120 pdfGeneratorService = getService(PdfGeneratorService.class);
121 dataContext = context.getDataContext();
122
123 signsToCaracteristicValue = Maps.newEnumMap(Signs.class);
124
125 {
126 Caracteristic caracteristic =
127 persistenceService.getSortedUnsortedCaracteristic();
128
129 sortedCaracteristic = CaracteristicQualitativeValues.getQualitativeValue(caracteristic, QualitativeValueId.SORTED_VRAC.getValue());
130 unsortedCaracteristic = CaracteristicQualitativeValues.getQualitativeValue(caracteristic, QualitativeValueId.SORTED_HORS_VRAC.getValue());
131 }
132
133 {
134 Caracteristic caracteristic = persistenceService.getSexCaracteristic();
135
136 Signs.DEFAULT.registerSign(caracteristic, signsToCaracteristicValue);
137 Signs.FEMALE.registerSign(caracteristic, signsToCaracteristicValue);
138 Signs.MALE.registerSign(caracteristic, signsToCaracteristicValue);
139 }
140 {
141 Caracteristic caracteristic = persistenceService.getSizeCategoryCaracteristic();
142
143 Signs.SMALL.registerSign(caracteristic, signsToCaracteristicValue);
144 Signs.MEDIUM.registerSign(caracteristic, signsToCaracteristicValue);
145 Signs.BIG.registerSign(caracteristic, signsToCaracteristicValue);
146 }
147 }
148
149
150
151
152
153
154
155
156
157 public PupitriImportResult importPupitri(File trunkFile,
158 File carrouselFile,
159 FishingOperation operation,
160 CatchBatch catchBatch) {
161
162 return importPupitri(trunkFile, carrouselFile, operation, catchBatch, false);
163 }
164
165
166
167
168
169
170
171
172
173
174 public PupitriImportResult importPupitri(File trunkFile,
175 File carrouselFile,
176 FishingOperation operation,
177 CatchBatch catchBatch,
178 boolean importMissingCategoryBatches) {
179
180 PupitriImportResult result = readImportPupitri(trunkFile, carrouselFile, operation, importMissingCategoryBatches);
181 return saveImportPupitri(trunkFile, carrouselFile, operation, catchBatch, importMissingCategoryBatches, result);
182 }
183
184
185
186
187
188
189
190
191 public PupitriImportResult readImportPupitri(File trunkFile,
192 File carrouselFile,
193 FishingOperation operation,
194 boolean importMissingCategoryBatches) {
195
196 PupitriImportResult result = new PupitriImportResult();
197
198 importPupitriTrunk(result, trunkFile, operation);
199 importPupitriCarrousel(result, carrouselFile, operation, importMissingCategoryBatches);
200
201 return result;
202 }
203
204
205
206
207
208
209
210
211 public PupitriImportResult saveImportPupitri(File trunkFile,
212 File carrouselFile,
213 FishingOperation operation,
214 CatchBatch catchBatch,
215 boolean importMissingCategoryBatches,
216 PupitriImportResult result) {
217
218 if (result.isFishingOperationFound()) {
219
220
221 File reportFile = generatePupitriReport(operation, result);
222
223
224 Decorator<Species> decorator = getService(DecoratorService.class).getDecoratorByType(Species.class);
225 result.prepareMelag(decorator);
226
227 try {
228
229
230 savePupitriImportResult(result, operation, catchBatch, importMissingCategoryBatches);
231
232
233 addFileAsAttachment(trunkFile, catchBatch);
234 addFileAsAttachment(carrouselFile, catchBatch);
235
236 DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd__HH_mm");
237 String reportAttachmentFilename = "rapport-pupitri-" + simpleDateFormat.format(context.currentDate()) + ".pdf";
238 if (log.isInfoEnabled()) {
239 log.info("Save report with attachment filename: " + reportAttachmentFilename);
240 }
241 String reportId = addFileAsAttachment(reportFile, reportAttachmentFilename, catchBatch);
242 result.setReportAttachmentId(reportId);
243 result.setReportAttachmentFilename(reportAttachmentFilename);
244
245 } finally {
246 FileUtils.deleteQuietly(reportFile);
247 }
248
249 }
250
251 return result;
252 }
253
254
255 protected File generatePupitriReport(FishingOperation operation, PupitriImportResult result) {
256
257 TaxonCache taxonCache = TaxonCaches.createSpeciesCache(persistenceService, context.getDataContext().getProtocol());
258
259 PupitriImportReportModel reportModel = new PupitriImportReportModel(operation, result);
260
261 for (PupitriSpeciesContext aCatch : result.getCatches()) {
262
263 addCatchEntry(taxonCache, aCatch, reportModel);
264
265 }
266
267 reportModel.sortRows();
268
269 File reportFile = context.getConfig().newTempFile("puputri-report", ".pdf");
270 Locale locale = context.getConfig().getI18nLocale();
271
272 pdfGeneratorService.generatePdf(reportFile, locale, "pupitriReport.ftl", reportModel);
273
274 return reportFile;
275
276 }
277
278 protected void addCatchEntry(TaxonCache taxonCache, PupitriSpeciesContext aCatch, PupitriImportReportModel reportModel) {
279
280 Species species = aCatch.getSpecies();
281 taxonCache.load(species);
282
283 String code = Speciess.getSurveyCodeOrRefTaxCode(species);
284
285 String name = species.getName();
286
287 String vernacularCode = species.getVernacularCode();
288
289 for (Signs signs : aCatch.getSigns()) {
290
291 PupitriSignContext signContext = aCatch.getSignContext(signs);
292
293 if (!WeightUnit.KG.isNullOrZero(signContext.getWeight())) {
294
295 PupitriImportReportRow reportCatch = PupitriImportReportRow.newRow(code,
296 name,
297 vernacularCode,
298 aCatch.isSorted(),
299 signContext);
300 reportModel.addRow(reportCatch);
301 }
302 }
303 }
304
305 protected String addFileAsAttachment(File f, CatchBatch catchBatch) {
306 return addFileAsAttachment(f, f.getName(), catchBatch);
307 }
308
309 protected String addFileAsAttachment(File f, String attachmentName, CatchBatch catchBatch) {
310 Attachment attachment = Attachments.newAttachment();
311 attachment.setObjectType(ObjectTypeCode.CATCH_BATCH);
312 attachment.setObjectId(Integer.valueOf(catchBatch.getId()));
313 attachment.setName(attachmentName);
314 String date = DateFormat.getDateTimeInstance().format(context.currentDate());
315 String comment = t("tutti.service.pupitri.import.attachment.comment", date);
316 attachment.setComment(comment);
317 Attachment savedAttachment = persistenceService.createAttachment(attachment, f);
318 return savedAttachment.getId();
319 }
320
321 protected void importPupitriTrunk(PupitriImportResult result, File file, FishingOperation operation) {
322
323 if (log.isInfoEnabled()) {
324 log.info("Will import pupitri operation [" + operation.toString() + "] trunk from file: " + file);
325 }
326
327
328
329 float sortedWeight = 0f;
330 float rejectedWeight = 0f;
331
332 TrunkRowModel csvModel = new TrunkRowModel(',');
333 File fileWithHeaders = createFileWithHeaders(csvModel, file);
334
335 try (Reader reader = ApplicationIOUtil.newReader(
336 fileWithHeaders,
337 n("tutti.service.pupitri.import.trunk.error"))) {
338 try (Import<TrunkRow> importer = Import.newImport(csvModel, reader)) {
339
340 for (TrunkRow bean : importer) {
341 if (bean.acceptOperation(operation)) {
342 switch (bean.getDirection()) {
343 case VAT:
344 sortedWeight += bean.getWeight();
345 break;
346
347 case VNT:
348 rejectedWeight += bean.getWeight();
349 }
350 result.incrementNbTrunkImported();
351 } else {
352 result.incrementNbTrunkNotImported();
353 }
354 }
355 }
356
357 } catch (Exception e) {
358 throw new ApplicationTechnicalException(
359 t("tutti.service.pupitri.import.trunk.error", operation.toString(), file), e);
360
361 } finally {
362 FileUtils.deleteQuietly(fileWithHeaders);
363 }
364
365 result.setSortedWeight(WeightUnit.KG.round(sortedWeight));
366
367 if (rejectedWeight > 0f) {
368
369
370 result.setRejectedWeight(WeightUnit.KG.round(rejectedWeight));
371 }
372
373 }
374
375 protected void importPupitriCarrousel(PupitriImportResult result,
376 File carrouselFile,
377 FishingOperation operation,
378 boolean importMissingCategoryBatches) {
379
380 if (log.isInfoEnabled()) {
381 log.info("Will import pupitri operation [" + operation.toString() +
382 "] carrousel from file: " + carrouselFile);
383 }
384
385
386 CarrouselImportRequestResult carrouselImportRequestResult = processCarrouselImportFile(carrouselFile,
387 operation,
388 importMissingCategoryBatches);
389
390
391 result.flushCarrouselResult(carrouselImportRequestResult);
392
393 }
394
395 protected CarrouselImportRequestResult processCarrouselImportFile(File carrouselFile,
396 FishingOperation operation,
397 boolean importMissingCategoryBatches) {
398
399
400 ListMultimap<String, Species> speciesBySurveyCode = ArrayListMultimap.create();
401 speciesBySurveyCode.putAll(Speciess.splitBySurveyCode(dataContext.getReferentSpeciesWithSurveyCode(false)));
402 speciesBySurveyCode.putAll(Speciess.splitByRefTaxCode(dataContext.getReferentSpecies()));
403
404 CarrouselImportRequestResult result = new CarrouselImportRequestResult(speciesBySurveyCode);
405
406 CarrouselRowModel carrouselCsvModel = new CarrouselRowModel(',');
407 File fileWithHeaders = createFileWithHeaders(carrouselCsvModel, carrouselFile);
408
409 try (Reader reader = ApplicationIOUtil.newReader(
410 fileWithHeaders,
411 n("tutti.service.pupitri.import.carrousel.error"))) {
412
413 try (Import<CarrouselRow> importer = Import.newImport(carrouselCsvModel, reader)) {
414
415 for (CarrouselRow bean : importer) {
416
417 importCarrouselRow(result, operation, bean, importMissingCategoryBatches);
418
419 }
420 }
421
422 } catch (Exception e) {
423 DecoratorService service = getService(DecoratorService.class);
424 throw new ApplicationTechnicalException(
425 t("tutti.service.pupitri.import.carrousel.error", carrouselFile, service.getDecorator(operation).toString(operation)), e);
426
427 } finally {
428
429 FileUtils.deleteQuietly(fileWithHeaders);
430 }
431
432 return result;
433
434 }
435
436 protected void importCarrouselRow(CarrouselImportRequestResult result,
437 FishingOperation operation,
438 CarrouselRow bean,
439 boolean importMissingCategoryBatches) {
440
441 if (!bean.acceptOperation(operation)) {
442
443
444 return;
445
446 }
447
448
449 result.incrementNbCarrousselImported();
450
451
452 Float beanWeight = bean.getWeight();
453 if (beanWeight < 0f) {
454
455
456 beanWeight = 0f;
457
458 }
459
460 boolean sorted = bean.isSorted();
461 if (sorted) {
462
463
464
465 result.addCarrouselSortedWeight(beanWeight);
466
467 } else {
468
469
470
471 result.addCarrouselUnsortedWeight(beanWeight);
472
473 }
474
475 String speciesId = bean.getSpeciesId();
476 List<Species> speciesList = result.getSpecies(speciesId);
477 if (CollectionUtils.isEmpty(speciesList)) {
478
479
480
481 result.addNotImportedSpeciesId(speciesId);
482
483 } else {
484
485
486
487 PupitriSpeciesContext pupitriSpeciesContext = result.getOrCreateCatch(speciesList,
488 importMissingCategoryBatches,
489 sorted);
490
491
492 pupitriSpeciesContext.addToSignContext(bean.getSign(), bean.getBoxType(), beanWeight);
493
494 }
495
496 }
497
498 protected void savePupitriImportResult(PupitriImportResult result,
499 FishingOperation operation,
500 CatchBatch catchBatch,
501 boolean importMissingCategoryBatches) {
502
503 catchBatch.setCatchTotalSortedTremisWeight(result.getSortedWeight());
504 catchBatch.setCatchTotalRejectedWeight(result.getRejectedWeight());
505
506 catchBatch.setCatchTotalSortedCarousselWeight(result.getCarrouselSortedWeight());
507
508
509
510 BatchContainer<SpeciesBatch> rootSpeciesBatch = persistenceService.getRootSpeciesBatch(operation.getIdAsInt(), false);
511 for (SpeciesBatch batch : rootSpeciesBatch.getChildren()) {
512 persistenceService.deleteSpeciesBatch(batch.getIdAsInt());
513 }
514
515
516
517 TuttiProtocol protocol = dataContext.getProtocol();
518
519
520 Collection<String> surveyCodesToSex =
521 getSurveyCodeWhoseCategoryIsMandatory(protocol,
522 persistenceService.getSexCaracteristic());
523
524
525 Collection<String> surveyCodesToSize =
526 getSurveyCodeWhoseCategoryIsMandatory(protocol,
527 persistenceService.getSizeCategoryCaracteristic());
528
529
530 Collection<String> surveyCodesToSexAndSize = CollectionUtils.intersection(surveyCodesToSex, surveyCodesToSize);
531
532
533 surveyCodesToSex.removeAll(surveyCodesToSexAndSize);
534 surveyCodesToSize.removeAll(surveyCodesToSexAndSize);
535
536 String melagComment = result.getMelagComment();
537
538 Decorator<Species> speciesDecorator = decoratorService.getDecoratorByType(Species.class);
539 for (PupitriSpeciesContext pupitriSpeciesContext : result.getCatches()) {
540
541 Species species = pupitriSpeciesContext.getSpecies();
542 CaracteristicQualitativeValue cqv = pupitriSpeciesContext.isSorted() ?
543 sortedCaracteristic : unsortedCaracteristic;
544
545 boolean splitSpecies = pupitriSpeciesContext.isSplitSpecies();
546
547 String speciesStr = speciesDecorator.toString(species);
548
549 if (splitSpecies) {
550
551
552 if (log.isDebugEnabled()) {
553 log.debug("Create a categorized batches for species " + speciesStr);
554 }
555
556 SpeciesBatch batch = createSpeciesBatch(operation,
557 species,
558 null,
559 PmfmId.SORTED_UNSORTED.getValue(),
560 cqv);
561
562 batch = persistenceService.createSpeciesBatch(batch, null, true);
563
564 Integer parentBatchId = batch.getIdAsInt();
565
566 for (Signs signs : pupitriSpeciesContext.getSigns()) {
567
568 boolean addMelagComment = pupitriSpeciesContext.isAddMelagComment(signs);
569 float catchWeight = pupitriSpeciesContext.getWeight(signs);
570
571 Integer categoryId = signs.getCategory();
572 CaracteristicQualitativeValue splitCqv = signsToCaracteristicValue.get(signs);
573 SpeciesBatch childBatch = createSpeciesBatch(
574 operation,
575 species,
576 WeightUnit.KG.isNullOrZero(catchWeight) ? null : catchWeight,
577 categoryId,
578 splitCqv);
579
580 if (addMelagComment) {
581 childBatch.setComment(melagComment);
582 if (log.isInfoEnabled()) {
583 log.info("Add melag comment for sign " + signs + " on " + speciesStr);
584 }
585 }
586
587 persistenceService.createSpeciesBatch(childBatch, parentBatchId, true);
588 }
589
590 } else {
591
592
593 float totalWeight = pupitriSpeciesContext.getTotalWeight();
594 if (log.isInfoEnabled()) {
595 log.info("Create a unique batch for species " + speciesStr + " with total weight: " + totalWeight);
596 }
597
598 SpeciesBatch batch = createSpeciesBatch(operation,
599 species,
600 totalWeight,
601 PmfmId.SORTED_UNSORTED.getValue(),
602 cqv);
603
604 boolean addMelagComment = pupitriSpeciesContext.isAddMelagComment(Signs.DEFAULT);
605 if (addMelagComment) {
606 if (log.isInfoEnabled()) {
607 log.info("Add melag comment for simple batch " + speciesStr);
608 }
609 batch.setComment(melagComment);
610 }
611
612 batch = persistenceService.createSpeciesBatch(batch, null, true);
613
614 if (importMissingCategoryBatches) {
615
616 Integer parentBatchId = batch.getIdAsInt();
617 String speciesId = species.getSurveyCode();
618
619 List<Signs> signs2add = null;
620
621 if (surveyCodesToSex.contains(speciesId)) {
622
623 signs2add = Lists.newArrayList(Signs.MALE, Signs.FEMALE, Signs.DEFAULT);
624
625 } else if (surveyCodesToSize.contains(speciesId)) {
626
627 signs2add = Lists.newArrayList(Signs.SMALL, Signs.BIG);
628 }
629
630 if (signs2add != null) {
631 for (Signs signs : signs2add) {
632
633 Integer categoryId = signs.getCategory();
634 CaracteristicQualitativeValue splitCqv = signsToCaracteristicValue.get(signs);
635 SpeciesBatch childBatch = createSpeciesBatch(
636 operation,
637 species,
638 null,
639 categoryId,
640 splitCqv);
641
642 persistenceService.createSpeciesBatch(childBatch, parentBatchId, true);
643 }
644 }
645 }
646 }
647 }
648
649 persistenceService.saveCatchBatch(catchBatch);
650 }
651
652 protected SpeciesBatch createSpeciesBatch(FishingOperation operation,
653 Species species,
654 Float catchWeight,
655 Integer categoryId,
656 Serializable cqv) {
657 SpeciesBatch batch = SpeciesBatchs.newSpeciesBatch();
658 batch.setFishingOperation(operation);
659 batch.setSampleCategoryId(categoryId);
660 batch.setSampleCategoryValue(cqv);
661 batch.setSpecies(species);
662 batch.setSampleCategoryWeight(
663 catchWeight == null ? null : WeightUnit.KG.round(catchWeight));
664 return batch;
665 }
666
667 protected File createFileWithHeaders(ImportModelWithHeader<?> model,
668 File file) {
669 File fileWithHeaders = new File(FileUtils.getTempDirectory(), file.getName());
670 String headers = StringUtils.join(model.getHeader(), model.getSeparator());
671
672 try {
673 FileUtils.writeLines(fileWithHeaders, Collections.singletonList(headers));
674 FileUtils.writeLines(fileWithHeaders, FileUtils.readLines(file), true);
675 } catch (IOException e) {
676 throw new ApplicationTechnicalException("Could not create file with header " + file, e);
677 }
678
679 return fileWithHeaders;
680 }
681
682 private Collection<String> getSurveyCodeWhoseCategoryIsMandatory(TuttiProtocol protocol, Caracteristic caracteristic) {
683
684 Objects.requireNonNull(caracteristic);
685
686 Collection<String> surveyCodes;
687
688 if (protocol == null) {
689
690 surveyCodes = new HashSet<>();
691
692 } else {
693
694 Predicate<SpeciesProtocol> predicate = SpeciesProtocols.speciesProtocolWhoseCategoryIsMandatoryPredicate(caracteristic);
695 Collection<SpeciesProtocol> speciesProtocols = protocol.getSpecies().stream().filter(predicate).collect(Collectors.toSet());
696
697 surveyCodes = speciesProtocols.stream().map(SpeciesProtocol::getSpeciesSurveyCode).collect(Collectors.toSet());
698
699 }
700
701 return surveyCodes;
702 }
703 }