View Javadoc
1   package fr.ifremer.tutti.service.pupitri;
2   
3   /*
4    * #%L
5    * Tutti :: Service
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.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   * @author Kevin Morin - kmorin@codelutin.com
94   * @since 1.2
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         { // sorted/unsorted caracteristic
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         { // sex category
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         { // size category
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      * Used merely in tests.
151      * @param trunkFile     incoming trunk file
152      * @param carrouselFile incoming carroussle file
153      * @param operation     target operation
154      * @param catchBatch    target catch batch
155      * @return the number of rows of the .car file which have not been imported
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      * Used merely in tests.
167      * @param trunkFile     incoming trunk file
168      * @param carrouselFile incoming carroussle file
169      * @param operation     target operation
170      * @param catchBatch    target catch batch
171      * @param importMissingCategoryBatches import or not empty batches for missing sex or size batches
172      * @return the number of rows of the .car file which have not been imported
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      * @param trunkFile     incoming trunk file
186      * @param carrouselFile incoming carroussle file
187      * @param operation     target operation
188      * @param importMissingCategoryBatches import or not empty batches for missing sex or size batches
189      * @return the number of rows of the .car file which have not been imported
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      * @param trunkFile     incoming trunk file
206      * @param carrouselFile incoming carroussle file
207      * @param operation     target operation
208      * @param importMissingCategoryBatches import or not empty batches for missing sex or size batches
209      * @return the number of rows of the .car file which have not been imported
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             // generation du rapport
221             File reportFile = generatePupitriReport(operation, result);
222 
223             // gestion du melange
224             Decorator<Species> decorator = getService(DecoratorService.class).getDecoratorByType(Species.class);
225             result.prepareMelag(decorator);
226 
227             try {
228 
229                 // persistence des lots
230                 savePupitriImportResult(result, operation, catchBatch, importMissingCategoryBatches);
231 
232                 // ajout des pièces-jointes
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     //FIXME Check how to deal with Melag meta species and species involed in melag
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         // prepare import
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             // On set rejectweight when not zero, See http://forge.codelutin.com/issues/5676
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         // process import file
386         CarrouselImportRequestResult carrouselImportRequestResult = processCarrouselImportFile(carrouselFile,
387                                                                                                operation,
388                                                                                                importMissingCategoryBatches);
389 
390         // save it to global result
391         result.flushCarrouselResult(carrouselImportRequestResult);
392 
393     }
394 
395     protected CarrouselImportRequestResult processCarrouselImportFile(File carrouselFile,
396                                                                       FishingOperation operation,
397                                                                       boolean importMissingCategoryBatches) {
398 
399         // get the map of species by survey code or reftax code
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             // ce lot n'est pas sur la bonne operation
444             return;
445 
446         }
447 
448         // le lot est sur la bonne operation
449         result.incrementNbCarrousselImported();
450 
451         // poids du lot
452         Float beanWeight = bean.getWeight();
453         if (beanWeight < 0f) {
454 
455             //FIXME Savoir pourquoi cela peut arriver ?
456             beanWeight = 0f;
457 
458         }
459 
460         boolean sorted = bean.isSorted();
461         if (sorted) {
462 
463             // ajout au total des poids trie du carrousel
464             // meme si ensuite l'espèce peut-être rejetée
465             result.addCarrouselSortedWeight(beanWeight);
466 
467         } else {
468 
469             // ajout au total des poids non trié du carrousel
470             // meme si ensuite l'espèce peut-être rejetée
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             // l'espece n'est pas reconnu
480             // pas de traitement sur ce lot
481             result.addNotImportedSpeciesId(speciesId);
482 
483         } else {
484 
485             // creation d'un nouveau lot (par rapport au tuple (espece, trie))
486             // ou bien recuperation d'un lot deja existant
487             PupitriSpeciesContext pupitriSpeciesContext = result.getOrCreateCatch(speciesList,
488                                                                                   importMissingCategoryBatches,
489                                                                                   sorted);
490 
491             // ajout des données (type de box, poids) au signe donne dans le lot
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         // delete all species batches
509 
510         BatchContainer<SpeciesBatch> rootSpeciesBatch = persistenceService.getRootSpeciesBatch(operation.getIdAsInt(), false);
511         for (SpeciesBatch batch : rootSpeciesBatch.getChildren()) {
512             persistenceService.deleteSpeciesBatch(batch.getIdAsInt());
513         }
514 
515         // insert all imported species batches
516 
517         TuttiProtocol protocol = dataContext.getProtocol();
518 
519         // especes à sexer
520         Collection<String> surveyCodesToSex =
521                 getSurveyCodeWhoseCategoryIsMandatory(protocol,
522                                                       persistenceService.getSexCaracteristic());
523 
524         // especes à trier par taille
525         Collection<String> surveyCodesToSize =
526                 getSurveyCodeWhoseCategoryIsMandatory(protocol,
527                                                       persistenceService.getSizeCategoryCaracteristic());
528 
529         // especes à sexer et trier par taille
530         Collection<String> surveyCodesToSexAndSize = CollectionUtils.intersection(surveyCodesToSex, surveyCodesToSize);
531 
532         // on ne veut que les espèces qu'il faut soit sexer soit trier par taille
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                 // create a top batch + a son for each sign
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                 // create a unique batch with sum all weights as simple weight
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 }