1 package fr.ifremer.reefdb.service.rulescontrol;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 import com.google.common.base.Predicate;
25 import com.google.common.collect.Lists;
26 import fr.ifremer.quadrige3.core.ProgressionCoreModel;
27 import fr.ifremer.quadrige3.core.dao.technical.Assert;
28 import fr.ifremer.quadrige3.core.dao.technical.Dates;
29 import fr.ifremer.quadrige3.ui.core.dto.CodeOnly;
30 import fr.ifremer.quadrige3.ui.core.dto.referential.BaseReferentialDTO;
31 import fr.ifremer.reefdb.config.ReefDbConfiguration;
32 import fr.ifremer.reefdb.dao.data.measurement.ReefDbMeasurementDao;
33 import fr.ifremer.reefdb.dao.data.samplingoperation.ReefDbSamplingOperationDao;
34 import fr.ifremer.reefdb.dao.data.survey.ReefDbSurveyDao;
35 import fr.ifremer.reefdb.dao.system.rule.ReefDbRuleDao;
36 import fr.ifremer.reefdb.dto.ErrorDTO;
37 import fr.ifremer.reefdb.dto.ReefDbBeanFactory;
38 import fr.ifremer.reefdb.dto.ReefDbBeans;
39 import fr.ifremer.reefdb.dto.configuration.control.ControlRuleDTO;
40 import fr.ifremer.reefdb.dto.configuration.control.RulePmfmDTO;
41 import fr.ifremer.reefdb.dto.data.CoordinateAware;
42 import fr.ifremer.reefdb.dto.data.LocationCoordinateAware;
43 import fr.ifremer.reefdb.dto.data.PositioningPrecisionAware;
44 import fr.ifremer.reefdb.dto.data.measurement.MeasurementAware;
45 import fr.ifremer.reefdb.dto.data.measurement.MeasurementDTO;
46 import fr.ifremer.reefdb.dto.data.sampling.SamplingOperationDTO;
47 import fr.ifremer.reefdb.dto.data.survey.SurveyDTO;
48 import fr.ifremer.reefdb.dto.enums.*;
49 import fr.ifremer.reefdb.dto.referential.pmfm.PmfmDTO;
50 import fr.ifremer.reefdb.service.ReefDbTechnicalException;
51 import fr.ifremer.reefdb.service.observation.ObservationService;
52 import org.apache.commons.collections4.CollectionUtils;
53 import org.apache.commons.lang3.StringUtils;
54 import org.apache.commons.logging.Log;
55 import org.apache.commons.logging.LogFactory;
56 import org.springframework.beans.factory.annotation.Autowired;
57 import org.springframework.stereotype.Service;
58
59 import javax.annotation.Resource;
60 import java.math.BigDecimal;
61 import java.time.LocalDate;
62 import java.util.*;
63 import java.util.stream.Collectors;
64
65 import static org.nuiton.i18n.I18n.t;
66
67
68
69
70 @Service("reefDbControlRuleService")
71 public class ControlRuleServiceImpl implements ControlRuleService {
72
73 private static final Log LOG = LogFactory.getLog(ControlRuleServiceImpl.class);
74
75 @Resource(name = "reefdbSurveyService")
76 protected ObservationService observationService;
77 @Resource(name = "reefDbRuleDao")
78 protected ReefDbRuleDao ruleDao;
79 @Resource(name = "reefDbSurveyDao")
80 protected ReefDbSurveyDao surveyDao;
81 @Resource(name = "reefDbSamplingOperationDao")
82 protected ReefDbSamplingOperationDao operationDao;
83 @Resource(name = "reefDbMeasurementDao")
84 protected ReefDbMeasurementDao measurementDao;
85 @Autowired
86 protected ReefDbConfiguration configuration;
87
88
89 private Map<SurveyDTO, List<ControlRuleDTO>> rulesBySurveyMap = new WeakHashMap<>();
90
91
92
93
94 @Override
95 public ControlRuleMessagesBean controlSurveys(Collection<? extends SurveyDTO> surveys,
96 boolean updateControlDateWhenSucceed,
97 boolean resetControlDateWhenFailed,
98 ProgressionCoreModel progressionModel) {
99
100 Date controlDate = new Date(System.currentTimeMillis());
101 ControlRuleMessagesBean messages = new ControlRuleMessagesBean(controlDate);
102
103 List<Integer> validControlledElements = Lists.newArrayList();
104 List<Integer> invalidControlledElements = Lists.newArrayList();
105
106 List<SurveyDTO> surveysToControl = surveys.stream().filter(survey -> !ReefDbBeans.isSurveyValidated(survey)).collect(Collectors.toList());
107 progressionModel.setTotal(surveysToControl.size());
108
109
110 for (final SurveyDTO survey : surveysToControl) {
111
112 progressionModel.increments(t("reefdb.service.common.progression",
113 t("reefdb.service.control"), progressionModel.getCurrent() + 1, progressionModel.getTotal()));
114
115
116
117 SurveyDTO surveyToControl = observationService.getSurveyWithoutPmfmFiltering(survey.getId());
118
119
120 boolean succeed = controlSurvey(surveyToControl, messages);
121
122 if (succeed && updateControlDateWhenSucceed) {
123 validControlledElements.add(survey.getId());
124
125 survey.setControlDate(controlDate);
126 } else if (!succeed && resetControlDateWhenFailed) {
127 invalidControlledElements.add(survey.getId());
128
129 survey.setControlDate(null);
130 }
131
132
133 survey.setErrors(surveyToControl.getErrors());
134 Map<Integer, SamplingOperationDTO> samplingOperationsMap = ReefDbBeans.mapById(survey.getSamplingOperations());
135 for (SamplingOperationDTO samplingOperationToControl : surveyToControl.getSamplingOperations()) {
136 SamplingOperationDTO samplingOperation = samplingOperationsMap.get(samplingOperationToControl.getId());
137 if (samplingOperation != null) {
138 samplingOperation.setErrors(samplingOperationToControl.getErrors());
139
140
141 }
142 }
143 }
144
145
146 updateSurveysControlDate(validControlledElements, invalidControlledElements, controlDate);
147
148
149 rulesBySurveyMap.clear();
150
151 return messages;
152 }
153
154
155
156
157 @Override
158 public ControlRuleMessagesBean controlSurvey(SurveyDTO survey,
159 boolean updateControlDateWhenSucceed,
160 boolean resetControlDateWhenFailed) {
161
162
163 if (ReefDbBeans.isSurveyValidated(survey)) {
164 return null;
165 }
166
167 Date controlDate = new Date(System.currentTimeMillis());
168 ControlRuleMessagesBean messages = new ControlRuleMessagesBean(controlDate);
169
170 List<Integer> validControlledElements = Lists.newArrayListWithCapacity(1);
171 List<Integer> invalidControlledElements = Lists.newArrayListWithCapacity(1);
172
173 boolean succeed = controlSurvey(survey, messages);
174 if (succeed && updateControlDateWhenSucceed) {
175 validControlledElements.add(survey.getId());
176
177 survey.setControlDate(controlDate);
178 } else if (!succeed && resetControlDateWhenFailed) {
179 invalidControlledElements.add(survey.getId());
180
181 survey.setControlDate(null);
182 }
183
184
185 updateSurveysControlDate(validControlledElements, invalidControlledElements, controlDate);
186
187
188 rulesBySurveyMap.clear();
189
190 return messages;
191 }
192
193 @Override
194 public boolean controlUniqueObject(ControlRuleDTO rule, Object objectToControl) {
195
196
197 ControlRuleMessagesBean messages = new ControlRuleMessagesBean(null);
198 ErrorDTO error = ReefDbBeanFactory.newErrorDTO();
199
200 if (objectToControl instanceof BigDecimal) {
201 validBigDecimal((BigDecimal) objectToControl, rule, messages, error, null);
202 } else if (objectToControl instanceof Double) {
203 validDouble((Double) objectToControl, rule, messages, error);
204 } else if (objectToControl instanceof Integer) {
205 validInteger((Integer) objectToControl, rule, messages, error);
206 } else if (objectToControl instanceof Date) {
207 validDate((Date) objectToControl, rule, messages, error);
208 } else if (objectToControl instanceof LocalDate) {
209 validLocalDate((LocalDate) objectToControl, rule, messages, error);
210 } else if (objectToControl instanceof String) {
211 validString((String) objectToControl, rule, messages, error);
212 } else if (objectToControl instanceof Collection) {
213 validCollection((Collection) objectToControl, rule, messages, error);
214 } else {
215 validObject(objectToControl, rule, messages, error);
216 }
217
218
219 return !error.isError() && !error.isWarning();
220 }
221
222 private boolean controlSurvey(SurveyDTO survey, ControlRuleMessagesBean messages) {
223
224 boolean isSurveyValid = true;
225 survey.getErrors().clear();
226
227
228 final List<ControlRuleDTO> rules = getRules(survey, ControlElementValues.SURVEY);
229 for (final ControlRuleDTO rule : rules) {
230
231
232 final ControlFeatureSurveyValues enumValue = ControlFeatureSurveyValues.getByCode(rule.getControlFeature().getCode());
233 if (enumValue == null) {
234 throw new ReefDbTechnicalException(String.format("ControlFeatureSurveyValues with code=%s has not been found", rule.getControlFeature().getCode()));
235 }
236 final ErrorDTO error = newControlError(ControlElementValues.SURVEY);
237
238
239 switch (enumValue) {
240 case CAMPAIGN:
241 validObject(survey.getCampaign(), rule, messages, error, SurveyDTO.PROPERTY_CAMPAIGN);
242 break;
243 case LOCATION:
244 validObject(survey.getLocation(), rule, messages, error, SurveyDTO.PROPERTY_LOCATION);
245 break;
246 case PROGRAM:
247 validObject(survey.getProgram(), rule, messages, error, SurveyDTO.PROPERTY_PROGRAM);
248 break;
249 case VALIDATION_COMMENT:
250 validString(survey.getValidationComment(), rule, messages, error, SurveyDTO.PROPERTY_VALIDATION_COMMENT);
251 break;
252 case DATE:
253 validLocalDate(survey.getDate(), rule, messages, error, SurveyDTO.PROPERTY_DATE);
254 break;
255 case CONTROL_DATE:
256 validDate(survey.getControlDate(), rule, messages, error, SurveyDTO.PROPERTY_CONTROL_DATE);
257 break;
258 case UPDATE_DATE:
259 validDate(survey.getUpdateDate(), rule, messages, error, SurveyDTO.PROPERTY_UPDATE_DATE);
260 break;
261 case VALIDATION_DATE:
262 validDate(survey.getValidationDate(), rule, messages, error, SurveyDTO.PROPERTY_VALIDATION_DATE);
263 break;
264 case TIME:
265 validInteger(survey.getTime(), rule, messages, error, SurveyDTO.PROPERTY_TIME);
266 break;
267 case COMMENT:
268 validString(survey.getComment(), rule, messages, error, SurveyDTO.PROPERTY_COMMENT);
269 break;
270 case LATITUDE_REAL_SURVEY:
271 validDouble(survey.getCoordinate() == null ? null : survey.getCoordinate().getMinLatitude(), rule, messages, error, CoordinateAware.PROPERTY_LATITUDE);
272 break;
273 case LONGITUDE_REAL_SURVEY:
274 validDouble(survey.getCoordinate() == null ? null : survey.getCoordinate().getMinLongitude(), rule, messages, error, CoordinateAware.PROPERTY_LONGITUDE);
275 break;
276 case NAME:
277 validString(survey.getName(), rule, messages, error, SurveyDTO.PROPERTY_NAME);
278 break;
279 case DEPARTMENT:
280 validObject(survey.getRecorderDepartment(), rule, messages, error, SurveyDTO.PROPERTY_RECORDER_DEPARTMENT);
281 break;
282 case POSITIONING:
283 validObject(survey.getPositioning(), rule, messages, error, SurveyDTO.PROPERTY_POSITIONING);
284 break;
285 case POSITIONING_PRECISION:
286 if (survey.getPositioning() != null) {
287 validString(survey.getPositioning().getPrecision(), rule, messages, error, PositioningPrecisionAware.PROPERTY_POSITIONING_PRECISION);
288 }
289 break;
290 case PRECISE_DEPTH:
291 validDouble(survey.getPreciseDepth(), rule, messages, error, SurveyDTO.PROPERTY_PRECISE_DEPTH);
292 break;
293 case LATITUDE_MAX_LOCATION:
294 validDouble(survey.getLocation().getCoordinate() == null ? null : survey.getLocation().getCoordinate().getMaxLatitude(), rule, messages, error, LocationCoordinateAware.PROPERTY_LOCATION_MAX_LATITUDE);
295 break;
296 case LATITUDE_MIN_LOCATION:
297 validDouble(survey.getLocation().getCoordinate() == null ? null : survey.getLocation().getCoordinate().getMinLatitude(), rule, messages, error, LocationCoordinateAware.PROPERTY_LOCATION_MIN_LATITUDE);
298 break;
299 case LONGITUDE_MAX_LOCATION:
300 validDouble(survey.getLocation().getCoordinate() == null ? null : survey.getLocation().getCoordinate().getMaxLongitude(), rule, messages, error, LocationCoordinateAware.PROPERTY_LOCATION_MAX_LONGITUDE);
301 break;
302 case LONGITUDE_MIN_LOCATION:
303 validDouble(survey.getLocation().getCoordinate() == null ? null : survey.getLocation().getCoordinate().getMinLongitude(), rule, messages, error, LocationCoordinateAware.PROPERTY_LOCATION_MIN_LONGITUDE);
304 break;
305 default:
306 break;
307 }
308 if (error.isError() || error.isWarning()) {
309 survey.addErrors(error);
310 if (error.isError()) {
311 isSurveyValid = false;
312 }
313 }
314
315 }
316
317
318 if (!controlMeasurements(survey, getRules(survey, ControlElementValues.MEASUREMENT), messages)) {
319 isSurveyValid = false;
320 }
321
322
323 List<Integer> validControlledElements = Lists.newArrayList();
324 List<Integer> invalidControlledElements = Lists.newArrayList();
325 for (final SamplingOperationDTO samplingOperation : survey.getSamplingOperations()) {
326 if (controlSamplingOperation(survey, samplingOperation, messages)) {
327 validControlledElements.add(samplingOperation.getId());
328 samplingOperation.setControlDate(messages.getControlDate());
329 } else {
330 invalidControlledElements.add(samplingOperation.getId());
331 samplingOperation.setControlDate(null);
332 }
333 }
334
335
336 updateSamplingOperationsControlDate(validControlledElements, invalidControlledElements, messages.getControlDate());
337
338 return isSurveyValid && invalidControlledElements.size() == 0;
339 }
340
341
342
343
344
345
346 private boolean controlSamplingOperation(SurveyDTO survey, SamplingOperationDTO samplingOperation, ControlRuleMessagesBean messages) {
347
348 boolean isSamplingOperationValid = true;
349
350
351 samplingOperation.getErrors().clear();
352
353
354 final List<ControlRuleDTO> rules = getRules(survey, ControlElementValues.SAMPLING_OPERATION);
355 for (final ControlRuleDTO rule : rules) {
356
357
358 final ControlFeatureSamplingOperationValues enumValue = ControlFeatureSamplingOperationValues.getByCode(rule.getControlFeature().getCode());
359 if (enumValue == null) {
360 throw new ReefDbTechnicalException(String.format("ControlFeatureSamplingOperationValues with code=%s has not been found", rule.getControlFeature().getCode()));
361 }
362 final ErrorDTO error = newControlError(ControlElementValues.SAMPLING_OPERATION);
363
364
365 switch (enumValue) {
366 case TIME:
367 validInteger(samplingOperation.getTime(), rule, messages, error, SamplingOperationDTO.PROPERTY_TIME);
368 break;
369 case COMMENT:
370 validString(samplingOperation.getComment(), rule, messages, error, SamplingOperationDTO.PROPERTY_COMMENT);
371 break;
372 case DEPTH:
373 validDouble(samplingOperation.getDepth(), rule, messages, error, SamplingOperationDTO.PROPERTY_DEPTH);
374 break;
375 case DEPTH_MAX:
376 validDouble(samplingOperation.getMaxDepth(), rule, messages, error, SamplingOperationDTO.PROPERTY_MAX_DEPTH);
377 break;
378 case DEPTH_MIN:
379 validDouble(samplingOperation.getMinDepth(), rule, messages, error, SamplingOperationDTO.PROPERTY_MIN_DEPTH);
380 break;
381 case LATITUDE_REAL:
382 validDouble(samplingOperation.getCoordinate() == null ? null : samplingOperation.getCoordinate().getMinLatitude(), rule, messages, error, CoordinateAware.PROPERTY_LATITUDE);
383 break;
384 case LONGITUDE_REAL:
385 validDouble(samplingOperation.getCoordinate() == null ? null : samplingOperation.getCoordinate().getMinLongitude(), rule, messages, error, CoordinateAware.PROPERTY_LONGITUDE);
386 break;
387 case NAME:
388 validString(samplingOperation.getName(), rule, messages, error, SamplingOperationDTO.PROPERTY_NAME);
389 break;
390 case POSITIONING:
391 validObject(samplingOperation.getPositioning(), rule, messages, error, SamplingOperationDTO.PROPERTY_POSITIONING);
392 break;
393 case POSITIONING_PRECISION:
394 if (samplingOperation.getPositioning() != null) {
395 validString(samplingOperation.getPositioning().getPrecision(), rule, messages, error, SamplingOperationDTO.PROPERTY_POSITIONING);
396 }
397 break;
398 case GEAR:
399 validObject(samplingOperation.getSamplingEquipment(), rule, messages, error, SamplingOperationDTO.PROPERTY_SAMPLING_EQUIPMENT);
400 break;
401 case SIZE:
402 validDouble(samplingOperation.getSize(), rule, messages, error, SamplingOperationDTO.PROPERTY_SIZE);
403 break;
404 case SIZE_UNIT:
405 validObject(samplingOperation.getSizeUnit(), rule, messages, error, SamplingOperationDTO.PROPERTY_SIZE_UNIT);
406 break;
407 case DEPARTMENT:
408 validObject(samplingOperation.getSamplingDepartment(), rule, messages, error, SamplingOperationDTO.PROPERTY_SAMPLING_DEPARTMENT);
409 break;
410
411 default:
412 break;
413 }
414
415 if (error.isError() || error.isWarning()) {
416 samplingOperation.addErrors(error);
417 if (error.isError()) {
418 isSamplingOperationValid = false;
419 }
420 }
421
422 }
423
424
425 boolean isMeasurementsValid = controlMeasurements(samplingOperation, getRules(survey, ControlElementValues.MEASUREMENT), messages);
426
427 return isSamplingOperationValid && isMeasurementsValid;
428 }
429
430
431
432
433
434
435 private boolean controlMeasurements(MeasurementAware bean, List<ControlRuleDTO> rules, ControlRuleMessagesBean messages) {
436
437 List<ErrorDTO> errors = Lists.newArrayList();
438
439
440 List<Integer> validMeasurementsElements = Lists.newArrayList();
441 List<Integer> invalidMeasurementsElements = Lists.newArrayList();
442 List<Integer> validTaxonMeasurementsElements = Lists.newArrayList();
443 List<Integer> invalidTaxonMeasurementsElements = Lists.newArrayList();
444
445
446 List<MeasurementDTO> measurementsToControl = Lists.newArrayList();
447 List<MeasurementDTO> individualMeasurementsToControl = Lists.newArrayList();
448 ReefDbBeans.populateMeasurementsFromPmfms(bean, measurementsToControl, individualMeasurementsToControl);
449
450
451 for (MeasurementDTO measurement : measurementsToControl) {
452 if (!ReefDbBeans.isMeasurementEmpty(measurement)) {
453 validMeasurementsElements.add(measurement.getId());
454 }
455 }
456 for (MeasurementDTO individualMeasurement : individualMeasurementsToControl) {
457 if (!ReefDbBeans.isMeasurementEmpty(individualMeasurement)) {
458 if (ReefDbBeans.isTaxonMeasurement(individualMeasurement)) {
459 validTaxonMeasurementsElements.add(individualMeasurement.getId());
460 } else {
461 validMeasurementsElements.add(individualMeasurement.getId());
462 }
463 }
464 }
465
466 for (final ControlRuleDTO rule : rules) {
467
468
469 for (final MeasurementDTO measurement : measurementsToControl) {
470 if (isPmfmFoundInRule(measurement.getPmfm(), rule)) {
471 ErrorDTO error = controlMeasurementDetail(measurement, rule, false, messages);
472 if (error.isError() || error.isWarning()) {
473 errors.add(error);
474 }
475 if (error.isError()) {
476 invalidMeasurementsElements.add(measurement.getId());
477 measurement.setControlDate(null);
478 } else {
479 measurement.setControlDate(messages.getControlDate());
480 }
481 }
482 }
483
484
485 for (final MeasurementDTO measurement : individualMeasurementsToControl) {
486 if (isPmfmFoundInRule(measurement.getPmfm(), rule)) {
487 ErrorDTO error = controlMeasurementDetail(measurement, rule, true, messages);
488 if (error.isError() || error.isWarning()) {
489 errors.add(error);
490 }
491 if (error.isError()) {
492 if (ReefDbBeans.isTaxonMeasurement(measurement)) {
493 invalidTaxonMeasurementsElements.add(measurement.getId());
494 } else {
495 invalidMeasurementsElements.add(measurement.getId());
496 }
497 measurement.setControlDate(null);
498 } else {
499 measurement.setControlDate(messages.getControlDate());
500 }
501 }
502 }
503 }
504
505
506 validMeasurementsElements.removeAll(invalidMeasurementsElements);
507 validTaxonMeasurementsElements.removeAll(invalidTaxonMeasurementsElements);
508
509 updateMeasurementsControlDate(validMeasurementsElements, invalidMeasurementsElements, messages.getControlDate());
510 updateTaxonMeasurementsControlDate(validTaxonMeasurementsElements, invalidTaxonMeasurementsElements, messages.getControlDate());
511
512
513 bean.getErrors().addAll(errors);
514
515 return invalidMeasurementsElements.size() + invalidTaxonMeasurementsElements.size() == 0;
516 }
517
518
519
520
521
522
523
524 private ErrorDTO controlMeasurementDetail(
525 MeasurementDTO measurement,
526 ControlRuleDTO rule,
527 boolean isIndividual,
528 ControlRuleMessagesBean messages) {
529
530
531 measurement.getErrors().clear();
532
533
534 final ControlFeatureMeasurementValues enumValue = ControlFeatureMeasurementValues.getByCode(rule.getControlFeature().getCode());
535 if (enumValue == null) {
536 throw new ReefDbTechnicalException(String.format("ControlFeatureMeasurementValues with code=%s has not been found", rule.getControlFeature().getCode()));
537 }
538 final ErrorDTO error = newControlError(ControlElementValues.MEASUREMENT);
539
540
541 switch (enumValue) {
542 case ANALYST:
543 if (!ReefDbBeans.isMeasurementEmpty(measurement)) {
544 validObject(measurement.getAnalyst(), rule, messages, error, MeasurementDTO.PROPERTY_ANALYST);
545 }
546 break;
547 case PMFM:
548 validObject(measurement.getPmfm(), rule, messages, error, measurement.getPmfm().getId(), isIndividual ? SurveyDTO.PROPERTY_INDIVIDUAL_PMFMS : SurveyDTO.PROPERTY_PMFMS);
549 break;
550 case NUMERICAL_VALUE:
551 validBigDecimal(measurement.getNumericalValue(), rule, messages, error, measurement.getPmfm().getId(), isIndividual ? SurveyDTO.PROPERTY_INDIVIDUAL_PMFMS : SurveyDTO.PROPERTY_PMFMS);
552 break;
553 case QUALITATIVE_VALUE:
554 validObject(measurement.getQualitativeValue(), rule, messages, error, measurement.getPmfm().getId(), isIndividual ? SurveyDTO.PROPERTY_INDIVIDUAL_PMFMS : SurveyDTO.PROPERTY_PMFMS);
555 break;
556 case TAXON:
557 if (isIndividual) {
558 validObject(measurement.getTaxon(), rule, messages, error, MeasurementDTO.PROPERTY_TAXON);
559 }
560 break;
561 case TAXON_GROUP:
562 if (isIndividual) {
563 validObject(measurement.getTaxonGroup(), rule, messages, error, MeasurementDTO.PROPERTY_TAXON_GROUP);
564 }
565 break;
566 default:
567 break;
568 }
569
570 if (error.isError() || error.isWarning()) {
571 if (isIndividual) {
572 error.setIndividualId(measurement.getIndividualId());
573 }
574 if (measurement.getErrors() == null) {
575 measurement.setErrors(new ArrayList<>());
576 }
577 measurement.addErrors(error);
578 }
579
580 return error;
581 }
582
583
584
585
586
587
588
589
590
591
592 private void validObject(Object object, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, String... propertyNames) {
593 validObject(object, rule, messages, error, null, propertyNames);
594 }
595
596
597
598
599
600
601
602
603
604
605
606 private void validObject(Object object, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, Integer pmfmId, String... propertyNames) {
607
608 switch (ControlFunctionValues.getFunctionValue(rule.getFunction().getId())) {
609
610 case IS_EMPTY:
611 if (object != null) {
612 addMessage(messages, rule, error, pmfmId, propertyNames);
613 }
614 break;
615
616 case NOT_EMPTY:
617 if (object == null) {
618 addMessage(messages, rule, error, pmfmId, propertyNames);
619 }
620 break;
621
622 case IS_AMONG:
623 if (rule.getAllowedValues() != null) {
624
625 List<String> allowedValues = Lists.newArrayList(rule.getAllowedValues().split(configuration.getValueSeparator()));
626 if (allowedValues.isEmpty()) break;
627
628 if (object instanceof BaseReferentialDTO) {
629 BaseReferentialDTO baseObject = (BaseReferentialDTO) object;
630
631
632 if (StringUtils.isNumeric(allowedValues.get(0))) {
633
634 if (baseObject instanceof CodeOnly) {
635
636
637 if (LOG.isDebugEnabled()) {
638 LOG.debug(String.format("the %s '%s' is not comparable with allowed values %s",
639 baseObject.getClass(), ((CodeOnly) baseObject).getCode(), allowedValues));
640 }
641 addMessage(messages, rule, error, pmfmId, propertyNames);
642
643 } else {
644
645
646 if (!allowedValues.contains(baseObject.getId().toString())) {
647 if (LOG.isDebugEnabled()) {
648 LOG.debug(String.format("the %s '%s' is not in allowed values %s",
649 baseObject.getClass(), baseObject.getId(), allowedValues));
650 }
651 addMessage(messages, rule, error, pmfmId, propertyNames);
652 }
653 }
654
655 } else {
656
657 if (baseObject instanceof CodeOnly) {
658
659
660 CodeOnly codeBaseObject = (CodeOnly) baseObject;
661 if (!allowedValues.contains(codeBaseObject.getCode())) {
662 if (LOG.isDebugEnabled()) {
663 LOG.debug(String.format("the %s '%s' is not in allowed values %s",
664 baseObject.getClass(), codeBaseObject.getCode(), allowedValues));
665 }
666 addMessage(messages, rule, error, pmfmId, propertyNames);
667 }
668
669 } else {
670
671
672 if (!allowedValues.contains(baseObject.getName())) {
673 if (LOG.isDebugEnabled()) {
674 LOG.debug(String.format("the %s '%s' is not in allowed values %s",
675 baseObject.getClass(), baseObject.getName(), allowedValues));
676 }
677 addMessage(messages, rule, error, pmfmId, propertyNames);
678 }
679 }
680 }
681 } else {
682
683 addMessage(messages, rule, error, pmfmId, propertyNames);
684 }
685 }
686 break;
687 default:
688
689 break;
690 }
691 }
692
693 private void validCollection(Collection collection, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, String... propertyNames) {
694
695 switch (ControlFunctionValues.getFunctionValue(rule.getFunction().getId())) {
696
697 case IS_EMPTY:
698 if (CollectionUtils.isNotEmpty(collection)) {
699 addMessage(messages, rule, error, null, propertyNames);
700 }
701 break;
702
703 case NOT_EMPTY:
704 if (CollectionUtils.isEmpty(collection)) {
705 addMessage(messages, rule, error, null, propertyNames);
706 }
707 break;
708
709 case IS_AMONG:
710 if (rule.getAllowedValues() != null) {
711 if (CollectionUtils.isNotEmpty(collection)) {
712
713
714 for (Object object : collection) {
715
716 validObject(object, rule, messages, error, propertyNames);
717
718
719 if (error.isWarning() || error.isError()) break;
720 }
721
722 } else {
723 addMessage(messages, rule, error, null, propertyNames);
724 }
725 }
726 break;
727 default:
728
729 break;
730 }
731
732 }
733
734
735
736
737
738
739
740
741
742
743 private void validDate(Date dateValue, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, String... propertyNames) {
744
745 switch (ControlFunctionValues.getFunctionValue(rule.getFunction().getId())) {
746
747 case IS_EMPTY:
748 if (dateValue != null) {
749 addMessage(messages, rule, error, null, propertyNames);
750 }
751 break;
752
753 case NOT_EMPTY:
754 if (dateValue == null) {
755 addMessage(messages, rule, error, null, propertyNames);
756 }
757 break;
758
759 case MIN_MAX_DATE:
760 if (dateValue == null) {
761 addMessage(messages, rule, error, null, propertyNames);
762 } else {
763
764
765 Date minDate = null;
766 if (rule.getMin() != null) {
767 Object min = rule.getMin();
768 if (min instanceof Date) {
769 minDate = (Date) min;
770 } else if (min instanceof LocalDate) {
771 minDate = Dates.convertToDate((LocalDate) min, configuration.getDbTimezone());
772 } else {
773 throw new ReefDbTechnicalException(String.format("the min date in rule %s is invalid : %s", rule.getCode(), min));
774 }
775 }
776
777
778 Date maxDate = null;
779 if (rule.getMax() != null) {
780 Object max = rule.getMax();
781 if (max instanceof Date) {
782 maxDate = (Date) max;
783 } else if (max instanceof LocalDate) {
784 maxDate = Dates.convertToDate((LocalDate) max, configuration.getDbTimezone());
785 } else {
786 throw new ReefDbTechnicalException(String.format("the max date in rule %s is invalid : %s", rule.getCode(), max));
787 }
788 }
789
790
791 if (minDate != null && maxDate != null) {
792 if (dateValue.before(minDate) || dateValue.after(maxDate)) {
793 addMessage(messages, rule, error, null, propertyNames);
794 }
795 } else if (minDate != null) {
796 if (dateValue.before(minDate)) {
797 addMessage(messages, rule, error, null, propertyNames);
798 }
799 } else if (maxDate != null) {
800 if (dateValue.after(maxDate)) {
801 addMessage(messages, rule, error, null, propertyNames);
802 }
803 }
804 }
805 break;
806
807 default:
808
809 break;
810 }
811
812 }
813
814
815
816
817
818
819
820
821
822
823 private void validLocalDate(LocalDate dateValue, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, String... propertyNames) {
824
825 switch (ControlFunctionValues.getFunctionValue(rule.getFunction().getId())) {
826
827 case IS_EMPTY:
828 if (dateValue != null) {
829 addMessage(messages, rule, error, null, propertyNames);
830 }
831 break;
832
833 case NOT_EMPTY:
834 if (dateValue == null) {
835 addMessage(messages, rule, error, null, propertyNames);
836 }
837 break;
838
839 case MIN_MAX_DATE:
840 if (dateValue == null) {
841 addMessage(messages, rule, error, null, propertyNames);
842 } else {
843
844
845 LocalDate minDate = null;
846 if (rule.getMin() != null) {
847 Object min = rule.getMin();
848 if (min instanceof Date) {
849 minDate = Dates.convertToLocalDate((Date) min, configuration.getDbTimezone());
850 } else if (min instanceof LocalDate) {
851 minDate = (LocalDate) min;
852 } else {
853 throw new ReefDbTechnicalException(String.format("the min date in rule %s is invalid : %s", rule.getCode(), min));
854 }
855 }
856
857
858 LocalDate maxDate = null;
859 if (rule.getMax() != null) {
860 Object max = rule.getMax();
861 if (max instanceof Date) {
862 maxDate = Dates.convertToLocalDate((Date) max, configuration.getDbTimezone());
863 } else if (max instanceof LocalDate) {
864 maxDate = (LocalDate) max;
865 } else {
866 throw new ReefDbTechnicalException(String.format("the max date in rule %s is invalid : %s", rule.getCode(), max));
867 }
868 }
869
870
871 if (minDate != null && maxDate != null) {
872 if (dateValue.isBefore(minDate) || dateValue.isAfter(maxDate)) {
873 addMessage(messages, rule, error, null, propertyNames);
874 }
875 } else if (minDate != null) {
876 if (dateValue.isBefore(minDate)) {
877 addMessage(messages, rule, error, null, propertyNames);
878 }
879 } else if (maxDate != null) {
880 if (dateValue.isAfter(maxDate)) {
881 addMessage(messages, rule, error, null, propertyNames);
882 }
883 }
884 }
885 break;
886
887 default:
888
889 break;
890 }
891
892 }
893
894
895
896
897
898
899
900
901
902
903 @SuppressWarnings("unused")
904 private void validInteger(Integer integerValue, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, String... propertyNames) {
905
906 switch (ControlFunctionValues.getFunctionValue(rule.getFunction().getId())) {
907
908 case IS_EMPTY:
909 if (integerValue != null) {
910 addMessage(messages, rule, error, null, propertyNames);
911 }
912 break;
913
914 case NOT_EMPTY:
915 if (integerValue == null) {
916 addMessage(messages, rule, error, null, propertyNames);
917 }
918 break;
919
920 case MIN_MAX:
921 if (integerValue == null) {
922 addMessage(messages, rule, error, null, propertyNames);
923 } else {
924
925
926 Integer minValue = null;
927 if (rule.getMin() != null) {
928 minValue = (Integer) rule.getMin();
929 }
930
931
932 Integer maxValue = null;
933 if (rule.getMax() != null) {
934 maxValue = (Integer) rule.getMax();
935 }
936
937
938 if (minValue != null && maxValue != null) {
939 if (integerValue < minValue && integerValue > maxValue) {
940 addMessage(messages, rule, error, null, propertyNames);
941 }
942 }
943 if (minValue != null) {
944 if (integerValue < minValue) {
945 addMessage(messages, rule, error, null, propertyNames);
946 }
947 }
948 if (maxValue != null) {
949 if (integerValue > maxValue) {
950 addMessage(messages, rule, error, null, propertyNames);
951 }
952 }
953 }
954 break;
955
956 case IS_AMONG:
957 if (rule.getAllowedValues() != null) {
958
959
960 final List<Integer> integerValues = new ArrayList<>();
961
962
963 final String[] stringValues = rule.getAllowedValues().split(configuration.getValueSeparator());
964 for (final String stringValue : stringValues) {
965 integerValues.add(Integer.parseInt(stringValue));
966 }
967
968
969 if (!integerValues.contains(integerValue)) {
970 addMessage(messages, rule, error, null, propertyNames);
971 }
972 }
973 break;
974
975 default:
976
977 break;
978 }
979
980 }
981
982
983
984
985
986
987
988
989
990
991 private void validDouble(Double doubleValue, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, String... propertyNames) {
992 validDouble(doubleValue, rule, messages, error, null, propertyNames);
993 }
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005 private void validDouble(Double doubleValue, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, Integer pmfmId, String... propertyNames) {
1006
1007 switch (ControlFunctionValues.getFunctionValue(rule.getFunction().getId())) {
1008
1009 case IS_EMPTY:
1010
1011 if (doubleValue != null) {
1012 addMessage(messages, rule, error, pmfmId, propertyNames);
1013 }
1014 break;
1015
1016 case NOT_EMPTY:
1017
1018 if (doubleValue == null) {
1019 addMessage(messages, rule, error, pmfmId, propertyNames);
1020 }
1021 break;
1022
1023 case MIN_MAX:
1024 if (doubleValue == null) {
1025 addMessage(messages, rule, error, pmfmId, propertyNames);
1026 } else {
1027
1028
1029 Double minValue = null;
1030 if (rule.getMin() != null) {
1031 minValue = (Double) rule.getMin();
1032 }
1033
1034
1035 Double maxValue = null;
1036 if (rule.getMax() != null) {
1037 maxValue = (Double) rule.getMax();
1038 }
1039
1040
1041 if (minValue != null && maxValue != null) {
1042 if (doubleValue < minValue || doubleValue > maxValue) {
1043 addMessage(messages, rule, error, pmfmId, propertyNames);
1044 }
1045 } else if (minValue != null) {
1046 if (doubleValue < minValue) {
1047 addMessage(messages, rule, error, pmfmId, propertyNames);
1048 }
1049 } else if (maxValue != null) {
1050 if (doubleValue > maxValue) {
1051 addMessage(messages, rule, error, pmfmId, propertyNames);
1052 }
1053 }
1054 }
1055 break;
1056
1057 case IS_AMONG:
1058 if (rule.getAllowedValues() != null) {
1059
1060
1061 final List<Double> doubleValues = new ArrayList<>();
1062
1063
1064 final String[] stringValues = rule.getAllowedValues().split(configuration.getValueSeparator());
1065 for (final String stringValue : stringValues) {
1066 try {
1067 doubleValues.add(Double.parseDouble(stringValue));
1068 } catch (NumberFormatException nfe) {
1069 if (LOG.isErrorEnabled()) {
1070 LOG.error(String.format("this value '%s' can't be cast as Double", stringValue));
1071 }
1072 }
1073 }
1074
1075
1076 if (!doubleValues.contains(doubleValue)) {
1077 if (LOG.isDebugEnabled()) {
1078 LOG.debug(String.format("the double value %s is not in allowed values %s", doubleValue, doubleValues));
1079 }
1080
1081 addMessage(messages, rule, error, pmfmId, propertyNames);
1082 }
1083 }
1084 break;
1085
1086 default:
1087
1088 break;
1089 }
1090 }
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102 private void validBigDecimal(BigDecimal bigDecimalValue, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, Integer pmfmId, String... propertyNames) {
1103
1104 Double doubleValue = bigDecimalValue == null ? null : bigDecimalValue.doubleValue();
1105 validDouble(doubleValue, rule, messages, error, pmfmId, propertyNames);
1106 }
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117 private void validString(String stringValue, ControlRuleDTO rule, ControlRuleMessagesBean messages, ErrorDTO error, String... propertyNames) {
1118
1119 switch (ControlFunctionValues.getFunctionValue(rule.getFunction().getId())) {
1120
1121 case IS_EMPTY:
1122
1123 if (StringUtils.isNotBlank(stringValue)) {
1124 addMessage(messages, rule, error, null, propertyNames);
1125 }
1126 break;
1127
1128 case NOT_EMPTY:
1129
1130 if (StringUtils.isBlank(stringValue)) {
1131 addMessage(messages, rule, error, null, propertyNames);
1132 }
1133 break;
1134
1135 case IS_AMONG:
1136 if (rule.getAllowedValues() != null) {
1137
1138 final List<String> stringValues = new ArrayList<>();
1139
1140
1141 final String[] stringTabValues = rule.getAllowedValues().split(configuration.getValueSeparator());
1142 Collections.addAll(stringValues, stringTabValues);
1143
1144
1145 if (!stringValues.contains(stringValue)) {
1146 addMessage(messages, rule, error, null, propertyNames);
1147 }
1148 }
1149 break;
1150
1151 default:
1152
1153 break;
1154 }
1155 }
1156
1157
1158
1159
1160
1161
1162 private void addMessage(ControlRuleMessagesBean messages, ControlRuleDTO rule, ErrorDTO error, Integer pmfmId, String... propertyNames) {
1163 error.setPropertyName(Arrays.asList(propertyNames));
1164 error.setPmfmId(pmfmId);
1165 error.setMessage(getMessage(rule));
1166 if (rule.isBlocking()) {
1167 error.setError(true);
1168 messages.addErrorMessage(error.getMessage());
1169 } else {
1170 error.setWarning(true);
1171 messages.addWarningMessage(error.getMessage());
1172 }
1173 }
1174
1175 private String getMessage(ControlRuleDTO rule) {
1176 if (StringUtils.isNotBlank(rule.getMessage())) {
1177 return rule.getMessage();
1178 }
1179
1180
1181 return t("reefdb.service.controlRule.invalid.message", rule.getCode());
1182 }
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193 private List<ControlRuleDTO> getRules(SurveyDTO survey, final ControlElementValues elementControl) {
1194 return ReefDbBeans.filterCollection(getRulesForSurvey(survey),
1195 (Predicate<ControlRuleDTO>) controlRule -> controlRule != null && elementControl.equals(controlRule.getControlElement()));
1196 }
1197
1198
1199
1200
1201 private List<ControlRuleDTO> getRulesForSurvey(SurveyDTO survey) {
1202 Assert.notNull(survey);
1203 Assert.notNull(survey.getDate());
1204 Assert.notNull(survey.getProgram());
1205 Assert.notNull(survey.getRecorderDepartment());
1206
1207 return rulesBySurveyMap.computeIfAbsent(survey,
1208 rules -> ruleDao.findActiveControlRules(
1209 Dates.convertToDate(survey.getDate(), configuration.getDbTimezone()),
1210 survey.getProgram().getCode(),
1211 survey.getRecorderDepartment().getId()));
1212 }
1213
1214 private ErrorDTO newControlError(ControlElementValues controlElementValue) {
1215 ErrorDTO error = ReefDbBeanFactory.newErrorDTO();
1216 error.setWarning(false);
1217 error.setError(false);
1218 error.setControl(true);
1219 error.setControlElementCode(controlElementValue.getCode());
1220 return error;
1221 }
1222
1223 private boolean isPmfmFoundInRule(PmfmDTO pmfm, ControlRuleDTO rule) {
1224
1225 if (rule.isRulePmfmsEmpty()) {
1226
1227 return true;
1228 }
1229
1230 for (RulePmfmDTO rulePmfm : rule.getRulePmfms()) {
1231
1232
1233 if (rulePmfm.getPmfm().getParameter().equals(pmfm.getParameter())
1234 && (rulePmfm.getPmfm().getMatrix() == null || rulePmfm.getPmfm().getMatrix().equals(pmfm.getMatrix()))
1235 && (rulePmfm.getPmfm().getFraction() == null || rulePmfm.getPmfm().getFraction().equals(pmfm.getFraction()))
1236 && (rulePmfm.getPmfm().getMethod() == null || rulePmfm.getPmfm().getMethod().equals(pmfm.getMethod()))
1237 && (rulePmfm.getPmfm().getUnit() == null || rulePmfm.getPmfm().getUnit().equals(pmfm.getUnit()))
1238 ) {
1239 return true;
1240 }
1241 }
1242
1243 return false;
1244 }
1245
1246 private void updateSurveysControlDate(Collection<Integer> validControlledElementsPks, Collection<Integer> invalidControlledElementsPks, Date controlDate) {
1247
1248 if (CollectionUtils.isNotEmpty(validControlledElementsPks)) {
1249 surveyDao.updateSurveysControlDate(validControlledElementsPks, controlDate);
1250 }
1251
1252 if (CollectionUtils.isNotEmpty(invalidControlledElementsPks)) {
1253 surveyDao.updateSurveysControlDate(invalidControlledElementsPks, null);
1254 }
1255
1256 }
1257
1258 private void updateSamplingOperationsControlDate(Collection<Integer> validControlledElementsPks, Collection<Integer> invalidControlledElementsPks, Date controlDate) {
1259
1260 if (CollectionUtils.isNotEmpty(validControlledElementsPks)) {
1261 operationDao.updateSamplingOperationsControlDate(validControlledElementsPks, controlDate);
1262 }
1263
1264 if (CollectionUtils.isNotEmpty(invalidControlledElementsPks)) {
1265 operationDao.updateSamplingOperationsControlDate(invalidControlledElementsPks, null);
1266 }
1267
1268 }
1269
1270 private void updateMeasurementsControlDate(Collection<Integer> validControlledElementsPks, Collection<Integer> invalidControlledElementsPks, Date controlDate) {
1271
1272 if (CollectionUtils.isNotEmpty(validControlledElementsPks)) {
1273 measurementDao.updateMeasurementsControlDate(validControlledElementsPks, controlDate);
1274 }
1275
1276 if (CollectionUtils.isNotEmpty(invalidControlledElementsPks)) {
1277 measurementDao.updateMeasurementsControlDate(invalidControlledElementsPks, null);
1278 }
1279
1280 }
1281
1282 private void updateTaxonMeasurementsControlDate(Collection<Integer> validControlledElementsPks, Collection<Integer> invalidControlledElementsPks, Date controlDate) {
1283
1284 if (CollectionUtils.isNotEmpty(validControlledElementsPks)) {
1285 measurementDao.updateTaxonMeasurementsControlDate(validControlledElementsPks, controlDate);
1286 }
1287
1288 if (CollectionUtils.isNotEmpty(invalidControlledElementsPks)) {
1289 measurementDao.updateTaxonMeasurementsControlDate(invalidControlledElementsPks, null);
1290 }
1291
1292 }
1293
1294 }