View Javadoc
1   package fr.ifremer.dali.dao.data.survey;
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.ImmutableList;
27  import com.google.common.collect.Lists;
28  import com.google.common.collect.Sets;
29  import fr.ifremer.dali.config.DaliConfiguration;
30  import fr.ifremer.dali.dao.administration.program.DaliProgramDao;
31  import fr.ifremer.dali.dao.administration.strategy.DaliStrategyDao;
32  import fr.ifremer.dali.dao.administration.user.DaliDepartmentDao;
33  import fr.ifremer.dali.dao.administration.user.DaliQuserDao;
34  import fr.ifremer.dali.dao.data.measurement.DaliMeasurementDao;
35  import fr.ifremer.dali.dao.data.photo.DaliPhotoDao;
36  import fr.ifremer.dali.dao.data.samplingoperation.DaliSamplingOperationDao;
37  import fr.ifremer.dali.dao.referential.DaliReferentialDao;
38  import fr.ifremer.dali.dao.referential.monitoringLocation.DaliMonitoringLocationDao;
39  import fr.ifremer.dali.dao.referential.pmfm.DaliPmfmDao;
40  import fr.ifremer.dali.dao.technical.Daos;
41  import fr.ifremer.dali.dao.technical.Geometries;
42  import fr.ifremer.dali.dto.DaliBeanFactory;
43  import fr.ifremer.dali.dto.DaliBeans;
44  import fr.ifremer.dali.dto.configuration.programStrategy.PmfmStrategyDTO;
45  import fr.ifremer.dali.dto.data.measurement.MeasurementDTO;
46  import fr.ifremer.dali.dto.data.photo.PhotoDTO;
47  import fr.ifremer.dali.dto.data.sampling.SamplingOperationDTO;
48  import fr.ifremer.dali.dto.data.survey.CampaignDTO;
49  import fr.ifremer.dali.dto.data.survey.OccasionDTO;
50  import fr.ifremer.dali.dto.data.survey.SurveyDTO;
51  import fr.ifremer.dali.dto.enums.StateValues;
52  import fr.ifremer.dali.dto.enums.SynchronizationStatusValues;
53  import fr.ifremer.dali.dto.referential.PersonDTO;
54  import fr.ifremer.dali.dto.referential.pmfm.PmfmDTO;
55  import fr.ifremer.dali.dto.system.QualificationHistoryDTO;
56  import fr.ifremer.dali.dto.system.ValidationHistoryDTO;
57  import fr.ifremer.dali.service.administration.program.ProgramStrategyService;
58  import fr.ifremer.quadrige3.core.dao.administration.program.Program;
59  import fr.ifremer.quadrige3.core.dao.administration.program.ProgramImpl;
60  import fr.ifremer.quadrige3.core.dao.administration.user.DepartmentImpl;
61  import fr.ifremer.quadrige3.core.dao.administration.user.Quser;
62  import fr.ifremer.quadrige3.core.dao.administration.user.QuserImpl;
63  import fr.ifremer.quadrige3.core.dao.data.survey.CampaignImpl;
64  import fr.ifremer.quadrige3.core.dao.data.survey.OccasionImpl;
65  import fr.ifremer.quadrige3.core.dao.data.survey.Survey;
66  import fr.ifremer.quadrige3.core.dao.data.survey.SurveyDaoImpl;
67  import fr.ifremer.quadrige3.core.dao.referential.*;
68  import fr.ifremer.quadrige3.core.dao.referential.monitoringLocation.MonitoringLocation;
69  import fr.ifremer.quadrige3.core.dao.referential.monitoringLocation.MonitoringLocationImpl;
70  import fr.ifremer.quadrige3.core.dao.referential.monitoringLocation.PositionningSystemImpl;
71  import fr.ifremer.quadrige3.core.dao.referential.pmfm.PmfmImpl;
72  import fr.ifremer.quadrige3.core.dao.system.*;
73  import fr.ifremer.quadrige3.core.dao.system.synchronization.SynchronizationStatus;
74  import fr.ifremer.quadrige3.core.dao.technical.Assert;
75  import fr.ifremer.quadrige3.core.dao.technical.Dates;
76  import org.apache.commons.collections4.CollectionUtils;
77  import org.apache.commons.collections4.IterableUtils;
78  import org.apache.commons.lang3.StringUtils;
79  import org.hibernate.Query;
80  import org.hibernate.Session;
81  import org.hibernate.SessionFactory;
82  import org.hibernate.type.DateType;
83  import org.hibernate.type.IntegerType;
84  import org.hibernate.type.StringType;
85  import org.springframework.beans.factory.InitializingBean;
86  import org.springframework.beans.factory.annotation.Autowired;
87  import org.springframework.dao.DataRetrievalFailureException;
88  import org.springframework.stereotype.Repository;
89  
90  import javax.annotation.Resource;
91  import java.math.BigDecimal;
92  import java.sql.Timestamp;
93  import java.util.*;
94  import java.util.stream.Collectors;
95  
96  import static org.nuiton.i18n.I18n.t;
97  
98  /**
99   * <p>DaliSurveyDaoImpl class.</p>
100  *
101  * @author Ludovic
102  */
103 @Repository("daliSurveyDao")
104 public class DaliSurveyDaoImpl extends SurveyDaoImpl implements DaliSurveyDao, InitializingBean {
105 
106     @Resource(name = "daliReferentialDao")
107     private DaliReferentialDao referentialDao;
108 
109     @Resource(name = "daliMonitoringLocationDao")
110     private DaliMonitoringLocationDao monitoringLocationDao;
111 
112     @Resource(name = "daliDepartmentDao")
113     protected DaliDepartmentDao departmentDao;
114 
115     @Resource(name = "daliProgramDao")
116     private DaliProgramDao programDao;
117 
118     @Resource(name = "daliStrategyDao")
119     private DaliStrategyDao strategyDao;
120 
121     @Resource(name = "daliSamplingOperationDao")
122     private DaliSamplingOperationDao samplingOperationDao;
123 
124     @Resource(name = "daliMeasurementDao")
125     private DaliMeasurementDao measurementDao;
126 
127     @Resource(name = "daliPhotoDao")
128     private DaliPhotoDao photoDao;
129 
130     @Resource(name = "daliQuserDao")
131     private DaliQuserDao quserDao;
132 
133     @Resource(name = "objectTypeDao")
134     private ObjectTypeDao objectTypeDao;
135 
136     @Resource(name = "validationHistoryDao")
137     private ValidationHistoryExtendDao validationHistoryDao;
138 
139     @Resource(name = "qualificationHistoryDao")
140     private QualificationHistoryDao qualificationHistoryDao;
141 
142     @Resource(name = "daliPmfmDao")
143     protected DaliPmfmDao pmfmDao;
144 
145     @Resource(name = "daliProgramStrategyService")
146     protected ProgramStrategyService programStrategyService;
147 
148     @Resource
149     protected DaliConfiguration config;
150 
151     private int pmfmIdSurveyCalculatedLength;
152     private int unitIdMeter;
153     private List<Integer[]> trawlAreaPmfmIdsTriplets;
154 
155     /**
156      * <p>Constructor for DaliSurveyDaoImpl.</p>
157      *
158      * @param sessionFactory a {@link org.hibernate.SessionFactory} object.
159      */
160     @Autowired
161     public DaliSurveyDaoImpl(SessionFactory sessionFactory) {
162         super(sessionFactory);
163     }
164 
165     /**
166      * {@inheritDoc}
167      */
168     @Override
169     public void afterPropertiesSet() {
170         initConstants();
171     }
172 
173     private void initConstants() {
174 
175         pmfmIdSurveyCalculatedLength = config.getSurveyCalculatedLengthPmfmId();
176         unitIdMeter = UnitId.METER.getValue();
177         trawlAreaPmfmIdsTriplets = config.getCalculatedTrawlAreaPmfmTriplets();
178 
179         if (config.isDbCheckConstantsEnable()) {
180             Session session = getSessionFactory().openSession();
181             try {
182                 if (pmfmIdSurveyCalculatedLength > 0) {
183                     Assert.notNull(session.get(PmfmImpl.class, pmfmIdSurveyCalculatedLength));
184                 }
185                 Assert.notNull(session.get(UnitImpl.class, unitIdMeter));
186                 trawlAreaPmfmIdsTriplets.forEach(
187                         triplet -> Arrays.stream(triplet).forEach(
188                                 pmfmId -> Assert.notNull(session.get(PmfmImpl.class, pmfmId)))
189                 );
190             } finally {
191                 Daos.closeSilently(session);
192             }
193 
194         }
195 
196     }
197 
198     /**
199      * {@inheritDoc}
200      */
201     @Override
202     @SuppressWarnings("unchecked")
203     public List<SurveyDTO> getSurveysByCriteria(Integer campaignId, Collection<String> programCodes, Integer locationId, String name, String comment, Integer stateId, Integer synchronizationStatusId,
204                                                 Date startDate, Date endDate, boolean strictDate) {
205 
206         Assert.notEmpty(programCodes, "programCodes must not be empty");
207 
208         // load query from named query
209         StringBuilder queryString = new StringBuilder(getSession().getNamedQuery("surveysByCriteria").getQueryString());
210 
211         // add more predicates
212         if (stateId != null) {
213             if (stateId.equals(StateValues.CONTROLLED.ordinal())) {
214                 queryString.append(System.lineSeparator());
215                 queryString.append("AND s.surveyControlDt is not null AND s.surveyValidDt is null");
216             } else if (stateId.equals(StateValues.VALIDATED.ordinal())) {
217                 queryString.append(System.lineSeparator());
218                 queryString.append("AND s.surveyValidDt is not null AND s.surveyQualifDt is null");
219             } else if (stateId.equals(StateValues.QUALIFIED.ordinal())) {
220                 queryString.append(System.lineSeparator());
221                 queryString.append("AND s.surveyQualifDt is not null");
222             }
223         }
224 
225         // synchronization status
226         if (synchronizationStatusId != null) {
227             SynchronizationStatusValues synchronizationStatus = SynchronizationStatusValues.fromOrdinal(synchronizationStatusId);
228             // Special case if DIRTY : add also READY_TO_SYNC (see mantis #26500)
229             if (synchronizationStatus == SynchronizationStatusValues.DIRTY) {
230                 queryString.append(System.lineSeparator())
231                         .append("AND s.synchronizationStatus IN ('")
232                         .append(synchronizationStatus.getCode())
233                         .append("','")
234                         .append(SynchronizationStatusValues.READY_TO_SYNCHRONIZE.getCode())
235                         .append("')");
236             } else {
237                 queryString.append(System.lineSeparator())
238                         .append("AND s.synchronizationStatus = '")
239                         .append(synchronizationStatus.getCode())
240                         .append('\'');
241             }
242         } else {
243             // ignore DELETED rows by default
244             queryString.append(System.lineSeparator())
245                     .append("AND s.synchronizationStatus <> '")
246                     .append(SynchronizationStatus.DELETED.getValue())
247                     .append('\'');
248         }
249 
250         if (startDate != null && endDate != null) {
251             if (startDate == endDate) {
252                 // equals
253                 queryString.append(System.lineSeparator());
254                 queryString.append("AND s.surveyDt = ");
255                 queryString.append(Daos.convertDateOnlyToSQLString(startDate));
256             } else {
257                 // between
258                 queryString.append(System.lineSeparator());
259                 queryString.append("AND s.surveyDt >=  ");
260                 queryString.append(Daos.convertDateOnlyToSQLString(startDate));
261                 queryString.append("AND s.surveyDt <=  ");
262                 queryString.append(Daos.convertDateOnlyToSQLString(endDate));
263             }
264         } else if (startDate != null) {
265             // >= or >
266             queryString.append(System.lineSeparator());
267             queryString.append("AND s.surveyDt ");
268             queryString.append((strictDate ? "> " : ">= "));
269             queryString.append(Daos.convertDateOnlyToSQLString(startDate));
270         } else if (endDate != null) {
271             // <= or <
272             queryString.append(System.lineSeparator());
273             queryString.append("AND s.surveyDt ");
274             queryString.append((strictDate ? "< " : "<= "));
275             queryString.append(Daos.convertDateOnlyToSQLString(endDate));
276         }
277 
278         // crete new query from query string
279         Query q = getSession().createQuery(queryString.toString());
280         setQueryParams(q, "surveysByCriteria",
281                 "campaignId", IntegerType.INSTANCE, campaignId,
282                 "locationId", IntegerType.INSTANCE, locationId,
283                 "name", StringType.INSTANCE, name,
284                 "comment", StringType.INSTANCE, comment);
285         q.setParameterList("programCodes", programCodes);
286 
287         Iterator<Object[]> it = q.iterate();
288 
289         List<SurveyDTO> result = Lists.newArrayList();
290         while (it.hasNext()) {
291             Object[] source = it.next();
292             result.add(toSurveyDTO(Arrays.asList(source).iterator()));
293         }
294 
295         return result;
296     }
297 
298     /**
299      * {@inheritDoc}
300      */
301     @Override
302     public long countSurveysWithProgramAndLocations(String programCode, List<Integer> locationIds) {
303 
304         Query query;
305         Set<Integer> locationIdsSet = locationIds != null ? locationIds.stream().filter(Objects::nonNull).collect(Collectors.toSet()) : null;
306         if (CollectionUtils.isNotEmpty(locationIdsSet)) {
307             query = createQuery("countSurveysByProgramAndLocations", "programCode", StringType.INSTANCE, programCode);
308             query.setParameterList("locationIds", locationIdsSet);
309         } else {
310             query = createQuery("countSurveysByProgram", "programCode", StringType.INSTANCE, programCode);
311         }
312         return queryCount(query);
313     }
314 
315     @Override
316     public long countSurveysWithProgramLocationAndOutsideDates(String programCode, int appliedStrategyId, int locationId, Date startDate, Date endDate) {
317 
318         return queryCount("countSurveysByProgramLocationAndOutsideDates",
319                 "programCode", StringType.INSTANCE, programCode,
320                 "locationId", IntegerType.INSTANCE, locationId,
321                 "appliedStrategyId", IntegerType.INSTANCE, appliedStrategyId,
322                 "startDate", DateType.INSTANCE, startDate,
323                 "endDate", DateType.INSTANCE, endDate,
324                 "synchronizationStatusToIgnore", StringType.INSTANCE, SynchronizationStatus.DELETED.getValue()
325         );
326     }
327 
328     @Override
329     public long countSurveysWithProgramLocationAndInsideDates(String programCode, int appliedStrategyId, int locationId, Date startDate, Date endDate) {
330 
331         return queryCount("countSurveysByProgramLocationAndInsideDates",
332                 "programCode", StringType.INSTANCE, programCode,
333                 "locationId", IntegerType.INSTANCE, locationId,
334                 "appliedStrategyId", IntegerType.INSTANCE, appliedStrategyId,
335                 "startDate", DateType.INSTANCE, startDate,
336                 "endDate", DateType.INSTANCE, endDate,
337                 "synchronizationStatusToIgnore", StringType.INSTANCE, SynchronizationStatus.DELETED.getValue()
338         );
339     }
340 
341     /**
342      * {@inheritDoc}
343      */
344     @Override
345     public SurveyDTO getSurveyById(int surveyId, boolean dontExcludePmfm) {
346 
347         Object[] row = queryUnique("surveyById",
348                 "surveyId", IntegerType.INSTANCE, surveyId);
349 
350         if (row == null) {
351             throw new DataRetrievalFailureException("can't load survey with id = " + surveyId);
352         }
353 
354         SurveyDTO survey = toSurveyDTO(Arrays.asList(row).iterator());
355 
356         // get observers
357         survey.setObservers(getObservers(survey.getId()));
358         survey.setObserversLoaded(true);
359 
360         // Get measurements
361         loadMeasurements(survey);
362 
363         // Add sampling operations
364         survey.setSamplingOperations(samplingOperationDao.getSamplingOperationsBySurveyId(survey.getId(), true));
365         survey.setSamplingOperationsLoaded(true);
366 
367         // Add photos
368         survey.setPhotos(photoDao.getPhotosBySurveyId(survey.getId()));
369         survey.setPhotosLoaded(true);
370 
371         // Associate Sampling Operations in Photos
372         if (!survey.isPhotosEmpty() && !survey.isSamplingOperationsEmpty()) {
373             Map<Integer, SamplingOperationDTO> samplingOperationMap = DaliBeans.mapById(survey.getSamplingOperations());
374             for (PhotoDTO photo : survey.getPhotos()) {
375                 if (photo.getSamplingOperation() != null && photo.getSamplingOperation().getId() != null) {
376                     // photo.getSamplingOperation() has only an Id
377                     SamplingOperationDTO samplingOperation = samplingOperationMap.get(photo.getSamplingOperation().getId());
378                     if (samplingOperation == null) {
379                         throw new DataRetrievalFailureException(
380                                 String.format("the sampling operation (id=%s) associated with the photo (id=%s) has not been loaded",
381                                         photo.getSamplingOperation().getId(), photo.getId()));
382                     }
383                     photo.setSamplingOperation(samplingOperation);
384                 }
385             }
386         }
387 
388         return survey;
389     }
390 
391     private void loadMeasurements(SurveyDTO survey, Integer... excludePmfmId) {
392 
393         List<MeasurementDTO> allMeasurements = measurementDao.getMeasurementsBySurveyId(survey.getId(), excludePmfmId);
394         if (CollectionUtils.isNotEmpty(allMeasurements)) {
395 
396             // Iterate on all measurement to split them
397             for (MeasurementDTO measurement : allMeasurements) {
398 
399                 if (measurement.getIndividualId() != null) {
400                     // If measurement has an individualId, split to other list
401                     survey.addIndividualMeasurements(measurement);
402                 } else {
403                     survey.addMeasurements(measurement);
404                 }
405             }
406         }
407 
408         survey.setMeasurementsLoaded(true);
409     }
410 
411     /**
412      * {@inheritDoc}
413      */
414     @Override
415     public List<PersonDTO> getObservers(int surveyId) {
416 
417         List<PersonDTO> observers = Lists.newArrayList();
418         Iterator<Integer> it = queryIteratorTyped("quserIdsBySurveyId", "surveyId", IntegerType.INSTANCE, surveyId);
419         while (it.hasNext()) {
420             Integer quserId = it.next();
421             if (quserId != null) {
422                 observers.add(quserDao.getUserById(quserId));
423             }
424         }
425         return observers;
426     }
427 
428     /**
429      * {@inheritDoc}
430      */
431     @Override
432     public SurveyDTO save(SurveyDTO bean) {
433         Assert.notNull(bean);
434         Assert.notNull(bean.getLocation());
435         Assert.notNull(bean.getLocation().getId());
436         Assert.notNull(bean.getDepartment());
437         Assert.notNull(bean.getDepartment().getId());
438         Assert.notNull(bean.getProgram());
439         Assert.notNull(bean.getProgram().getCode());
440         Assert.notNull(bean.getDate());
441 
442         Survey entity;
443         boolean programChanged = false;
444         if (bean.getId() == null) {
445             entity = Survey.Factory.newInstance();
446         } else {
447             entity = get(bean.getId());
448             if (entity == null) {
449                 throw new DataRetrievalFailureException("Could not retrieve survey with id=" + bean.getId());
450             }
451 
452             // Check if program changed (if so, will update children)
453             programChanged = !hasSameProgram(bean, entity);
454         }
455 
456         beanToEntity(bean, entity);
457         update(entity);
458 
459         // save coordinates
460         boolean geometrySaved = saveGeometry(bean, entity);
461 
462         // if a geometry have been saved, calculate length
463         if (geometrySaved && pmfmIdSurveyCalculatedLength > 0) {
464 
465             // Get pmfms from strategy
466             Set<PmfmStrategyDTO> pmfmStrategies = strategyDao.getPmfmStrategiesByProgramCodeAndLocation(
467                     bean.getProgram().getCode(), bean.getLocation().getId(), bean.getDate());
468 
469             // Find the pmfm for calculated length
470             if (pmfmStrategies.stream().anyMatch(pmfmStrategy ->
471                     pmfmStrategy.getPmfm().getId() == pmfmIdSurveyCalculatedLength
472                             && pmfmStrategy.isSurvey() && !pmfmStrategy.isSampling() && !pmfmStrategy.isGrouping())) {
473 
474                 // load all measurements first
475                 if (!bean.isMeasurementsLoaded())
476                     loadMeasurements(bean);
477                 // calculate the length
478                 calculateLength(bean, pmfmIdSurveyCalculatedLength);
479             }
480         }
481 
482         // if trawl area configuration is set, try to calculate it (Mantis #42640)
483         if (CollectionUtils.isNotEmpty(trawlAreaPmfmIdsTriplets)) {
484 
485             for (Integer[] triplet : trawlAreaPmfmIdsTriplets) {
486                 // should be a valid triplet
487                 int trawlAreaPmfmId = triplet[0];
488                 int lengthPmfmId = triplet[1];
489                 int trawlOpeningPmfmId = triplet[2];
490 
491                 if (!isPmfmUnitValidForAreaCalculation(trawlAreaPmfmId, ImmutableList.of(lengthPmfmId, trawlOpeningPmfmId))) {
492                     // calculation impossible (not compatible unit or not existing pmfm), try with next triplet
493                     continue;
494                 }
495 
496                 // check all 3 pmfms are in pmfm strategies
497                 Set<PmfmStrategyDTO> pmfmStrategies = strategyDao.getPmfmStrategiesByProgramCodeAndLocation(
498                         bean.getProgram().getCode(), bean.getLocation().getId(), bean.getDate());
499                 List<PmfmStrategyDTO> surveyStrategies = pmfmStrategies.stream()
500                         .filter(pmfmStrategy -> pmfmStrategy.isSurvey() && !pmfmStrategy.isSampling() && !pmfmStrategy.isGrouping()).collect(Collectors.toList());
501 
502                 if (surveyStrategies.stream().anyMatch(pmfmStrategy -> pmfmStrategy.getPmfm().getId() == trawlAreaPmfmId)
503                         && surveyStrategies.stream().anyMatch(pmfmStrategy -> pmfmStrategy.getPmfm().getId() == lengthPmfmId)
504                         && surveyStrategies.stream().anyMatch(pmfmStrategy -> pmfmStrategy.getPmfm().getId() == trawlOpeningPmfmId)) {
505 
506                     // load all measurements first
507                     if (!bean.isMeasurementsLoaded())
508                         loadMeasurements(bean);
509 
510                     // calculate the area
511                     calculateArea(bean, trawlAreaPmfmId, lengthPmfmId, trawlOpeningPmfmId);
512 
513                     // stop iteration
514                     break;
515                 }
516             }
517         }
518 
519         // save measurements
520         if (bean.isMeasurementsLoaded()) {
521             List<MeasurementDTO> allMeasurements = Lists.newArrayList();
522             allMeasurements.addAll(bean.getMeasurements());
523             allMeasurements.addAll(bean.getIndividualMeasurements());
524             measurementDao.saveMeasurementsBySurveyId(bean.getId(), allMeasurements/*, pmfmIdDepthValues*/);
525         }
526 
527         // save sampling operations
528         if (bean.isSamplingOperationsLoaded()) {
529             samplingOperationDao.saveSamplingOperationsBySurveyId(bean.getId(), bean.getSamplingOperations());
530         }
531 
532         // save photos
533         if (bean.isPhotosLoaded()) {
534             photoDao.savePhotosBySurveyId(bean.getId(), bean.getPhotos());
535         }
536 
537         // Update program on children entities (mantis #37520)
538         if (programChanged) {
539             applySurveyProgramsToChildren(bean.getId());
540         }
541 
542         getSession().flush();
543         getSession().clear();
544 
545         // Update flags has_meas on survey and sampling operations (mantis #28257)
546         updateHasMeasFlag(bean.getId());
547         samplingOperationDao.updateHasMeasFlagBySurveyId(bean.getId());
548 
549         return bean;
550     }
551 
552     /**
553      * {@inheritDoc}
554      */
555     @Override
556     public void remove(List<Integer> surveyIds) {
557         if (CollectionUtils.isNotEmpty(surveyIds)) {
558             for (Integer surveyId : surveyIds) {
559                 remove(surveyId);
560             }
561         }
562     }
563 
564     /**
565      * {@inheritDoc}
566      */
567     @Override
568     public void remove(Integer surveyId) {
569 
570         if (surveyId != null) {
571             // if needed, do some removes here before remove main entity
572 
573             photoDao.removeBySurveyId(surveyId);
574             measurementDao.removeAllMeasurementsBySurveyId(surveyId);
575             samplingOperationDao.removeBySurveyId(surveyId);
576 
577             super.remove(surveyId);
578             getSession().flush();
579             getSession().clear();
580         } // else this is temporary unsaved survey, no need to remove from DB
581     }
582 
583     /**
584      * {@inheritDoc}
585      */
586     @Override
587     public void validate(int surveyId, String validationComment, Date validationDate, Integer validatorId, boolean readyToSynchronize) {
588         Survey survey = get(surveyId);
589 
590         // validation date
591         survey.setSurveyValidDt(validationDate);
592 
593         // qualification comment
594         String oldSurveyValidCm = survey.getSurveyValidCm();
595         if (StringUtils.isBlank(oldSurveyValidCm)) {
596             // try get old comment from history (mantis #45569)
597             oldSurveyValidCm = validationHistoryDao.getLastValidationComment(surveyId, ObjectTypeCode.SURVEY.getValue());
598         }
599         survey.setSurveyValidCm(validationComment);
600 
601         // synchronization status
602         survey.setSynchronizationStatus(readyToSynchronize
603                 ? SynchronizationStatus.READY_TO_SYNCHRONIZE.getValue()
604                 : SynchronizationStatus.DIRTY.getValue());
605 
606         // scope (mantis #28257)
607         survey.setSurveyScope(Daos.convertToString(true));
608 
609         // geometry validation date (mantis #28257)
610         survey.setSurveyGeometryValidDt(validationDate);
611 
612         // insert a validation history line
613         ValidationHistory history = ValidationHistory.Factory.newInstance(
614                 surveyId,
615                 objectTypeDao.get(ObjectTypeCode.SURVEY.getValue()),
616                 quserDao.get(validatorId)
617         );
618         history.setValidHistPreviousCm(oldSurveyValidCm);
619         history.setValidHistOperationCm(validationComment);
620         history.setUpdateDt(new Timestamp(validationDate.getTime()));
621 
622         validationHistoryDao.create(history);
623 
624         update(survey);
625         getSession().flush();
626         getSession().clear();
627     }
628 
629     /**
630      * {@inheritDoc}
631      */
632     @Override
633     public void unvalidate(int surveyId, String unvalidationComment, Date unvalidationDate, Integer validatorId) {
634         Survey survey = get(surveyId);
635 
636         // Reset Validation date
637         survey.setSurveyValidDt(null);
638 
639         // Reset Qualification date & comment (mantis #0026463)
640         survey.setSurveyQualifDt(null);
641         survey.setSurveyQualifCm(null);
642 
643         // Quality flag
644         QualityFlag oldQualityFlag = survey.getQualityFlag();
645         survey.setQualityFlag(getDefaultQualityFlag());
646 
647         String oldSurveyValidCm = survey.getSurveyValidCm();
648         survey.setSurveyValidCm(null);
649 
650         // Synchronization status
651         survey.setSynchronizationStatus(SynchronizationStatus.DIRTY.getValue());
652 
653         // Reset geometry validation date (mantis 28257)
654         survey.setSurveyGeometryValidDt(null);
655 
656         // insert a validation history line
657         ValidationHistory validationHistory = ValidationHistory.Factory.newInstance(
658                 surveyId,
659                 objectTypeDao.get(ObjectTypeCode.SURVEY.getValue()),
660                 quserDao.get(validatorId)
661         );
662         validationHistory.setValidHistPreviousCm(oldSurveyValidCm);
663         validationHistory.setValidHistOperationCm(unvalidationComment);
664         validationHistory.setUpdateDt(new Timestamp(unvalidationDate.getTime()));
665         validationHistoryDao.create(validationHistory);
666 
667         // insert qualification history line
668         QualificationHistory qualificationHistory = QualificationHistory.Factory.newInstance(String.valueOf(surveyId),
669                 objectTypeDao.get(ObjectTypeCode.SURVEY.getValue()),
670                 quserDao.get(validatorId));
671         qualificationHistory.setQualHistOperationCm("Dévalidation"); // TODO à mettre dans une constante ou configOption ?
672         // save previous quality flag
673         qualificationHistory.setQualityFlag(oldQualityFlag);
674         qualificationHistory.setUpdateDt(new Timestamp(unvalidationDate.getTime()));
675 
676         qualificationHistoryDao.create(qualificationHistory);
677 
678         update(survey);
679     }
680 
681     @Override
682     public void qualify(int surveyId, String qualificationComment, Date qualificationDate, Integer validatorId, String qualityFlagCode) {
683         Survey survey = get(surveyId);
684 
685         // qualification date
686         survey.setSurveyQualifDt(qualificationDate);
687 
688         // qualification comment
689         String oldSurveyQualifCm = survey.getSurveyQualifCm();
690         survey.setSurveyQualifCm(qualificationComment);
691 
692         // quality flag
693         QualityFlag qualifiedQualityFlag = load(QualityFlagImpl.class, qualityFlagCode);
694         survey.setQualityFlag(qualifiedQualityFlag);
695 
696         // synchronization status (Mantis #39454)
697         survey.setSynchronizationStatus(SynchronizationStatus.READY_TO_SYNCHRONIZE.getValue());
698 
699         // scope (mantis #28257)
700         survey.setSurveyScope(Daos.convertToString(true));
701 
702         // insert a validation history line
703         QualificationHistory history = QualificationHistoryImpl.Factory.newInstance(String.valueOf(surveyId),
704                 objectTypeDao.get(ObjectTypeCode.SURVEY.getValue()),
705                 quserDao.get(validatorId));
706         history.setQualHistPreviousCm(oldSurveyQualifCm);
707         history.setQualHistOperationCm(qualificationComment);
708         history.setUpdateDt(new Timestamp(qualificationDate.getTime()));
709         history.setQualityFlag(qualifiedQualityFlag);
710 
711         qualificationHistoryDao.create(history);
712 
713         update(survey);
714         getSession().flush();
715         getSession().clear();
716     }
717 
718     @Override
719     public List<ValidationHistoryDTO> getValidationHistory(int surveyId) {
720         List<ValidationHistoryDTO> result = new ArrayList<>();
721         Iterator<Object[]> rows = queryIterator("validationHistoryBySurveyId",
722                 "objectTypeCd", StringType.INSTANCE, ObjectTypeCode.SURVEY.getValue(),
723                 "surveyId", IntegerType.INSTANCE, surveyId);
724         while (rows.hasNext()) {
725             result.add(toValidationHistoryDTO(Arrays.asList(rows.next()).iterator()));
726         }
727         return result;
728     }
729 
730     @Override
731     public List<QualificationHistoryDTO> getQualificationHistory(int surveyId) {
732         List<QualificationHistoryDTO> result = new ArrayList<>();
733         Iterator<Object[]> rows = queryIterator("qualificationHistoryBySurveyId",
734                 "objectTypeCd", StringType.INSTANCE, ObjectTypeCode.SURVEY.getValue(),
735                 "surveyId", StringType.INSTANCE, String.valueOf(surveyId));
736         while (rows.hasNext()) {
737             result.add(toQualificationHistoryDTO(Arrays.asList(rows.next()).iterator()));
738         }
739         return result;
740     }
741 
742     /**
743      * {@inheritDoc}
744      */
745     @Override
746     public void updateSurveysControlDate(Collection<Integer> surveyIds, Date controlDate) {
747         createQuery("updateSurveyControlDate")
748                 .setParameterList("surveyIds", surveyIds)
749                 .setParameter("controlDate", controlDate).executeUpdate();
750     }
751 
752     // ------------------------------------------------
753     // ---------------PRIVATE METHODS------------------
754     // ------------------------------------------------
755     private SurveyDTO toSurveyDTO(Iterator<Object> source) {
756         SurveyDTO result = DaliBeanFactory.newSurveyDTO();
757         result.setId((Integer) source.next());
758 
759         // location
760         Integer locationId = (Integer) source.next();
761         result.setLocation(monitoringLocationDao.getLocationById(locationId));
762 
763         // department
764         Integer departmentId = (Integer) source.next();
765         result.setDepartment(departmentDao.getDepartmentById(departmentId));
766 
767         // Apply DB timezone to survey date (Mantis Reefdb #41597)
768         result.setDate(Dates.convertToLocalDate(Daos.convertToDate(source.next()), config.getDbTimezone()));
769 
770         result.setTime(Daos.convertToInteger((Number) source.next()));
771         result.setName((String) source.next());
772         result.setComment((String) source.next());
773         result.setControlDate(Daos.convertToDate(source.next()));
774         result.setValidationDate(Daos.convertToDate(source.next()));
775         result.setValidationComment((String) source.next());
776         result.setQualificationDate(Daos.convertToDate(source.next()));
777         result.setQualificationComment((String) source.next());
778         result.setUpdateDate(Daos.convertToDate(source.next()));
779         result.setBottomDepth(Daos.convertToDouble((Float) source.next()));
780 
781         // program
782         String programCode = (String) source.next();
783         if (StringUtils.isNotBlank(programCode)) {
784             result.setProgram(programDao.getProgramByCode(programCode));
785         }
786 
787         // campaign
788         CampaignDTO campaign = DaliBeanFactory.newCampaignDTO();
789         campaign.setId((Integer) source.next());
790         campaign.setName((String) source.next());
791         campaign.setStartDate(Daos.convertToDate(source.next()));
792         campaign.setEndDate(Daos.convertToDate(source.next()));
793         if (campaign.getId() != null) {
794             result.setCampaign(campaign);
795         }
796 
797         // get positioning system id
798         Integer positioningSystemId = (Integer) source.next();
799 
800         result.setPositioningComment((String) source.next());
801 
802         // occasion
803         OccasionDTO occasion = DaliBeanFactory.newOccasionDTO();
804         occasion.setId((Integer) source.next());
805         occasion.setName((String) source.next());
806         if (occasion.getId() != null) {
807             result.setOccasion(occasion);
808         }
809 
810         // is actual position ?
811         boolean isActualPosition = Daos.safeConvertToBoolean(source.next(), false);
812 
813         // Geometry and positioning system
814         // -> only keep if actualPosition = true (see mantis #28257)
815         String geometryStr = (String) source.next();
816         if (isActualPosition) {
817             result.setCoordinate(Geometries.getCoordinate(geometryStr));
818 
819             if (positioningSystemId != null) {
820                 result.setPositioning(referentialDao.getPositioningSystemById(positioningSystemId));
821             }
822         }
823 
824         // synchronization status
825         result.setSynchronizationStatus(SynchronizationStatusValues.toSynchronizationStatusDTO((String) source.next()));
826 
827         // Quality flag
828         result.setQualityLevel(referentialDao.getQualityFlagByCode((String) source.next()));
829 
830         return result;
831     }
832 
833     private ValidationHistoryDTO toValidationHistoryDTO(Iterator<Object> source) {
834         ValidationHistoryDTO result = DaliBeanFactory.newValidationHistoryDTO();
835         result.setDate(Daos.convertToDate(source.next()));
836         result.setComment((String) source.next());
837         result.setRecorderPerson(quserDao.getUserById((Integer) source.next()));
838         return result;
839     }
840 
841     private QualificationHistoryDTO toQualificationHistoryDTO(Iterator<Object> source) {
842         QualificationHistoryDTO result = DaliBeanFactory.newQualificationHistoryDTO();
843         result.setDate(Daos.convertToDate(source.next()));
844         result.setComment((String) source.next());
845         String qualityCode = (String) source.next();
846         if (qualityCode != null) result.setQualityLevel(referentialDao.getQualityFlagByCode(qualityCode));
847         result.setRecorderPerson(quserDao.getUserById((Integer) source.next()));
848         return result;
849     }
850 
851     private void beanToEntity(SurveyDTO bean, Survey entity) {
852 
853         // mandatory attributes
854 
855         // quality flag
856         if (entity.getQualityFlag() == null) {
857             // load a default quality flag
858             entity.setQualityFlag(getDefaultQualityFlag());
859         }
860 
861         // monitoring location
862         if (entity.getMonitoringLocation() == null
863                 || entity.getMonitoringLocation().getMonLocId() == null
864                 || !entity.getMonitoringLocation().getMonLocId().equals(bean.getLocation().getId())) {
865             entity.setMonitoringLocation(load(MonitoringLocationImpl.class, bean.getLocation().getId()));
866         }
867 
868         // UT format from monitoring location
869         /* TODO update UT format depending on DST of location and date
870          Get with a shape file containing the zone ids , ans query it by lat/long of location
871          with tz_world.shp , query the TZID  
872 
873          And get the information with :
874          ZoneId.of( "America/Montreal" )
875             .getRules()
876             .isDaylightSavings(
877                 Instant.now() =replace by the survey date
878             )
879         */
880         entity.setSurveyUtFormat(entity.getMonitoringLocation().getMonLocUtFormat());
881 
882         // Recorder Department (Mantis #42614 Only if REC_DEP_ID is null)
883         if (entity.getRecorderDepartment() == null) {
884             entity.setRecorderDepartment(load(DepartmentImpl.class, bean.getDepartment().getId()));
885         }
886 
887         // date
888         Date beanDate = Dates.convertToDate(bean.getDate(), config.getDbTimezone());
889         if (entity.getSurveyDt() == null || !entity.getSurveyDt().equals(beanDate)) {
890             entity.setSurveyDt(beanDate);
891         }
892 
893         // program
894         if (bean.getProgram() != null) {
895             // create a list to trace not updated items, to be able to removeMeasurementsByIds them later
896             Set<Program> remainingPrograms = Sets.newHashSet(entity.getPrograms());
897             // load item to store
898             Program program = load(ProgramImpl.class, bean.getProgram().getCode());
899             if (!entity.getPrograms().contains(program)) {
900                 entity.addPrograms(program);
901             }
902             remainingPrograms.remove(program);
903             // removeMeasurementsByIds unchanged item
904             entity.getPrograms().removeAll(remainingPrograms);
905         } else {
906             entity.getPrograms().clear();
907         }
908 
909         // synchronization status: force to DIRTY
910         entity.setSynchronizationStatus(SynchronizationStatus.DIRTY.getValue());
911         // update also on source bean (Mantis #40769)
912         bean.setSynchronizationStatus(SynchronizationStatusValues.DIRTY.toSynchronizationStatusDTO());
913 
914         // first save if new entity
915         if (entity.getSurveyId() == null) {
916             create(entity);
917             bean.setId(entity.getSurveyId());
918         }
919 
920         // optional attributes
921         entity.setSurveyTime(bean.getTime());
922         entity.setSurveyLb(bean.getName());
923         entity.setSurveyCm(bean.getComment());
924         entity.setSurveyControlDt(bean.getControlDate());
925 
926         // Bottom depth and unit
927         if (bean.getBottomDepth() == null) {
928             entity.setSurveyBottomDepth(null);
929             // Remove unit
930             entity.setBottomDepthUnit(null);
931         } else {
932             // TODO fix mantis 28678 (conversion error : use double instead ? e.g. 1.4 -> 1.39999)
933             entity.setSurveyBottomDepth(bean.getBottomDepth().floatValue());
934 
935             // Set unit as meter (see mantis #28678)
936             entity.setBottomDepthUnit(load(UnitImpl.class, unitIdMeter));
937         }
938 
939         // campaign
940         if (bean.getCampaign() != null) {
941             entity.setCampaign(load(CampaignImpl.class, bean.getCampaign().getId()));
942         } else {
943             entity.setCampaign(null);
944         }
945 
946         // positioning system
947         if (bean.getPositioning() != null) {
948             entity.setPositionningSystem(load(PositionningSystemImpl.class, bean.getPositioning().getId()));
949         } else {
950             entity.setPositionningSystem(null);
951         }
952         entity.setSurveyPositionCm(bean.getPositioningComment());
953 
954         // occasion
955         if (bean.getOccasion() != null) {
956             entity.setOccasion(load(OccasionImpl.class, bean.getOccasion().getId()));
957         } else {
958             entity.setOccasion(null);
959         }
960 
961         // observers
962         if (bean.isObserversLoaded()) {
963             if (!bean.isObserversEmpty()) {
964 
965                 Map<Integer, Quser> remainingUsers = DaliBeans.mapByProperty(entity.getQusers(), "quserId");
966 
967                 for (PersonDTO observer : bean.getObservers()) {
968                     if (remainingUsers.remove(observer.getId()) == null) {
969                         // add user
970                         Quser quser = load(QuserImpl.class, observer.getId());
971                         entity.addQusers(quser);
972                     }
973                 }
974 
975                 if (!remainingUsers.isEmpty()) {
976                     entity.getQusers().removeAll(remainingUsers.values());
977                 }
978 
979             } else if (entity.getQusers() != null) {
980                 entity.getQusers().clear();
981             }
982         }
983     }
984 
985     private boolean hasSameProgram(SurveyDTO bean, Survey entity) {
986 
987         String beanProgCd = bean.getProgram() != null ? bean.getProgram().getCode() : null;
988 
989         List<String> entityProgCds = CollectionUtils.isNotEmpty(entity.getPrograms())
990                 ? DaliBeans.collectProperties(entity.getPrograms(), "progCd")
991                 : null;
992 
993         return (beanProgCd == null && entityProgCds == null)
994                 || (CollectionUtils.size(entityProgCds) == 1 && Objects.equals(beanProgCd, IterableUtils.get(entityProgCds, 0)));
995     }
996 
997     private boolean saveGeometry(SurveyDTO bean, Survey entity) {
998 
999         boolean geometrySaved = false;
1000 
1001         // If user has filled coordinates
1002         if (Geometries.isValid(bean.getCoordinate())) {
1003 
1004             if (Geometries.isPoint(bean.getCoordinate())) {
1005 
1006                 // point
1007                 if (CollectionUtils.size(entity.getSurveyPoints()) == 1) {
1008 
1009                     SurveyPoint surveyPointToUpdate = CollectionUtils.extractSingleton(entity.getSurveyPoints());
1010                     if (!Geometries.equals(bean.getCoordinate(), surveyPointToUpdate.getSurveyPosition())) {
1011                         surveyPointToUpdate.setSurveyPosition(Geometries.getPosition(bean.getCoordinate()));
1012                         getSession().update(surveyPointToUpdate);
1013                     }
1014 
1015                 } else {
1016                     // create a survey point
1017                     SurveyPoint surveyPoint = SurveyPoint.Factory.newInstance();
1018                     surveyPoint.setSurvey(entity);
1019                     surveyPoint.setSurveyPosition(Geometries.getPosition(bean.getCoordinate()));
1020                     getSession().save(surveyPoint);
1021                     entity.getSurveyPoints().clear();
1022                     entity.addSurveyPoints(surveyPoint);
1023                 }
1024 
1025                 // Clear Lines
1026                 entity.getSurveyLines().clear();
1027 
1028             }
1029 
1030             // Line geometry
1031             else {
1032 
1033                 // Survey has an existing line: update it
1034                 if (CollectionUtils.size(entity.getSurveyLines()) == 1) {
1035 
1036                     SurveyLine surveyLine = CollectionUtils.extractSingleton(entity.getSurveyLines());
1037                     if (!Geometries.equals(bean.getCoordinate(), surveyLine.getSurveyPosition())) {
1038                         surveyLine.setSurveyPosition(Geometries.getPosition(bean.getCoordinate()));
1039                         getSession().update(surveyLine);
1040                     }
1041                 }
1042 
1043                 // Survey has NO line: create new
1044                 else {
1045                     // create a survey line
1046                     SurveyLine surveyLine = SurveyLine.Factory.newInstance();
1047                     surveyLine.setSurvey(entity);
1048                     surveyLine.setSurveyPosition(Geometries.getPosition(bean.getCoordinate()));
1049                     getSession().save(surveyLine);
1050                     entity.getSurveyLines().clear();
1051                     entity.addSurveyLines(surveyLine);
1052                 }
1053 
1054                 // Clear Points
1055                 entity.getSurveyPoints().clear();
1056             }
1057 
1058             // Update flag to known is coordinate are filled by user or not (mantis #28257)
1059             entity.setSurveyActualPosition(Daos.convertToString(true));
1060 
1061             // Clear unused coordinates (Areas)
1062             entity.getSurveyAreas().clear();
1063 
1064             geometrySaved = true;
1065         }
1066 
1067         // If a location has been define
1068         else if (bean.getLocation() != null && bean.getLocation().getId() != null) {
1069 
1070             // Copy geom from this location (mantis #28688)
1071             geometrySaved = saveGeometryAndPositioningFromLocation(entity, bean.getLocation().getId());
1072         }
1073 
1074         // No geometry could be set: reset all geometry data
1075         else {
1076             removeGeometry(entity);
1077         }
1078 
1079         // Update survey
1080         // This is need because geometries list could have change (e.g. entity.getSurveyAreas().clear())
1081         update(entity);
1082 
1083         return geometrySaved;
1084     }
1085 
1086     private boolean saveGeometryAndPositioningFromLocation(Survey entity, int monLocId) {
1087 
1088         boolean geometrySaved = false;
1089 
1090         // Retrieve the location entity
1091         MonitoringLocation location = load(MonitoringLocationImpl.class, monLocId);
1092 
1093         // Point geometry
1094         if (CollectionUtils.size(location.getMonLocPoints()) == 1) {
1095             MonLocPoint locationPoint = CollectionUtils.extractSingleton(location.getMonLocPoints());
1096 
1097             // Survey has already a point, so update it
1098             if (CollectionUtils.size(entity.getSurveyPoints()) == 1) {
1099                 SurveyPoint surveyPoint = CollectionUtils.extractSingleton(entity.getSurveyPoints());
1100                 if (!Objects.equals(locationPoint.getMonLocPosition(), surveyPoint.getSurveyPosition())) {
1101                     surveyPoint.setSurveyPosition(locationPoint.getMonLocPosition());
1102                     getSession().update(surveyPoint);
1103                     geometrySaved = true;
1104                 }
1105             }
1106 
1107             // No existing point: create new
1108             else {
1109                 SurveyPoint surveyPoint = SurveyPoint.Factory.newInstance();
1110                 surveyPoint.setSurvey(entity);
1111                 surveyPoint.setSurveyPosition(locationPoint.getMonLocPosition());
1112                 getSession().save(surveyPoint);
1113                 entity.getSurveyPoints().clear();
1114                 entity.addSurveyPoints(surveyPoint);
1115                 geometrySaved = true;
1116             }
1117 
1118             // Clean unused
1119             entity.getSurveyLines().clear();
1120             entity.getSurveyAreas().clear();
1121         }
1122 
1123         // Line geometry
1124         else if (CollectionUtils.size(location.getMonLocLines()) == 1) {
1125             MonLocLine locationLine = CollectionUtils.extractSingleton(location.getMonLocLines());
1126 
1127             // Survey has already a area, so update it
1128             if (CollectionUtils.size(entity.getSurveyLines()) == 1) {
1129                 SurveyLine surveyLine = CollectionUtils.extractSingleton(entity.getSurveyLines());
1130                 if (!Objects.equals(locationLine.getMonLocPosition(), surveyLine.getSurveyPosition())) {
1131                     surveyLine.setSurveyPosition(locationLine.getMonLocPosition());
1132                     getSession().update(surveyLine);
1133                     geometrySaved = true;
1134                 }
1135             }
1136 
1137             // No existing area: create new
1138             else {
1139                 SurveyLine surveyLine = SurveyLine.Factory.newInstance();
1140                 surveyLine.setSurvey(entity);
1141                 surveyLine.setSurveyPosition(locationLine.getMonLocPosition());
1142                 getSession().save(surveyLine);
1143                 entity.getSurveyLines().clear();
1144                 entity.addSurveyLines(surveyLine);
1145                 geometrySaved = true;
1146             }
1147 
1148             // Clean unused
1149             entity.getSurveyPoints().clear();
1150             entity.getSurveyAreas().clear();
1151         }
1152 
1153         // Area geometry
1154         else if (CollectionUtils.size(location.getMonLocAreas()) == 1) {
1155             MonLocArea locationArea = CollectionUtils.extractSingleton(location.getMonLocAreas());
1156 
1157             // Survey has already a area, so update it
1158             if (CollectionUtils.size(entity.getSurveyAreas()) == 1) {
1159                 SurveyArea surveyArea = CollectionUtils.extractSingleton(entity.getSurveyAreas());
1160                 if (!Objects.equals(locationArea.getMonLocPosition(), surveyArea.getSurveyPosition())) {
1161                     surveyArea.setSurveyPosition(locationArea.getMonLocPosition());
1162                     getSession().update(surveyArea);
1163                     geometrySaved = true;
1164                 }
1165             }
1166 
1167             // No existing area: create new
1168             else {
1169                 SurveyArea surveyArea = SurveyArea.Factory.newInstance();
1170                 surveyArea.setSurvey(entity);
1171                 surveyArea.setSurveyPosition(locationArea.getMonLocPosition());
1172                 getSession().save(surveyArea);
1173                 entity.getSurveyAreas().clear();
1174                 entity.addSurveyAreas(surveyArea);
1175                 geometrySaved = true;
1176             }
1177 
1178             // Clean unused
1179             entity.getSurveyPoints().clear();
1180             entity.getSurveyLines().clear();
1181         }
1182 
1183         // Update flag to known is coordinate is not filled by user or not (mantis #28257)
1184         entity.setSurveyActualPosition(Daos.convertToString(false));
1185 
1186         // Apply positioning system from location (see mantis #28706)
1187         entity.setPositionningSystem(location.getPositionningSystem());
1188 
1189         return geometrySaved;
1190     }
1191 
1192     private void removeGeometry(Survey entity) {
1193 
1194         entity.getSurveyPoints().clear();
1195         entity.getSurveyLines().clear();
1196         entity.getSurveyAreas().clear();
1197 
1198         // Update flag to known is coordinate is not filled by user or not (mantis #28257)
1199         entity.setSurveyActualPosition(Daos.convertToString(false));
1200     }
1201 
1202     private void calculateLength(SurveyDTO survey, int calculatedLengthPmfmId) {
1203 
1204         PmfmDTO pmfm = pmfmDao.getPmfmById(calculatedLengthPmfmId);
1205         Assert.notNull(pmfm);
1206         BigDecimal length = null;
1207 
1208         Double lengthInMeter = Geometries.getDistanceInMeter(survey.getCoordinate());
1209         if (lengthInMeter != null) {
1210             // Convert to the correct unit
1211             length = DaliBeans.convertLengthValue(
1212                     BigDecimal.valueOf(lengthInMeter),
1213                     UnitId.METER.getValue(),
1214                     pmfm.getUnit().getId());
1215         }
1216 
1217         // get the existing measurement
1218         MeasurementDTO measurement = DaliBeans.getMeasurementByPmfmId(survey, calculatedLengthPmfmId);
1219 
1220         if (length != null) {
1221             if (measurement == null) {
1222                 // create new
1223                 measurement = DaliBeanFactory.newMeasurementDTO();
1224                 measurement.setPmfm(pmfm);
1225                 survey.addMeasurements(measurement);
1226             }
1227 
1228             // trick the scale to remove unwanted decimal for some unit
1229             if (Objects.equals(pmfm.getUnit().getId(), UnitId.METER.getValue())
1230                     || Objects.equals(pmfm.getUnit().getId(), UnitId.CENTIMENTER.getValue())
1231                     || Objects.equals(pmfm.getUnit().getId(), UnitId.MILLIMETER.getValue())
1232                     || Objects.equals(pmfm.getUnit().getId(), UnitId.MICROMETER.getValue())) {
1233 
1234                 // default precision is the meter
1235                 length = length.setScale(0, BigDecimal.ROUND_HALF_EVEN);
1236                 measurement.setDigitNb(0);
1237 
1238             } else {
1239                 // if other unit used, set to 3 decimals (ie KILOMETER is rounded to meter)
1240                 length = length.setScale(3, BigDecimal.ROUND_HALF_EVEN);
1241                 measurement.setDigitNb(3);
1242             }
1243 
1244             // set value and the caller method do the save
1245             measurement.setNumericalValue(length);
1246             measurement.setComment(t("dali.service.data.survey.calculatedLength.comment"));
1247 
1248             // Set default analyst (Mantis #47285)
1249             measurement.setAnalyst(programStrategyService.getAnalysisDepartmentOfAppliedStrategyBySurvey(survey));
1250 
1251         } else if (measurement != null) {
1252 
1253             // remove measurement if no valid length
1254             survey.removeMeasurements(measurement);
1255         }
1256     }
1257 
1258     private void calculateArea(SurveyDTO survey, int areaPmfmId, int lengthPmfmId, int openingPmfmId) {
1259 
1260         MeasurementDTO lengthMeasurement = DaliBeans.getMeasurementByPmfmId(survey, lengthPmfmId);
1261         MeasurementDTO openingMeasurement = DaliBeans.getMeasurementByPmfmId(survey, openingPmfmId);
1262 
1263         BigDecimal area = null;
1264         if (!DaliBeans.isNumericalMeasurementEmpty(lengthMeasurement) && !DaliBeans.isNumericalMeasurementEmpty(openingMeasurement)) {
1265             // calculate
1266             area = lengthMeasurement.getNumericalValue().multiply(openingMeasurement.getNumericalValue());
1267         }
1268 
1269         // get the existing measurement
1270         MeasurementDTO measurement = DaliBeans.getMeasurementByPmfmId(survey, areaPmfmId);
1271 
1272         if (area != null) {
1273 
1274             if (measurement == null) {
1275                 // create new
1276                 measurement = DaliBeanFactory.newMeasurementDTO();
1277                 measurement.setPmfm(pmfmDao.getPmfmById(areaPmfmId));
1278                 survey.addMeasurements(measurement);
1279             }
1280 
1281             // default precision is the meter
1282             area = area.setScale(0, BigDecimal.ROUND_HALF_EVEN);
1283             measurement.setDigitNb(0);
1284 
1285             // set value and the caller method do the save
1286             measurement.setNumericalValue(area);
1287             measurement.setComment(t("dali.service.data.survey.calculatedArea.comment"));
1288 
1289             // Set default analyst (Mantis #47285)
1290             measurement.setAnalyst(programStrategyService.getAnalysisDepartmentOfAppliedStrategyBySurvey(survey));
1291 
1292         } else if (measurement != null) {
1293 
1294             // remove measurement if no valid area
1295             survey.removeMeasurements(measurement);
1296         }
1297     }
1298 
1299     private boolean isPmfmUnitValidForAreaCalculation(Integer areaPmfmId, Collection<Integer> lengthPmfmIds) {
1300 
1301         Assert.notNull(areaPmfmId);
1302         Assert.notEmpty(lengthPmfmIds);
1303 
1304         List<PmfmDTO> pmfms = pmfmDao.getPmfmsByIds(ImmutableList.of(areaPmfmId));
1305         if (pmfms.isEmpty()) {
1306             // area pmfm not found, return silently
1307             return false;
1308         }
1309 
1310         Integer areaUnitId = pmfms.get(0).getUnit().getId();
1311         Assert.notNull(areaUnitId);
1312 
1313         // get compatibility map from config option
1314         Map<Integer, Integer> compatibleUnits = config.getCompatibleLengthUnitIdByAreaUnitId();
1315 
1316         if (!compatibleUnits.containsKey(areaUnitId)) {
1317             return false; // this area unit id is not in compatibility map
1318         }
1319 
1320         pmfms = pmfmDao.getPmfmsByIds(lengthPmfmIds);
1321         if (pmfms.size() != lengthPmfmIds.size()) {
1322             return false; // not all length pmfm exists in PMFM referential
1323         }
1324 
1325         // get unique length unit
1326         if (pmfms.stream().map(pmfmDTO -> pmfmDTO.getUnit().getId()).distinct().count() > 1) {
1327             return false; // not all length unit are equals
1328         }
1329 
1330         Integer lengthUnitId = pmfms.get(0).getUnit().getId();
1331         Assert.notNull(lengthUnitId);
1332 
1333         // return true if the length unit is is compatible with the area
1334         return lengthUnitId.equals(compatibleUnits.get(areaUnitId));
1335 
1336     }
1337 
1338     /**
1339      * return the default quality flag
1340      */
1341     private QualityFlag getDefaultQualityFlag() {
1342         return load(QualityFlagImpl.class, QualityFlagCode.NOT_QUALIFIED.getValue()); // = non qualifié
1343     }
1344 
1345 }