View Javadoc
1   package fr.ifremer.dali.service.observation;
2   
3           /*
4            * #%L
5    * Dali :: Core
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24           */
25  
26  import com.google.common.collect.Lists;
27  import fr.ifremer.dali.config.DaliConfiguration;
28  import fr.ifremer.dali.dao.administration.user.DaliDepartmentDao;
29  import fr.ifremer.dali.dao.data.measurement.DaliMeasurementDao;
30  import fr.ifremer.dali.dao.data.photo.DaliPhotoDao;
31  import fr.ifremer.dali.dao.data.samplingoperation.DaliSamplingOperationDao;
32  import fr.ifremer.dali.dao.data.survey.DaliSurveyDao;
33  import fr.ifremer.dali.dto.DaliBeanFactory;
34  import fr.ifremer.dali.dto.DaliBeans;
35  import fr.ifremer.dali.dto.configuration.programStrategy.PmfmStrategyDTO;
36  import fr.ifremer.dali.dto.configuration.programStrategy.ProgStratDTO;
37  import fr.ifremer.dali.dto.configuration.programStrategy.ProgramDTO;
38  import fr.ifremer.dali.dto.data.measurement.MeasurementDTO;
39  import fr.ifremer.dali.dto.data.sampling.SamplingOperationDTO;
40  import fr.ifremer.dali.dto.data.survey.CampaignDTO;
41  import fr.ifremer.dali.dto.data.survey.SurveyDTO;
42  import fr.ifremer.dali.dto.data.survey.SurveyFilterDTO;
43  import fr.ifremer.dali.dto.enums.FilterTypeValues;
44  import fr.ifremer.dali.dto.enums.SearchDateValues;
45  import fr.ifremer.dali.dto.enums.SynchronizationStatusValues;
46  import fr.ifremer.dali.dto.referential.*;
47  import fr.ifremer.dali.dto.referential.pmfm.PmfmDTO;
48  import fr.ifremer.dali.dto.system.QualificationHistoryDTO;
49  import fr.ifremer.dali.dto.system.ValidationHistoryDTO;
50  import fr.ifremer.dali.service.DaliDataContext;
51  import fr.ifremer.dali.service.DaliTechnicalException;
52  import fr.ifremer.dali.service.StatusFilter;
53  import fr.ifremer.dali.service.administration.campaign.CampaignService;
54  import fr.ifremer.dali.service.administration.context.ContextService;
55  import fr.ifremer.dali.service.administration.program.ProgramStrategyService;
56  import fr.ifremer.dali.service.administration.user.UserService;
57  import fr.ifremer.dali.service.persistence.PersistenceService;
58  import fr.ifremer.dali.service.referential.ReferentialService;
59  import fr.ifremer.quadrige3.core.ProgressionCoreModel;
60  import fr.ifremer.quadrige3.core.dao.referential.QualityFlagCode;
61  import fr.ifremer.quadrige3.core.dao.technical.Assert;
62  import fr.ifremer.quadrige3.core.dao.technical.Dates;
63  import fr.ifremer.quadrige3.core.dao.technical.Times;
64  import fr.ifremer.quadrige3.core.security.SecurityContextHelper;
65  import org.apache.commons.collections4.CollectionUtils;
66  import org.apache.commons.lang3.StringUtils;
67  import org.apache.commons.logging.Log;
68  import org.apache.commons.logging.LogFactory;
69  import org.springframework.beans.factory.annotation.Autowired;
70  import org.springframework.stereotype.Service;
71  
72  import javax.annotation.Resource;
73  import java.time.LocalDate;
74  import java.util.*;
75  import java.util.stream.Collectors;
76  
77  import static org.nuiton.i18n.I18n.t;
78  
79  /**
80   * <p>ObservationServiceImpl class.</p>
81   *
82   * @author Ludovic
83   */
84  @Service("daliSurveyService")
85  public class ObservationServiceImpl implements ObservationInternalService {
86  
87      public static final Log LOG = LogFactory.getLog(ObservationServiceImpl.class);
88  
89      @Resource(name = "daliSurveyService")
90      private ObservationInternalService loopBackService;
91  
92      @Resource(name = "daliPersistenceService")
93      private PersistenceService persistenceService;
94  
95      @Resource(name = "daliDataContext")
96      private DaliDataContext dataContext;
97  
98      @Resource(name = "daliContextService")
99      private ContextService contextService;
100 
101     @Resource(name = "daliReferentialService")
102     private ReferentialService referentialService;
103 
104     @Resource(name = "daliUserService")
105     private UserService userService;
106 
107     @Resource(name = "daliProgramStrategyService")
108     private ProgramStrategyService programStrategyService;
109 
110     @Resource(name = "daliCampaignService")
111     private CampaignService campaignService;
112 
113     @Resource(name = "daliSurveyDao")
114     private DaliSurveyDao surveyDao;
115 
116     @Resource(name = "daliSamplingOperationDao")
117     private DaliSamplingOperationDao samplingOperationDao;
118 
119     @Resource(name = "daliMeasurementDao")
120     private DaliMeasurementDao measurementDao;
121 
122     @Resource(name = "daliPhotoDao")
123     private DaliPhotoDao photoDao;
124 
125     @Resource(name = "daliDepartmentDao")
126     protected DaliDepartmentDao departmentDao;
127 
128     @Autowired
129     protected DaliConfiguration config;
130 
131     /**
132      * {@inheritDoc}
133      */
134     @Override
135     public List<SurveyDTO> getSurveys(SurveyFilterDTO surveyFilter) {
136         Integer campaignId = surveyFilter.getCampaignId();
137         String programCode = surveyFilter.getProgramCode();
138         Integer locationId = surveyFilter.getLocationId();
139         Integer stateId = surveyFilter.getStateId();
140         Integer synchronizationStatusId = surveyFilter.getShareId();
141         String name = surveyFilter.getName();
142         String comment = surveyFilter.getComment();
143         LocalDate startDate = null;
144         LocalDate endDate = null;
145         boolean strictDate = false;
146         if (surveyFilter.getSearchDateId() != null) {
147             SearchDateValues searchDateValue = SearchDateValues.values()[surveyFilter.getSearchDateId()];
148             switch (searchDateValue) {
149                 case EQUALS:
150                     startDate = surveyFilter.getDate1();
151                     endDate = startDate;
152                     strictDate = true;
153                     break;
154                 case BETWEEN:
155                     startDate = surveyFilter.getDate1();
156                     endDate = surveyFilter.getDate2();
157                     break;
158                 case BEFORE:
159                     endDate = surveyFilter.getDate1();
160                     strictDate = true;
161                     break;
162                 case BEFORE_OR_EQUALS:
163                     endDate = surveyFilter.getDate1();
164                     break;
165                 case AFTER:
166                     startDate = surveyFilter.getDate1();
167                     strictDate = true;
168                     break;
169                 case AFTER_OR_EQUALS:
170                     startDate = surveyFilter.getDate1();
171                     break;
172             }
173         }
174         Collection<String> programCodes;
175         // if no filter on single program, force filter on writable programs
176         if (StringUtils.isBlank(programCode)) {
177             programCodes = programStrategyService.getWritableProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId());
178         } else {
179             programCodes = Collections.singleton(programCode);
180         }
181 
182         List<SurveyDTO> surveys = surveyDao.getSurveysByCriteria(
183                 campaignId,
184                 programCodes,
185                 locationId,
186                 name,
187                 comment,
188                 stateId,
189                 synchronizationStatusId,
190                 Dates.convertToDate(startDate, config.getDbTimezone()),
191                 Dates.convertToDate(endDate, config.getDbTimezone()),
192                 strictDate);
193 
194         // Filter surveys by hermetic programs and service (Mantis #42617)
195         return filterSurveysByDepartmentHermetic(surveys);
196     }
197 
198     private List<SurveyDTO> filterSurveysByDepartmentHermetic(List<SurveyDTO> surveys) {
199 
200         Integer userId= dataContext.getRecorderPersonId();
201         Assert.notNull(userId);
202         Integer recDepId = dataContext.getRecorderDepartmentId();
203         Assert.notNull(recDepId);
204 
205         List<ProgramDTO> managedPrograms = programStrategyService.getManagedProgramsByUser(userId);
206 
207         return surveys.parallelStream().filter(survey -> {
208             ProgramDTO program = survey.getProgram();
209             // Don't filter malformed survey (Mantis #43941)
210             if (program == null) return true;
211             // Filter survey if program is hermetic by department
212             if (program.isDepartmentHermetic() && !managedPrograms.contains(program)) {
213                 return survey.getDepartment().getId().equals(recDepId);
214             }
215             return true;
216         }).collect(Collectors.toList());
217     }
218 
219     /**
220      * {@inheritDoc}
221      */
222     @Override
223     public void loadSamplingOperationsFromSurvey(SurveyDTO survey, boolean withIndividualMeasurements) {
224 
225         if (survey == null || survey.getId() == null) {
226             return;
227         }
228 
229         if (!survey.isSamplingOperationsLoaded()) {
230 
231             // load and affect sampling operations
232             survey.setSamplingOperations(samplingOperationDao.getSamplingOperationsBySurveyId(survey.getId(), withIndividualMeasurements));
233             survey.setSamplingOperationsLoaded(true);
234 
235             if (!survey.isSamplingOperationsEmpty()) {
236 
237                 // collect measurements (ungrouped only)
238                 for (SamplingOperationDTO samplingOperation : survey.getSamplingOperations()) {
239 
240                     // affect all pmfms to each sampling operation, even if it don't have measurement with all pmfms
241                     if (samplingOperation.getPmfms() == null) {
242                         samplingOperation.setPmfms(new ArrayList<>());
243                     }
244 
245                     if (withIndividualMeasurements) {
246                         if (samplingOperation.getIndividualPmfms() == null) {
247                             samplingOperation.setIndividualPmfms(new ArrayList<>());
248                         }
249                     } else {
250                         // clear individual pmfms and measurements
251                         samplingOperation.setIndividualPmfms(null);
252                         samplingOperation.setIndividualMeasurements(null);
253                         samplingOperation.setIndividualMeasurementsLoaded(false); // should already be set to false
254                     }
255 
256                     // add PMFMs from existing ungrouped measurements
257                     DaliBeans.populatePmfmsFromMeasurements(samplingOperation);
258 
259                 }
260             }
261         }
262     }
263 
264     /**
265      * {@inheritDoc}
266      */
267     @Override
268     public SamplingOperationDTO newSamplingOperation(SurveyDTO survey, List<PmfmDTO> pmfms) {
269         Assert.notNull(survey);
270         Assert.notNull(pmfms);
271 
272         SamplingOperationDTO result = DaliBeanFactory.newSamplingOperationDTO();
273 
274         // add empty measurements
275         for (final PmfmDTO pmfm : pmfms) {
276             final MeasurementDTO measurement = DaliBeanFactory.newMeasurementDTO();
277             measurement.setPmfm(pmfm);
278             result.addMeasurements(measurement);
279         }
280         result.addAllPmfms(pmfms);
281 
282         // add department from strategy
283         result.setSamplingDepartment(programStrategyService.getSamplingDepartmentOfAppliedStrategyBySurvey(survey));
284 
285         return result;
286     }
287 
288     /**
289      * {@inheritDoc}
290      */
291     @Override
292     public SurveyDTO getSurvey(Integer surveyId) {
293         return getSurvey(surveyId, false);
294     }
295 
296     /**
297      * {@inheritDoc}
298      */
299     @Override
300     public SurveyDTO getSurveyWithoutPmfmFiltering(Integer surveyId) {
301         return getSurvey(surveyId, true);
302     }
303 
304     /**
305      * {@inheritDoc}
306      */
307     @Override
308     public void saveSurveys(Collection<? extends SurveyDTO> surveys, ProgressionCoreModel progressionModel) {
309 
310         List<SurveyDTO> surveysToSave = surveys.stream().filter(SurveyDTO::isDirty).collect(Collectors.toList());
311         progressionModel.setTotal(surveysToSave.size());
312 
313         for (SurveyDTO survey : surveysToSave) {
314             progressionModel.increments(t("dali.service.common.progression",
315                     t("dali.service.observation.save"), progressionModel.getCurrent() + 1, progressionModel.getTotal()));
316 
317             if (survey.getId() == null) {
318 
319                 // add recorderDepartment if any
320                 if (survey.getDepartment() == null) {
321                     // get department from authenticated user
322                     Integer depId = dataContext.getRecorderDepartmentId();
323                     if (depId == null) {
324                         throw new DaliTechnicalException("no RecorderDepartmentId found in data context");
325                     }
326                     survey.setDepartment(departmentDao.getDepartmentById(depId));
327                 }
328             }
329 
330             saveSurvey(survey);
331             survey.setDirty(false);
332         }
333     }
334 
335     /**
336      * {@inheritDoc}
337      */
338     @Override
339     public void saveSurvey(SurveyDTO survey) {
340         // when saving, reset control date - see mantis #26451
341         if (survey.isDirty() || survey.getControlDate() != null) {
342             survey.setControlDate(null);
343         }
344 
345         // Apply default analysis department, from strategy (mantis #28257)
346         applyDefaultAnalysisDepartment(survey);
347 
348         // Persist the update survey
349         surveyDao.save(survey);
350     }
351 
352     /**
353      * {@inheritDoc}
354      */
355     @Override
356     public void deleteSurveys(List<Integer> surveyIds) {
357         if (CollectionUtils.isEmpty(surveyIds)) return;
358         Integer recorderUserId = dataContext.getRecorderPersonId();
359 
360         // Do not delete, but insert into DeletedItemHistory
361         surveyIds.stream().filter(Objects::nonNull).distinct().forEach(surveyId -> surveyDao.removeUsingDeletedItemHistory(surveyId, recorderUserId));
362     }
363 
364     /**
365      * {@inheritDoc}
366      */
367     @Override
368     public SurveyDTO duplicateSurvey(SurveyDTO survey, boolean fullDuplication, boolean copyCoordinates) {
369 
370         // Duplicate survey
371         SurveyDTO duplicateSurvey = DaliBeans.clone(survey);
372         // Remove ID
373         duplicateSurvey.setId(null);
374         duplicateSurvey.setDirty(true);
375 
376         // duplicated data to keep:
377         if (survey.isObserversEmpty() && survey.getId() != null) {
378             duplicateSurvey.setObservers(surveyDao.getObservers(survey.getId()));
379         } else {
380             duplicateSurvey.setObservers(DaliBeans.clone(survey.getObservers()));
381         }
382 
383         // reset other survey data
384         duplicateSurvey.setPhotos(null);
385         duplicateSurvey.setPhotosLoaded(false);
386         duplicateSurvey.setMeasurements(null);
387         duplicateSurvey.setIndividualMeasurements(null);
388         duplicateSurvey.setMeasurementsLoaded(false);
389         duplicateSurvey.setPmfms(null);
390         duplicateSurvey.setIndividualPmfms(null);
391         duplicateSurvey.setComment(null);
392         duplicateSurvey.setQualificationComment(null);
393         duplicateSurvey.setValidationDate(null);
394         duplicateSurvey.setUpdateDate(null);
395         duplicateSurvey.setControlDate(null);
396         duplicateSurvey.setSynchronizationStatus(null);
397         duplicateSurvey.setErrors(null);
398 
399         if (copyCoordinates) {
400             duplicateSurvey.setCoordinate(DaliBeans.clone(survey.getCoordinate()));
401             // and positioning system
402             duplicateSurvey.setPositioning(survey.getPositioning());
403             // keep positioning comment if we keep same positioning ?
404             duplicateSurvey.setPositioningComment(survey.getPositioningComment());
405         } else {
406             duplicateSurvey.setCoordinate(null);
407             duplicateSurvey.setPositioning(null);
408             duplicateSurvey.setPositioningComment(null);
409         }
410 
411         // same occasion ? or just when full duplication ?
412         if (survey.getOccasion() != null) {
413             duplicateSurvey.setOccasion(DaliBeans.clone(survey.getOccasion()));
414             duplicateSurvey.getOccasion().setId(null);
415         }
416 
417         if (fullDuplication) {
418 
419             // ensure sampling operations are loaded
420             loadSamplingOperationsFromSurvey(survey, true);
421 
422             // sampling operations
423             List<SamplingOperationDTO> duplicateSamplingOperations = Lists.newArrayList();
424             if (!survey.isSamplingOperationsEmpty()) {
425                 for (SamplingOperationDTO samplingOperation : survey.getSamplingOperations()) {
426                     SamplingOperationDTO duplicateSamplingOperation = DaliBeans.clone(samplingOperation);
427                     duplicateSamplingOperation.setId(null);
428                     duplicateSamplingOperation.setMeasurements(null);
429                     duplicateSamplingOperation.setIndividualMeasurements(null);
430                     duplicateSamplingOperation.setMeasurementsLoaded(false);
431                     duplicateSamplingOperation.setIndividualMeasurementsLoaded(false);
432                     duplicateSamplingOperation.setComment(null);
433                     duplicateSamplingOperation.setErrors(null);
434 
435                     if (copyCoordinates) {
436                         duplicateSamplingOperation.setCoordinate(samplingOperation.getCoordinate());
437                         duplicateSamplingOperation.setPositioning(samplingOperation.getPositioning());
438                     } else {
439                         duplicateSamplingOperation.setCoordinate(null);
440                         duplicateSamplingOperation.setPositioning(null);
441                     }
442 
443                     duplicateSamplingOperations.add(duplicateSamplingOperation);
444                 }
445             }
446 
447             duplicateSurvey.setSamplingOperations(duplicateSamplingOperations);
448             duplicateSurvey.setSamplingOperationsLoaded(true);
449 
450         } else {
451             // Remove sampling operations
452             duplicateSurvey.setSamplingOperations(null);
453             duplicateSurvey.setSamplingOperationsLoaded(false);
454         }
455 
456         return duplicateSurvey;
457 
458     }
459 
460     /**
461      * {@inheritDoc}
462      */
463     @Override
464     public List<ProgramDTO> getAvailablePrograms(Integer locationId, LocalDate date, boolean forceNoContext) {
465         List<ProgramDTO> result;
466         if (dataContext.isContextFiltered(FilterTypeValues.PROGRAM) && !forceNoContext) {
467             result = contextService.getFilteredPrograms(dataContext.getContextId());
468             // context filtered programs can eventually out of user rights, filter them
469             Set<String> writableProgramCodes = programStrategyService.getWritableProgramCodesByQuserId(dataContext.getPrincipalUserId());
470             result = result.stream().filter(programDTO -> writableProgramCodes.contains(programDTO.getCode())).collect(Collectors.toList());
471         } else {
472             result = programStrategyService.getWritablePrograms();
473         }
474 
475         // Filter result with provided location and date
476         if (CollectionUtils.isNotEmpty(result) && locationId != null && date != null) {
477             List<ProgramDTO> filteredPrograms = programStrategyService.getWritableProgramsByLocationAndDate(
478                     locationId,
479                     Dates.convertToDate(date, config.getDbTimezone()));
480             return (List<ProgramDTO>) CollectionUtils.intersection(result, filteredPrograms);
481         }
482 
483         return result;
484     }
485 
486     @Override
487     public List<CampaignDTO> getAvailableCampaigns(LocalDate date, boolean forceNoContext) {
488         List<CampaignDTO> result;
489         if (dataContext.isContextFiltered(FilterTypeValues.CAMPAIGN) && !forceNoContext) {
490             result = contextService.getFilteredCampaigns(dataContext.getContextId());
491         } else {
492             result = new ArrayList<>(campaignService.getAllCampaigns()); // put in a new List because the DAO returns an immutable list
493         }
494 
495         if (CollectionUtils.isNotEmpty(result) && date != null) {
496             List<CampaignDTO> filteredCampaigns = campaignService.findCampaignsIncludingDate(Dates.convertToDate(date, config.getDbTimezone()));
497             return (List<CampaignDTO>) CollectionUtils.intersection(result, filteredCampaigns);
498         }
499 
500         return result;
501     }
502 
503     /**
504      * {@inheritDoc}
505      */
506     @Override
507     public List<AnalysisInstrumentDTO> getAvailableAnalysisInstruments(boolean forceNoContext) {
508         if (dataContext.isContextFiltered(FilterTypeValues.ANALYSIS_INSTRUMENT) && !forceNoContext) {
509             return contextService.getFilteredAnalysisInstruments(dataContext.getContextId());
510         } else {
511             return referentialService.getAnalysisInstruments(StatusFilter.ACTIVE);
512         }
513     }
514 
515     /**
516      * {@inheritDoc}
517      */
518     @Override
519     public List<SamplingEquipmentDTO> getAvailableSamplingEquipments(boolean forceNoContext) {
520         if (dataContext.isContextFiltered(FilterTypeValues.SAMPLING_EQUIPMENT) && !forceNoContext) {
521             return contextService.getFilteredSamplingEquipments(dataContext.getContextId());
522         } else {
523             return referentialService.getSamplingEquipments(StatusFilter.ACTIVE);
524         }
525     }
526 
527     /**
528      * {@inheritDoc}
529      */
530     @Override
531     public List<LocationDTO> getAvailableLocations(boolean forceNoContext) {
532         if (dataContext.isContextFiltered(FilterTypeValues.LOCATION) && !forceNoContext) {
533             return contextService.getFilteredLocations(dataContext.getContextId());
534         } else {
535             return referentialService.getLocations(StatusFilter.ACTIVE);
536         }
537     }
538 
539     /**
540      * {@inheritDoc}
541      */
542     @Override
543     public List<LocationDTO> getAvailableLocations(Integer campaignId, String programCode, boolean forceNoContext) {
544         List<LocationDTO> locations = referentialService.getLocations(campaignId, programCode);
545         if (CollectionUtils.isNotEmpty(locations)) {
546             if (dataContext.isContextFiltered(FilterTypeValues.LOCATION) && !forceNoContext) {
547                 List<LocationDTO> filteredLocations = contextService.getFilteredLocations(dataContext.getContextId());
548                 if (CollectionUtils.isNotEmpty(filteredLocations)) {
549                     return (List<LocationDTO>) CollectionUtils.intersection(locations, filteredLocations);
550                 }
551             }
552         }
553         return locations;
554     }
555 
556     /**
557      * {@inheritDoc}
558      */
559     @Override
560     public List<TaxonGroupDTO> getAvailableTaxonGroups(TaxonDTO taxon, boolean forceNoContext) {
561         List<TaxonGroupDTO> result;
562         if (taxon == null) {
563             return getAvailableTaxonGroups(forceNoContext);
564         } else {
565             result = taxon.getTaxonGroups();
566             if (CollectionUtils.isNotEmpty(result)) {
567                 if (dataContext.isContextFiltered(FilterTypeValues.TAXON_GROUP) && !forceNoContext) {
568                     List<TaxonGroupDTO> filteredTaxonGroups = contextService.getFilteredTaxonGroups(dataContext.getContextId());
569                     if (CollectionUtils.isNotEmpty(filteredTaxonGroups)) {
570                         result = (List<TaxonGroupDTO>) CollectionUtils.intersection(result, filteredTaxonGroups);
571                     }
572                 }
573             }
574         }
575         return referentialService.getFullTaxonGroups(result);
576     }
577 
578     /**
579      * {@inheritDoc}
580      */
581     @Override
582     public List<TaxonDTO> getAvailableTaxons(TaxonGroupDTO taxonGroup, boolean forceNoContext) {
583         if (taxonGroup == null) {
584             return getAvailableTaxons(forceNoContext);
585         } else {
586             // Mantis #0029620 don't use taxonGroup.getTaxons() because ehcache don't persist correctly the taxons and taxonGroups
587             List<TaxonDTO> availableTaxons = referentialService.getTaxons(taxonGroup.getId());
588 
589             // Remove obsolete taxons
590             availableTaxons = availableTaxons.stream().filter(taxon->!taxon.isObsolete()).collect(Collectors.toList());
591 
592             if (CollectionUtils.isNotEmpty(availableTaxons)) {
593                 if (dataContext.isContextFiltered(FilterTypeValues.TAXON) && !forceNoContext) {
594                     List<TaxonDTO> filteredTaxons = contextService.getFilteredTaxons(dataContext.getContextId());
595                     if (CollectionUtils.isNotEmpty(filteredTaxons)) {
596                         availableTaxons = (List<TaxonDTO>) CollectionUtils.intersection(availableTaxons, filteredTaxons);
597                     }
598                 }
599             }
600             referentialService.fillReferentTaxons(availableTaxons);
601             return availableTaxons;
602         }
603     }
604 
605     /**
606      * {@inheritDoc}
607      */
608     @Override
609     public List<PmfmDTO> getAvailablePmfms(boolean forceNoContext) {
610         if (dataContext.isContextFiltered(FilterTypeValues.PMFM) && !forceNoContext) {
611             return contextService.getFilteredPmfms(dataContext.getContextId());
612         } else {
613             return referentialService.getPmfms(StatusFilter.ACTIVE);
614         }
615     }
616 
617     /**
618      * {@inheritDoc}
619      */
620     @Override
621     public List<DepartmentDTO> getAvailableDepartments(boolean forceNoContext) {
622         if (dataContext.isContextFiltered(FilterTypeValues.DEPARTMENT) && !forceNoContext) {
623             return contextService.getFilteredDepartments(dataContext.getContextId());
624         } else {
625             return referentialService.getDepartments(StatusFilter.ACTIVE);
626         }
627     }
628 
629     /**
630      * {@inheritDoc}
631      */
632     @Override
633     public List<PersonDTO> getAvailableUsers(boolean forceNoFilter) {
634         if (dataContext.isContextFiltered(FilterTypeValues.USER) && !forceNoFilter) {
635             return contextService.getFilteredUsers(dataContext.getContextId());
636         } else {
637             return userService.getActiveUsers();
638         }
639     }
640 
641     /**
642      * {@inheritDoc}
643      */
644     @Override
645     public void validateSurveys(Collection<? extends SurveyDTO> surveys, String validationComment, ProgressionCoreModel progressionModel) {
646 
647         if (CollectionUtils.isEmpty(surveys)) return;
648 
649         // compute validation date
650         Date validationDate = new Date(System.currentTimeMillis());
651 
652         // collect surveys to validate
653         List<SurveyDTO> surveysToValidate = surveys.stream()
654                 .filter(survey -> !survey.isDirty() && survey.getControlDate() != null && survey.getValidationDate() == null)
655                 .collect(Collectors.toList());
656 
657         // collect survey ids
658         List<Integer> allSurveyIds = surveysToValidate.stream().map(SurveyDTO::getId).distinct().collect(Collectors.toList());
659         int chunkSize = config.getMassiveProcessChunkSize();
660         List<List<Integer>> chunkSurveyIds = Lists.partition(allSurveyIds, chunkSize);
661         boolean enableMassiveUpdate = chunkSurveyIds.size() > 1;
662 
663         if (enableMassiveUpdate) {
664             // enable massive update
665             persistenceService.enableMassiveUpdate();
666         }
667 
668         long start = System.currentTimeMillis();
669         try {
670             progressionModel.setTotal(chunkSurveyIds.size());
671             for (int chunk = 0; chunk < chunkSurveyIds.size(); chunk++) {
672 
673                 List<Integer> surveyIds = chunkSurveyIds.get(chunk);
674                 progressionModel.increments(t("dali.service.observation.validation.progression",
675                         allSurveyIds.size(), chunk * chunkSize + 1, chunk * chunkSize + surveyIds.size()));
676                 if (LOG.isDebugEnabled()) {
677                     LOG.debug(String.format("Validating %s-%s of %s surveys", chunk * chunkSize + 1, chunk * chunkSize + surveyIds.size(), allSurveyIds.size()));
678                 }
679                 loopBackService.validateSurveys(surveyIds, validationDate, validationComment);
680             }
681 
682         } finally {
683 
684             if (enableMassiveUpdate) {
685                 // disable massive update (default)
686                 persistenceService.disableMassiveUpdate();
687             }
688         }
689 
690         // perform update on DTO
691         surveysToValidate.forEach(survey -> {
692 
693             // update survey bean validation data
694             survey.setValidationDate(validationDate);
695             survey.setValidationComment(validationComment);
696 
697             // update synchronization status
698             survey.setSynchronizationStatus(SynchronizationStatusValues.READY_TO_SYNCHRONIZE.toSynchronizationStatusDTO());
699 
700             // reset dirty flag
701             survey.setDirty(false);
702 
703         });
704 
705         if (LOG.isDebugEnabled()) {
706             LOG.debug(String.format("Validation total time = %s", Times.durationToString(System.currentTimeMillis() - start)));
707         }
708     }
709 
710     @Override
711     public void validateSurveys(List<Integer> surveyIds, Date validationDate, String validationComment) {
712 
713         Assert.notEmpty(surveyIds);
714         Assert.notNull(validationDate);
715 
716         // Trim to null, to avoid empty comment
717         validationComment = StringUtils.trimToNull(validationComment);
718 
719         Integer validatorId = dataContext.getRecorderPersonId();
720 
721         // recorder person must hold the validation privileges
722         Assert.isTrue(dataContext.isRecorderValidator(), String.format("Recorder %s does not have qualifier privilege", validatorId));
723 
724         long start = System.currentTimeMillis();
725         for (Integer surveyId : surveyIds) {
726 
727             // update survey validation date, set comment and update validation history (validator, date, comment, save old comment)
728             surveyDao.validate(surveyId, validationComment, validationDate, validatorId, true);
729 
730         }
731 
732         if (LOG.isDebugEnabled()) {
733             LOG.debug(String.format("validation of %d survey(s) in %s",
734                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start)));
735         }
736 
737         start = System.currentTimeMillis();
738         int nbSamplingOperations = samplingOperationDao.validateBySurveyIds(surveyIds, validationDate);
739         if (LOG.isDebugEnabled()) {
740             LOG.debug(String.format("validation of %d survey(s) in %s : %s sampling operations",
741                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbSamplingOperations));
742         }
743 
744         start = System.currentTimeMillis();
745         int nbMeasurements = measurementDao.validateAllMeasurementsBySurveyIds(surveyIds, validationDate);
746         if (LOG.isDebugEnabled()) {
747             LOG.debug(String.format("validation of %d survey(s) in %s : %s (taxon)measurements",
748                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbMeasurements));
749         }
750 
751         start = System.currentTimeMillis();
752         int nbPhotos = photoDao.validateBySurveyIds(surveyIds, validationDate);
753         if (LOG.isDebugEnabled()) {
754             LOG.debug(String.format("validation of %d survey(s) in %s : %s photos",
755                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbPhotos));
756         }
757 
758     }
759 
760     /**
761      * {@inheritDoc}
762      */
763     @Override
764     public void unvalidateSurveys(Collection<? extends SurveyDTO> surveys, String unvalidationComment, ProgressionCoreModel progressionModel) {
765 
766         if (CollectionUtils.isEmpty(surveys)) return;
767 
768         // compute unvalidation date
769         Date unvalidationDate = new Date(System.currentTimeMillis());
770 
771         // collect surveys to unvalidate
772         List<SurveyDTO> surveysToUnvalidate = surveys.stream()
773                 .filter(survey -> survey.getId() != null && survey.getValidationDate() != null)
774                 .collect(Collectors.toList());
775 
776         // collect survey ids
777         List<Integer> allSurveyIds = surveysToUnvalidate.stream().map(SurveyDTO::getId).distinct().collect(Collectors.toList());
778         int chunkSize = config.getMassiveProcessChunkSize();
779         List<List<Integer>> chunkSurveyIds = Lists.partition(allSurveyIds, chunkSize);
780         boolean enableMassiveUpdate = chunkSurveyIds.size() > 1;
781 
782         if (enableMassiveUpdate) {
783             // enable massive update
784             persistenceService.enableMassiveUpdate();
785         }
786 
787         long start = System.currentTimeMillis();
788         try {
789             progressionModel.setTotal(chunkSurveyIds.size());
790             for (int chunk = 0; chunk < chunkSurveyIds.size(); chunk++) {
791 
792                 List<Integer> surveyIds = chunkSurveyIds.get(chunk);
793                 progressionModel.increments(t("dali.service.observation.unvalidation.progression",
794                         allSurveyIds.size(), chunk * chunkSize + 1, chunk * chunkSize + surveyIds.size()));
795                 if (LOG.isDebugEnabled()) {
796                     LOG.debug(String.format("Unvalidating %s-%s of %s surveys", chunk * chunkSize + 1, chunk * chunkSize + surveyIds.size(), allSurveyIds.size()));
797                 }
798                 loopBackService.unvalidateSurveys(surveyIds, unvalidationDate, unvalidationComment);
799             }
800 
801         } finally {
802 
803             if (enableMassiveUpdate) {
804                 // disable massive update (default)
805                 persistenceService.disableMassiveUpdate();
806             }
807         }
808 
809         // perform update on DTO
810         surveysToUnvalidate.forEach(survey -> {
811 
812             // update survey bean validation data
813             survey.setValidationDate(null);
814             survey.setValidationComment(null);
815             survey.setQualificationDate(null);
816             survey.setQualificationComment(null);
817             survey.setQualityLevel(referentialService.getNotQualityLevel());
818 
819             // update synchronization status
820             survey.setSynchronizationStatus(SynchronizationStatusValues.DIRTY.toSynchronizationStatusDTO());
821 
822             // reset dirty flag
823             survey.setDirty(false);
824 
825         });
826 
827         if (LOG.isDebugEnabled()) {
828             LOG.debug(String.format("Unvalidation total time = %s", Times.durationToString(System.currentTimeMillis() - start)));
829         }
830     }
831 
832     /**
833      * New method for unvalidate surveys
834      *
835      * @param surveyIds           survey ids to unvalidate
836      * @param unvalidationComment the comment for unvalidation
837      * @param unvalidationDate    the date of unvalidation
838      */
839     @Override
840     public void unvalidateSurveys(List<Integer> surveyIds, Date unvalidationDate, String unvalidationComment) {
841 
842         Assert.notEmpty(surveyIds);
843         Assert.notNull(unvalidationDate);
844         Assert.notBlank(unvalidationComment);
845 
846         // Trim to null, to avoid empty comment
847         unvalidationComment = StringUtils.trimToNull(unvalidationComment);
848 
849         Integer validatorId = dataContext.getRecorderPersonId();
850 
851         // recorder person must hold the validation privileges
852         Assert.isTrue((dataContext.isRecorderValidator()), String.format("Recorder %s does not have qualifier privilege", validatorId));
853 
854         long start = System.currentTimeMillis();
855         for (Integer surveyId : surveyIds) {
856 
857             // update survey validation date, set comment and update validation history (validator, date, comment, save old comment)
858             surveyDao.unvalidate(surveyId, unvalidationComment, unvalidationDate, validatorId);
859 
860         }
861         if (LOG.isDebugEnabled()) {
862             LOG.debug(String.format("unvalidation of %d survey(s) in %s",
863                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start)));
864         }
865 
866         start = System.currentTimeMillis();
867         int nbSamplingOperations = samplingOperationDao.unvalidateBySurveyIds(surveyIds);
868         if (LOG.isDebugEnabled()) {
869             LOG.debug(String.format("unvalidation of %d survey(s) in %s : %s sampling operations",
870                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbSamplingOperations));
871         }
872 
873         start = System.currentTimeMillis();
874         int nbMeasurements = measurementDao.unvalidateAllMeasurementsBySurveyIds(surveyIds);
875         if (LOG.isDebugEnabled()) {
876             LOG.debug(String.format("unvalidation of %d survey(s) in %s : %s (taxon)measurements",
877                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbMeasurements));
878         }
879 
880         start = System.currentTimeMillis();
881         int nbPhotos = photoDao.unvalidateBySurveyIds(surveyIds);
882         if (LOG.isDebugEnabled()) {
883             LOG.debug(String.format("unvalidation of %d survey(s) in %s : %s photos",
884                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbPhotos));
885         }
886 
887     }
888 
889     @Override
890     public void qualifySurveys(Collection<? extends SurveyDTO> surveys, String qualificationComment, QualityLevelDTO qualityLevel, ProgressionCoreModel progressionModel) {
891 
892         if (CollectionUtils.isEmpty(surveys)) return;
893         Assert.notNull(qualityLevel);
894 
895         // compute qualification date
896         Date qualificationDate = new Date(System.currentTimeMillis());
897 
898         // collect surveys to qualify
899         List<SurveyDTO> surveysToQualify = surveys.stream()
900                 .filter(survey -> !survey.isDirty() && survey.getValidationDate() != null)
901                 .collect(Collectors.toList());
902 
903         // collect survey ids
904         List<Integer> allSurveyIds = surveysToQualify.stream().map(SurveyDTO::getId).distinct().collect(Collectors.toList());
905         int chunkSize = config.getMassiveProcessChunkSize();
906         List<List<Integer>> chunkSurveyIds = Lists.partition(allSurveyIds, chunkSize);
907         boolean enableMassiveUpdate = chunkSurveyIds.size() > 1;
908 
909         if (enableMassiveUpdate) {
910             // enable massive update
911             persistenceService.enableMassiveUpdate();
912         }
913 
914         long start = System.currentTimeMillis();
915         try {
916             progressionModel.setTotal(chunkSurveyIds.size());
917             for (int chunk = 0; chunk < chunkSurveyIds.size(); chunk++) {
918 
919                 List<Integer> surveyIds = chunkSurveyIds.get(chunk);
920                 progressionModel.increments(t("dali.service.observation.qualification.progression",
921                         allSurveyIds.size(), chunk * chunkSize + 1, chunk * chunkSize + surveyIds.size()));
922                 if (LOG.isDebugEnabled()) {
923                     LOG.debug(String.format("Qualifying %s-%s of %s surveys", chunk * chunkSize + 1, chunk * chunkSize + surveyIds.size(), allSurveyIds.size()));
924                 }
925                 loopBackService.qualifySurveys(surveyIds, qualificationDate, qualificationComment, qualityLevel.getCode());
926             }
927 
928         } finally {
929 
930             if (enableMassiveUpdate) {
931                 // disable massive update (default)
932                 persistenceService.disableMassiveUpdate();
933             }
934         }
935 
936         // perform update on DTO
937         surveysToQualify.forEach(survey -> {
938 
939             // update survey bean qualification date
940             survey.setQualificationDate(qualificationDate);
941             survey.setQualificationComment(qualificationComment);
942             survey.setQualityLevel(qualityLevel);
943 
944             // reset dirty flag
945             survey.setDirty(false);
946 
947         });
948 
949         if (LOG.isDebugEnabled()) {
950             LOG.debug(String.format("Qualification total time = %s", Times.durationToString(System.currentTimeMillis() - start)));
951         }
952 
953     }
954 
955     @Override
956     public void qualifySurveys(List<Integer> surveyIds, Date qualificationDate, String qualificationComment, String qualityLevelCode) {
957 
958         Assert.notEmpty(surveyIds);
959         Assert.notNull(qualificationDate);
960         Assert.notBlank(qualityLevelCode);
961         // For GOOD quality flag, comment is optional
962         if (!QualityFlagCode.GOOD.getValue().equals(qualityLevelCode)) {
963             Assert.notBlank(qualificationComment);
964         }
965 
966         // Trim to null, to avoid empty comment
967         qualificationComment = StringUtils.trimToNull(qualificationComment);
968 
969         Integer qualifierId = dataContext.getRecorderPersonId();
970 
971         // recorder person must hold the validation privileges
972         Assert.isTrue(dataContext.isRecorderQualifier(), String.format("Recorder %s does not have qualifier privilege", qualifierId));
973 
974         long start = System.currentTimeMillis();
975         for (Integer surveyId : surveyIds) {
976 
977             // update survey qualification date, set comment and update qualification history (validator, date, comment, save old comment)
978             surveyDao.qualify(surveyId, qualificationComment, qualificationDate, qualifierId, qualityLevelCode);
979 
980         }
981 
982         if (LOG.isDebugEnabled()) {
983             LOG.debug(String.format("qualification of %d survey(s) in %s",
984                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start)));
985         }
986 
987         start = System.currentTimeMillis();
988         int nbSamplingOperations = samplingOperationDao.qualifyBySurveyIds(surveyIds, qualificationDate, qualityLevelCode);
989         if (LOG.isDebugEnabled()) {
990             LOG.debug(String.format("qualification of %d survey(s) in %s : %s sampling operations",
991                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbSamplingOperations));
992         }
993 
994         start = System.currentTimeMillis();
995         int nbMeasurements = measurementDao.qualifyAllMeasurementsBySurveyIds(surveyIds, qualificationDate);
996         if (LOG.isDebugEnabled()) {
997             LOG.debug(String.format("qualification of %d survey(s) in %s : %s (taxon)measurements",
998                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbMeasurements));
999         }
1000 
1001         start = System.currentTimeMillis();
1002         int nbPhotos = photoDao.qualifyBySurveyIds(surveyIds, qualificationDate);
1003         if (LOG.isDebugEnabled()) {
1004             LOG.debug(String.format("qualification of %d survey(s) in %s : %s photos",
1005                     surveyIds.size(), Times.durationToString(System.currentTimeMillis() - start), nbPhotos));
1006         }
1007     }
1008 
1009     @Override
1010     public List<ValidationHistoryDTO> getValidationHistory(int surveyId) {
1011         return surveyDao.getValidationHistory(surveyId);
1012     }
1013 
1014     @Override
1015     public List<QualificationHistoryDTO> getQualificationHistory(int surveyId) {
1016         return surveyDao.getQualificationHistory(surveyId);
1017     }
1018 
1019     /**
1020      * {@inheritDoc}
1021      * @return
1022      */
1023     @Override
1024     public long countSurveysWithProgramAndLocations(String programCode, List<Integer> locationIds) {
1025         Assert.notBlank(programCode);
1026 
1027         return surveyDao.countSurveysWithProgramAndLocations(programCode, locationIds);
1028     }
1029 
1030     @Override
1031     public long countSurveysWithProgramLocationAndOutsideDates(String programCode, int appliedStrategyId, int locationId, LocalDate startDate, LocalDate endDate) {
1032         Assert.notBlank(programCode);
1033         Assert.notNull(startDate);
1034         Assert.notNull(endDate);
1035 
1036         return surveyDao.countSurveysWithProgramLocationAndOutsideDates(
1037                 programCode,
1038                 appliedStrategyId,
1039                 locationId,
1040                 Dates.convertToDate(startDate, config.getDbTimezone()),
1041                 Dates.convertToDate(endDate, config.getDbTimezone()));
1042     }
1043 
1044     @Override
1045     public long countSurveysWithProgramLocationAndInsideDates(String programCode, int appliedStrategyId, int locationId, LocalDate startDate, LocalDate endDate) {
1046         Assert.notBlank(programCode);
1047         Assert.notNull(startDate);
1048         Assert.notNull(endDate);
1049 
1050         return surveyDao.countSurveysWithProgramLocationAndInsideDates(
1051                 programCode,
1052                 appliedStrategyId,
1053                 locationId,
1054                 Dates.convertToDate(startDate, config.getDbTimezone()),
1055                 Dates.convertToDate(endDate, config.getDbTimezone()));
1056     }
1057 
1058     @Override
1059     public long countSurveysWithProgramLocationAndOutsideDates(ProgStratDTO progStrat, int locationId) {
1060         Assert.notNull(progStrat);
1061 
1062         return surveyDao.countSurveysWithProgramLocationAndOutsideDates(
1063                 progStrat.getProgram().getCode(),
1064                 progStrat.getAppliedStrategyId(),
1065                 locationId,
1066                 Dates.convertToDate(progStrat.getStartDate(), config.getDbTimezone()),
1067                 Dates.convertToDate(progStrat.getEndDate(), config.getDbTimezone()));
1068     }
1069 
1070     @Override
1071     public long countSurveysWithCampaign(int campaignId) {
1072 
1073         return campaignService.countSurveysWithCampaign(campaignId);
1074     }
1075 
1076     @Override
1077     public List<QualityLevelDTO> getQualityLevels() {
1078 
1079         // Get all quality flags except 'not qualified'
1080         List<QualityLevelDTO> result = new ArrayList<>(referentialService.getQualityLevels(StatusFilter.ACTIVE));
1081         result.remove(referentialService.getNotQualityLevel());
1082         return result;
1083     }
1084 
1085     /*
1086         PRIVATE METHODS
1087      */
1088 
1089     private SurveyDTO getSurvey(Integer surveyId, boolean withoutFilter) {
1090         Assert.notNull(surveyId);
1091 
1092         SurveyDTO survey = surveyDao.getSurveyById(surveyId, withoutFilter);
1093 
1094         // Load Pmfm strategies
1095         survey.setPmfmStrategies(programStrategyService.getPmfmStrategiesBySurvey(survey));
1096 
1097         {
1098             List<PmfmDTO> pmfms = Lists.newArrayList();
1099             List<PmfmDTO> individualPmfms = Lists.newArrayList();
1100 
1101             // Collect Pmfms for survey
1102             for (PmfmStrategyDTO p : survey.getPmfmStrategies()) {
1103                 if (p.isSurvey()) {
1104                     if (p.isGrouping()) {
1105                         individualPmfms.add(p.getPmfm());
1106                     } else {
1107                         pmfms.add(p.getPmfm());
1108                     }
1109                 }
1110             }
1111 
1112             // Add Pmfm strategies on survey
1113             survey.setPmfms(pmfms);
1114             // if needed, filter out some predefined pmfm
1115 //            final List<Integer> pmfmIdsToIgnore = ImmutableList.of(config.getDepthValuesPmfmId());
1116 //            survey.setPmfms(DaliBeans.filterPmfm(pmfms, pmfmIdsToIgnore));
1117             survey.setIndividualPmfms(individualPmfms);
1118         }
1119 
1120         // Collect Pmfms from existing measurements
1121         DaliBeans.populatePmfmsFromMeasurements(survey);
1122 
1123         if (!survey.isSamplingOperationsEmpty()) {
1124 
1125             // Collect Pmfms for sampling operations
1126             List<PmfmDTO> pmfms = Lists.newArrayList();
1127             List<PmfmDTO> individualPmfms = Lists.newArrayList();
1128             for (PmfmStrategyDTO p : survey.getPmfmStrategies()) {
1129                 if (p.isSampling()) {
1130                     if (p.isGrouping()) {
1131                         individualPmfms.add(p.getPmfm());
1132                     } else {
1133                         pmfms.add(p.getPmfm());
1134                     }
1135                 }
1136             }
1137 
1138             for (SamplingOperationDTO samplingOperation : survey.getSamplingOperations()) {
1139 
1140                 // Add Pmfm strategies on sampling operations
1141                 samplingOperation.setPmfms(pmfms);
1142                 samplingOperation.setIndividualPmfms(individualPmfms);
1143 
1144                 // Collect Pmfms from existing measurements
1145                 DaliBeans.populatePmfmsFromMeasurements(samplingOperation);
1146 
1147             }
1148 
1149         }
1150 
1151         return survey;
1152 
1153     }
1154 
1155     private List<TaxonGroupDTO> getAvailableTaxonGroups(boolean forceNoContext) {
1156         if (dataContext.isContextFiltered(FilterTypeValues.TAXON_GROUP) && !forceNoContext) {
1157             return contextService.getFilteredTaxonGroups(dataContext.getContextId());
1158         } else {
1159             return referentialService.getTaxonGroups();
1160         }
1161     }
1162 
1163     private List<TaxonDTO> getAvailableTaxons(boolean forceNoContext) {
1164         List<TaxonDTO> availableTaxons;
1165         if (dataContext.isContextFiltered(FilterTypeValues.TAXON) && !forceNoContext) {
1166             availableTaxons = contextService.getFilteredTaxons(dataContext.getContextId());
1167         } else {
1168             availableTaxons = referentialService.getTaxons(null);
1169         }
1170 
1171         // Remove obsolete taxons
1172         availableTaxons = availableTaxons.stream().filter(taxon->!taxon.isObsolete()).collect(Collectors.toList());
1173 
1174         referentialService.fillReferentTaxons(availableTaxons);
1175         return availableTaxons;
1176     }
1177 
1178     /**
1179      * Apply default analysis department on all measurements from a survey, from strategy
1180      *
1181      * @param survey the survey
1182      */
1183     private void applyDefaultAnalysisDepartment(SurveyDTO survey) {
1184         Assert.notNull(survey);
1185 
1186         // Get analysis departments from apply strategies
1187         DepartmentDTO analysisDepartment = programStrategyService.getSamplingDepartmentOfAppliedStrategyBySurvey(survey);
1188 
1189         // Nothing to apply: skip
1190         if (analysisDepartment == null) {
1191             return;
1192         }
1193 
1194         // If measurement loaded
1195         if (survey.isMeasurementsLoaded()) {
1196             // For each measurement, apply the default analysis department (if not set)
1197             for (MeasurementDTO measurement : survey.getMeasurements()) {
1198                 if (measurement.getAnalyst() == null) {
1199                     measurement.setAnalyst(analysisDepartment);
1200                 }
1201             }
1202 
1203             // For each individual measurement, apply the default analysis department (if not set)
1204             for (MeasurementDTO measurement : survey.getIndividualMeasurements()) {
1205                 if (measurement.getAnalyst() == null) {
1206                     measurement.setAnalyst(analysisDepartment);
1207                 }
1208             }
1209         }
1210 
1211         // if sampling operations are loaded
1212         if (survey.isSamplingOperationsLoaded()
1213                 && CollectionUtils.isNotEmpty(survey.getSamplingOperations())) {
1214 
1215             // Apply on each sampling operation measurements
1216             for (SamplingOperationDTO samplingOperation : survey.getSamplingOperations()) {
1217                 if (samplingOperation.isMeasurementsLoaded()) {
1218                     // For each measurement, apply the default analysis department (if not set)
1219                     for (MeasurementDTO measurement : samplingOperation.getMeasurements()) {
1220                         if (measurement.getAnalyst() == null) {
1221                             measurement.setAnalyst(analysisDepartment);
1222                         }
1223                     }
1224                 }
1225 
1226                 if (samplingOperation.isIndividualMeasurementsLoaded()) {
1227                     // For each measurement, apply the default analysis department (if not set)
1228                     for (MeasurementDTO measurement : samplingOperation.getIndividualMeasurements()) {
1229                         if (measurement.getAnalyst() == null) {
1230                             measurement.setAnalyst(analysisDepartment);
1231                         }
1232                     }
1233                 }
1234             }
1235         }
1236     }
1237 
1238 }