View Javadoc
1   package fr.ifremer.reefdb.service.administration.program;
2   
3   /*
4    * #%L
5    * Reef DB :: 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.base.Joiner;
27  import com.google.common.collect.*;
28  import fr.ifremer.quadrige3.core.config.QuadrigeConfiguration;
29  import fr.ifremer.quadrige3.core.dao.administration.program.ProgramDao;
30  import fr.ifremer.quadrige3.core.dao.technical.Assert;
31  import fr.ifremer.quadrige3.core.dao.technical.Dates;
32  import fr.ifremer.quadrige3.core.security.AuthenticationInfo;
33  import fr.ifremer.quadrige3.core.security.SecurityContextHelper;
34  import fr.ifremer.quadrige3.core.service.administration.program.ProgramServiceImpl;
35  import fr.ifremer.quadrige3.core.vo.administration.program.ProgramVO;
36  import fr.ifremer.quadrige3.synchro.service.client.SynchroRestClientService;
37  import fr.ifremer.reefdb.config.ReefDbConfiguration;
38  import fr.ifremer.reefdb.dao.administration.program.ReefDbProgramDao;
39  import fr.ifremer.reefdb.dao.administration.strategy.ReefDbStrategyDao;
40  import fr.ifremer.reefdb.dao.referential.monitoringLocation.ReefDbMonitoringLocationDao;
41  import fr.ifremer.reefdb.dao.system.filter.ReefDbFilterDao;
42  import fr.ifremer.reefdb.dto.ReefDbBeanFactory;
43  import fr.ifremer.reefdb.dto.ReefDbBeans;
44  import fr.ifremer.reefdb.dto.configuration.filter.FilterDTO;
45  import fr.ifremer.reefdb.dto.configuration.programStrategy.*;
46  import fr.ifremer.reefdb.dto.data.survey.SurveyDTO;
47  import fr.ifremer.reefdb.dto.enums.ExtractionFilterValues;
48  import fr.ifremer.reefdb.dto.enums.FilterTypeValues;
49  import fr.ifremer.reefdb.dto.referential.DepartmentDTO;
50  import fr.ifremer.reefdb.dto.referential.LocationDTO;
51  import fr.ifremer.reefdb.dto.referential.pmfm.PmfmDTO;
52  import fr.ifremer.reefdb.service.ReefDbTechnicalException;
53  import fr.ifremer.reefdb.service.StatusFilter;
54  import fr.ifremer.reefdb.service.administration.user.UserService;
55  import org.apache.commons.collections4.CollectionUtils;
56  import org.apache.commons.lang3.StringUtils;
57  import org.apache.commons.logging.Log;
58  import org.apache.commons.logging.LogFactory;
59  import org.springframework.stereotype.Service;
60  
61  import javax.annotation.Resource;
62  import java.util.*;
63  import java.util.function.Predicate;
64  import java.util.stream.Collectors;
65  
66  /**
67   * Service need for programs and strategy.
68   */
69  @Service("reefdbProgramStrategyService")
70  public class ProgramStrategyServiceImpl
71          extends ProgramServiceImpl
72          implements ProgramStrategyService {
73  
74      /* Logger */
75      private static final Log LOG = LogFactory.getLog(ProgramStrategyServiceImpl.class);
76      private static final Predicate<PmfmStrategyDTO> PMFM_FOR_SURVEY_PREDICATE = object -> object.isSurvey() && !object.isGrouping();
77      private static final Predicate<PmfmStrategyDTO> GROUPED_PMFM_FOR_SURVEY_PREDICATE = object -> object.isSurvey() && object.isGrouping();
78      private static final Predicate<PmfmStrategyDTO> PMFM_FOR_SAMPLING_OPERATION_PREDICATE = object -> object.isSampling() && !object.isGrouping();
79      private static final Predicate<PmfmStrategyDTO> GROUPED_PMFM_FOR_SAMPLING_OPERATION_PREDICATE = object -> object.isSampling() && object.isGrouping();
80      @Resource(name = "reefDbProgramDao")
81      protected ReefDbProgramDao programDao;
82      @Resource(name = "reefDbStrategyDao")
83      protected ReefDbStrategyDao strategyDao;
84      @Resource(name = "reefDbMonitoringLocationDao")
85      protected ReefDbMonitoringLocationDao locationDao;
86      @Resource(name = "reefDbUserService")
87      protected UserService userService;
88      @Resource(name = "synchroRestClientService")
89      protected SynchroRestClientService synchroRestClientService;
90      @Resource(name = "reefDbFilterDao")
91      protected ReefDbFilterDao filterDao;
92      @Resource
93      protected ReefDbConfiguration config;
94  
95      @Override
96      public List<ProgramDTO> getProgramsByCodes(List<String> programCodes) {
97          Assert.notEmpty(programCodes);
98          List<ProgramDTO> programs = programDao.getProgramsByCodes(programCodes);
99          programs.forEach(program -> program.getErrors().clear());
100         return programs;
101     }
102 
103     /**
104      * {@inheritDoc}
105      */
106     @Override
107     public List<ProgramDTO> searchPrograms(String programCode, String programName, StatusFilter statusFilter) {
108         return filterProgramDTOsByConfig(
109                 programDao.findProgramsByCodeAndName(statusFilter.toStatusCodes(), programCode, programName)
110         );
111     }
112 
113     /**
114      * {@inheritDoc}
115      */
116     @Override
117     public List<ProgramDTO> getProgramsByStatus(StatusFilter statusFilter) {
118         return searchPrograms(null, null, statusFilter);
119     }
120 
121     /**
122      * {@inheritDoc}
123      */
124     @Override
125     public List<ProgramDTO> getWritableProgramsByUserAndStatus(int userId, StatusFilter statusFilter) {
126         Assert.notNull(statusFilter);
127 
128         List<ProgramDTO> programsByStatus = programDao.findProgramsByCodeAndName(statusFilter.toStatusCodes(), null, null);
129         return filterWritablePrograms(userId, programsByStatus);
130     }
131 
132     @Override
133     public List<ProgramDTO> getWritableProgramsByCampaignId(Integer campaignId) {
134         return filterWritablePrograms(programDao.getProgramsByCampaignId(campaignId));
135     }
136 
137     @Override
138     public List<ProgramDTO> getWritablePrograms() {
139         // Return programs with write rights for current user
140         return filterWritablePrograms(programDao.getAllPrograms());
141     }
142 
143     @Override
144     public List<ProgramDTO> getWritableProgramsByLocationAndDate(Integer locationId, Date date) {
145         return filterWritablePrograms(programDao.findProgramsByLocationAndDate(StatusFilter.ACTIVE.toStatusCodes(), locationId, date));
146     }
147 
148     @Override
149     public ProgramDTO getWritableProgramByCode(String programCode) {
150         return filterWritableProgram(programDao.getProgramByCode(programCode));
151     }
152 
153     @Override
154     public List<ProgramDTO> getManagedPrograms() {
155         return getManagedProgramsByUser(SecurityContextHelper.getQuadrigeUserId());
156     }
157 
158     @Override
159     public List<ProgramDTO> getManagedProgramsByUser(int userId) {
160         return filterManagedPrograms(userId, programDao.getAllPrograms());
161     }
162 
163     /**
164      * {@inheritDoc}
165      */
166     @Override
167     public List<ProgramDTO> getManagedProgramsByUserAndStatus(int userId, StatusFilter statusFilter) {
168         Assert.notNull(statusFilter);
169 
170         List<ProgramDTO> programsForStatus = programDao.findProgramsByCodeAndName(statusFilter.toStatusCodes(), null, null);
171         return filterManagedPrograms(userId, programsForStatus);
172     }
173 
174     /**
175      * {@inheritDoc}
176      */
177     @Override
178     public boolean isProgramUsedByRuleList(String programCode) {
179         return programDao.isProgramUsedByRuleList(programCode);
180     }
181 
182     /**
183      * {@inheritDoc}
184      */
185     @Override
186     public boolean isProgramUsedByExtraction(String programCode) {
187 
188         // search the program in all existing extraction filters
189         List<FilterDTO> programFilters = filterDao.getAllExtractionFilters(ExtractionFilterValues.PROGRAM.getFilterTypeId());
190         if (CollectionUtils.isNotEmpty(programFilters)) {
191             for (FilterDTO programFilter : programFilters) {
192                 List<String> programCodes = filterDao.getFilteredElementsByFilterId(programFilter.getId());
193                 if (programCodes.contains(programCode)) {
194                     return true;
195                 }
196             }
197         }
198         return false;
199     }
200 
201     /**
202      * {@inheritDoc}
203      */
204     @Override
205     public boolean isProgramUsedByFilter(String programCode) {
206 
207         // search the program in all existing context filters
208         List<FilterDTO> programFilters = filterDao.getAllContextFilters(null, FilterTypeValues.PROGRAM.getFilterTypeId());
209         if (CollectionUtils.isNotEmpty(programFilters)) {
210             for (FilterDTO programFilter : programFilters) {
211                 List<String> programCodes = filterDao.getFilteredElementsByFilterId(programFilter.getId());
212                 if (programCodes.contains(programCode)) {
213                     return true;
214                 }
215             }
216         }
217         return false;
218     }
219 
220     /**
221      * {@inheritDoc}
222      */
223     @Override
224     public void saveStrategiesByProgramAndLocation(List<ProgStratDTO> strategies, int locationId) {
225 
226         if (CollectionUtils.isEmpty(strategies)) {
227             return;
228         }
229 
230         for (ProgStratDTO strategy : strategies) {
231             strategyDao.saveStrategyByLocation(strategy, locationId);
232         }
233 
234     }
235 
236     /**
237      * {@inheritDoc}
238      */
239     @Override
240     public void loadStrategiesAndLocations(ProgramDTO program) {
241         Assert.notNull(program);
242         Assert.notBlank(program.getCode());
243 
244         // Load strategies
245         if (!program.isStrategiesLoaded()) {
246             List<StrategyDTO> strategies = new ArrayList<>(strategyDao.getStrategiesByProgramCode(program.getCode()));
247             program.setStrategies(strategies);
248             program.setStrategiesLoaded(true);
249 
250             // Load applied strategies
251             if (!program.isStrategiesEmpty()) {
252                 Multimap<Integer, AppliedStrategyDTO> allAppliedPeriods = strategyDao.getAllAppliedPeriodsByProgramCode(program.getCode());
253                 for (StrategyDTO strategy : program.getStrategies()) {
254                     if (strategy.getId() != null && !strategy.isAppliedStrategiesLoaded()) {
255                         Collection<AppliedStrategyDTO> appliedStrategies = allAppliedPeriods.get(strategy.getId());
256                         strategy.setAppliedStrategies(new ArrayList<>(appliedStrategies));
257                         strategy.setAppliedStrategiesLoaded(true);
258                     }
259                 }
260             }
261         }
262 
263         // Load program's location
264         if (!program.isLocationsLoaded()) {
265             List<LocationDTO> locations = locationDao.getLocationsByCampaignAndProgram(null, program.getCode());
266             program.setLocations(new ArrayList<>(locations));
267             program.setLocationsLoaded(true);
268         }
269     }
270 
271     /**
272      * {@inheritDoc}
273      */
274     @Override
275     public void loadPmfmStrategy(StrategyDTO strategy) {
276         Assert.notNull(strategy);
277 
278         if (strategy.getId() == null) {
279             return;
280         }
281 
282         if (!strategy.isPmfmStrategiesLoaded()) {
283             List<PmfmStrategyDTO> pmfms = strategyDao.getPmfmsAppliedStrategy(strategy.getId());
284             strategy.setPmfmStrategies(new ArrayList<>(pmfms));
285             strategy.setPmfmStrategiesLoaded(true);
286         }
287     }
288 
289     /**
290      * {@inheritDoc}
291      */
292     @Override
293     public void savePrograms(List<? extends ProgramDTO> programs) {
294         savePrograms(null, programs);
295     }
296 
297     /**
298      * {@inheritDoc}
299      */
300     @Override
301     public void savePrograms(AuthenticationInfo authenticationInfo, List<? extends ProgramDTO> programs) {
302 
303         if (CollectionUtils.isEmpty(programs)) {
304             return;
305         }
306 
307         Set<String> remoteProgramCodes = Sets.newHashSet();
308         for (ProgramDTO programToSave : programs) {
309 
310             // don't try to save an unmodified program
311             if (!programToSave.isDirty()) {
312                 continue;
313             }
314 
315             // Store remote program codes
316             boolean isRemoteProgram = !ReefDbBeans.isLocalStatus(programToSave.getStatus());
317             if (isRemoteProgram) {
318                 if (authenticationInfo == null) {
319                     throw new ReefDbTechnicalException("Could not save a remote program: authentication is required to connect to synchro server");
320                 }
321 
322                 remoteProgramCodes.add(programToSave.getCode());
323             }
324 
325             // save
326             programDao.saveProgram(programToSave);
327 
328             // reset dirty flag
329             programToSave.setDirty(false);
330             programToSave.setNewCode(false);
331             programToSave.setStrategiesLoaded(false);
332             programToSave.setLocationsLoaded(false);
333         }
334 
335         // Save remote programs, using synchro server
336         if (CollectionUtils.isNotEmpty(remoteProgramCodes)) {
337             saveProgramOnServer(authenticationInfo, remoteProgramCodes);
338         }
339     }
340 
341     /**
342      * {@inheritDoc}
343      */
344     @Override
345     public void loadAppliedPeriods(ProgramDTO program, StrategyDTO strategy) {
346         Assert.notNull(program);
347         Assert.notNull(strategy);
348 
349         List<AppliedStrategyDTO> appliedStrategies;
350         Map<Integer, LocationDTO> missingProgramMonitoringLocationIds = ReefDbBeans.mapById(program.getLocations());
351 
352         // If first load of this strategy
353         if (strategy.getId() != null && !strategy.isAppliedStrategiesLoaded()) {
354             // Load strategy's appliedPeriods
355             appliedStrategies = new ArrayList<>(strategyDao.getAppliedPeriodsByStrategyId(strategy.getId()));
356 
357             // Add missing program's locations
358             for (AppliedStrategyDTO appliedStrategy : appliedStrategies) {
359                 missingProgramMonitoringLocationIds.remove(appliedStrategy.getId());
360             }
361 
362         }
363 
364         // Refresh the strategy (program could have new location)
365         else {
366             appliedStrategies = Lists.newArrayList();
367 
368             // Detect new/removed program's location
369             for (AppliedStrategyDTO appliedStrategy : strategy.getAppliedStrategies()) {
370                 Integer monitoringLocationId = appliedStrategy.getId();
371 
372                 // Only add location that exists in program's locations
373                 if (missingProgramMonitoringLocationIds.containsKey(monitoringLocationId)) {
374                     appliedStrategies.add(appliedStrategy);
375                     missingProgramMonitoringLocationIds.remove(appliedStrategy.getId());
376                 }
377             }
378 
379         }
380 
381         // Add locations that exist in program but not in strategy
382         if (!missingProgramMonitoringLocationIds.isEmpty()) {
383             for (LocationDTO missingLocation : missingProgramMonitoringLocationIds.values()) {
384                 AppliedStrategyDTO newAppliedStrategy = ReefDbBeans.locationToAppliedStrategyDTO(missingLocation);
385                 // applied strategy ID, and dates remain null
386                 appliedStrategies.add(newAppliedStrategy);
387             }
388         }
389 
390         // Apply to the strategy
391         strategy.setAppliedStrategies(appliedStrategies);
392         strategy.setAppliedStrategiesLoaded(true);
393     }
394 
395     /**
396      * {@inheritDoc}
397      */
398     @Override
399     public List<PmfmStrategyDTO> getPmfmStrategiesBySurvey(SurveyDTO survey) {
400 
401         if (survey == null || survey.getProgram() == null || StringUtils.isBlank(survey.getProgram().getCode())
402                 || survey.getLocation() == null || survey.getLocation().getId() == null
403                 || survey.getDate() == null) {
404             return null;
405         }
406 
407         Set<PmfmStrategyDTO> pmfmStrategies = strategyDao.getPmfmStrategiesByProgramCodeAndLocation(
408                 survey.getProgram().getCode(),
409                 survey.getLocation().getId(),
410                 survey.getDate());
411 
412         // Replace qualitative value in pmfm by those restricted by pmfm strategy
413         pmfmStrategies.forEach(pmfmStrategyDTO -> {
414             PmfmDTO pmfmForDataInput = ReefDbBeans.clone(pmfmStrategyDTO.getPmfm());
415             pmfmForDataInput.setQualitativeValues(pmfmStrategyDTO.getQualitativeValues());
416             pmfmStrategyDTO.setPmfm(pmfmForDataInput);
417         });
418 
419         // Sort by rankOrder
420         return pmfmStrategies.stream()
421             .sorted(Comparator.nullsLast(Comparator.comparingInt(PmfmStrategyDTO::getRankOrder)))
422             .collect(Collectors.toList());
423     }
424 
425     /**
426      * {@inheritDoc}
427      */
428     @Override
429     public DepartmentDTO getDepartmentOfAppliedStrategyBySurvey(SurveyDTO survey) {
430 
431         if (survey == null || survey.getProgram() == null || StringUtils.isBlank(survey.getProgram().getCode())
432                 || survey.getLocation() == null || survey.getLocation().getId() == null
433                 || survey.getDate() == null) {
434             return null;
435         }
436 
437         List<ProgStratDTO> appliedStrategies = strategyDao.getStrategiesByProgramCodeAndMonitoringLocationId(survey.getProgram().getCode(), survey.getLocation().getId());
438 
439         for (ProgStratDTO appliedStrategy : appliedStrategies) {
440             // also filter on date
441             if (appliedStrategy.getStartDate() != null && appliedStrategy.getEndDate() != null
442                     && Dates.isBetween(survey.getDate(), appliedStrategy.getStartDate(), appliedStrategy.getEndDate())
443                     && appliedStrategy.getDepartment() != null) {
444 
445                 // return first not null department found
446                 return appliedStrategy.getDepartment();
447             }
448         }
449 
450         return null;
451     }
452 
453     /**
454      * {@inheritDoc}
455      */
456     @Override
457     public Map<Integer, DepartmentDTO> getAnalysisDepartmentsByPmfmMapBySurvey(SurveyDTO survey) {
458 
459         // Get pmfm strategies
460         List<PmfmStrategyDTO> pmfmStrategies = getPmfmStrategiesBySurvey(survey);
461         if (CollectionUtils.isEmpty(pmfmStrategies)) {
462             return null;
463         }
464 
465         Map<Integer, DepartmentDTO> result = Maps.newHashMap();
466 
467         // Extract each analysis department, and store it by pmdm id
468         for (PmfmStrategyDTO pmfmStrategyDTO : pmfmStrategies) {
469             if (pmfmStrategyDTO.getPmfm() != null
470                     && pmfmStrategyDTO.getPmfm().getId() != null
471                     && pmfmStrategyDTO.getAnalysisDepartment() != null) {
472                 Integer pmfmId = pmfmStrategyDTO.getPmfm().getId();
473                 if (!result.containsKey(pmfmId)) {
474                     result.put(pmfmId, pmfmStrategyDTO.getAnalysisDepartment());
475                 }
476             }
477         }
478 
479         return result;
480     }
481 
482     @Override
483     public DepartmentDTO getAnalysisDepartmentOfAppliedStrategyBySurvey(SurveyDTO survey) {
484 
485         return getAnalysisDepartmentOfAppliedStrategyBySurvey(survey, null);
486     }
487 
488     @Override
489     public DepartmentDTO getAnalysisDepartmentOfAppliedStrategyBySurvey(SurveyDTO survey, Collection<PmfmDTO> pmfms) {
490 
491         if (survey == null || survey.getProgram() == null || StringUtils.isBlank(survey.getProgram().getCode())
492                 || survey.getLocation() == null || survey.getLocation().getId() == null
493                 || survey.getDate() == null) {
494             return null;
495         }
496 
497         List<ProgStratDTO> appliedStrategies = strategyDao.getStrategiesByProgramCodeAndMonitoringLocationId(survey.getProgram().getCode(), survey.getLocation().getId());
498 
499         for (ProgStratDTO appliedStrategy : appliedStrategies) {
500             // also filter on date
501             if (appliedStrategy.getStartDate() != null && appliedStrategy.getEndDate() != null
502                     && Dates.isBetween(survey.getDate(), appliedStrategy.getStartDate(), appliedStrategy.getEndDate())) {
503 
504                 DepartmentDTO analysisDepartment = CollectionUtils.isEmpty(pmfms)
505                         ? strategyDao.getAnalysisDepartmentByAppliedStrategyId(appliedStrategy.getAppliedStrategyId())
506                         : strategyDao.getAnalysisDepartmentByAppliedStrategyIdAndPmfmIds(
507                         appliedStrategy.getAppliedStrategyId(),
508                         pmfms.stream().map(PmfmDTO::getId).collect(Collectors.toList()));
509 
510                 // return first not null department found
511                 if (analysisDepartment != null)
512                     return analysisDepartment;
513             }
514         }
515 
516         return null;
517     }
518 
519     /**
520      * {@inheritDoc}
521      */
522     @Override
523     public List<PmfmDTO> getPmfmsForSurvey(SurveyDTO survey) {
524         return getPredicatedPmfmsBySurvey(survey, PMFM_FOR_SURVEY_PREDICATE);
525     }
526 
527     /**
528      * {@inheritDoc}
529      */
530     @Override
531     public List<PmfmDTO> getGroupedPmfmsForSurvey(SurveyDTO survey) {
532         return getPredicatedPmfmsBySurvey(survey, GROUPED_PMFM_FOR_SURVEY_PREDICATE);
533     }
534 
535     /**
536      * {@inheritDoc}
537      */
538     @Override
539     public List<PmfmDTO> getPmfmsForSamplingOperationBySurvey(SurveyDTO survey) {
540         return getPredicatedPmfmsBySurvey(survey, PMFM_FOR_SAMPLING_OPERATION_PREDICATE);
541     }
542 
543     /**
544      * {@inheritDoc}
545      */
546     @Override
547     public List<PmfmDTO> getGroupedPmfmsForSamplingOperationBySurvey(SurveyDTO survey) {
548         return getPredicatedPmfmsBySurvey(survey, GROUPED_PMFM_FOR_SAMPLING_OPERATION_PREDICATE);
549     }
550 
551     private List<PmfmDTO> getPredicatedPmfmsBySurvey(SurveyDTO survey, Predicate<PmfmStrategyDTO> predicate) {
552 
553         Assert.notNull(survey);
554 
555         List<PmfmStrategyDTO> pmfmStrategies = getPmfmStrategiesBySurvey(survey);
556 
557         return CollectionUtils.isNotEmpty(pmfmStrategies)
558                 ? pmfmStrategies.stream().filter(predicate).map(PmfmStrategyDTO::getPmfm).collect(Collectors.toList())
559                 : new ArrayList<>();
560     }
561 
562     /**
563      * {@inheritDoc}
564      */
565     @Override
566     public List<ProgStratDTO> getStrategyUsageByLocationId(Integer locationId) {
567         return strategyDao.getStrategiesByProgramCodeAndMonitoringLocationId(null, locationId);
568     }
569 
570     /**
571      * {@inheritDoc}
572      */
573     @Override
574     public List<ProgStratDTO> getStrategyUsageByProgramAndLocationId(String programCode, Integer locationId) {
575         return strategyDao.getStrategiesByProgramCodeAndMonitoringLocationId(programCode, locationId);
576     }
577 
578     /**
579      * {@inheritDoc}
580      */
581     @Override
582     public void deletePrograms(List<String> programCodes) {
583         Assert.notNull(programCodes);
584         programCodes.forEach(programCode -> programDao.remove(programCode));
585     }
586 
587     /**
588      * {@inheritDoc}
589      */
590     @Override
591     public StrategyDTO duplicateStrategy(final StrategyDTO strategy, ProgramDTO targetProgram, boolean skipLocalReferential) {
592         Assert.notNull(strategy);
593         Assert.notNull(targetProgram);
594 
595         // Strategy duplicated
596         final StrategyDTO duplicateStrategy = ReefDbBeans.clone(strategy);
597         duplicateStrategy.setAppliedStrategies(ReefDbBeans.clone(strategy.getAppliedStrategies()));
598         duplicateStrategy.setPmfmStrategies(ReefDbBeans.clone(strategy.getPmfmStrategies()));
599 
600         // Remove ID and unused properties
601         duplicateStrategy.setId(null);
602         duplicateStrategy.setName(null);
603         duplicateStrategy.setErrors(null);
604 
605         // Add locations to Program
606         if (!duplicateStrategy.isAppliedStrategiesEmpty()) {
607 
608             List<Integer> programLocationIds = ReefDbBeans.collectIds(targetProgram.getLocations());
609             ListIterator<AppliedStrategyDTO> appliedStrategyIterator = duplicateStrategy.getAppliedStrategies().listIterator();
610             while (appliedStrategyIterator.hasNext()) {
611                 AppliedStrategyDTO appliedStrategy = appliedStrategyIterator.next();
612 
613                 if (skipLocalReferential) {
614                     // remove local location
615                     if (ReefDbBeans.isLocalStatus(appliedStrategy.getStatus())) {
616                         appliedStrategyIterator.remove();
617                         continue;
618                     }
619 
620                     // reset local department
621                     if (appliedStrategy.getDepartment() != null && ReefDbBeans.isLocalStatus(appliedStrategy.getDepartment().getStatus())) {
622                         appliedStrategy.setDepartment(null);
623                     }
624 
625                 }
626 
627                 // reset properties
628                 appliedStrategy.setAppliedStrategyId(null);
629                 appliedStrategy.setStartDate(null);
630                 appliedStrategy.setEndDate(null);
631                 appliedStrategy.setPreviousStartDate(null);
632                 appliedStrategy.setPreviousEndDate(null);
633                 appliedStrategy.setDepartment(null);
634 
635                 // add to program location
636                 if (!programLocationIds.contains(appliedStrategy.getId())) {
637                     targetProgram.addLocations(appliedStrategy);
638                 }
639 
640             }
641 
642         }
643 
644         if (!duplicateStrategy.isPmfmStrategiesEmpty()) {
645             ListIterator<PmfmStrategyDTO> pmfmStrategyIterator = duplicateStrategy.getPmfmStrategies().listIterator();
646             while (pmfmStrategyIterator.hasNext()) {
647                 PmfmStrategyDTO pmfmStrategy = pmfmStrategyIterator.next();
648 
649                 if (skipLocalReferential) {
650                     // remove this pmfm strategy if the pmfm is local
651                     if (pmfmStrategy.getPmfm() != null && ReefDbBeans.isLocalStatus(pmfmStrategy.getPmfm().getStatus())) {
652                         pmfmStrategyIterator.remove();
653                         continue;
654                     }
655 
656                     // reset the local department
657                     if (pmfmStrategy.getAnalysisDepartment() != null && ReefDbBeans.isLocalStatus(pmfmStrategy.getAnalysisDepartment().getStatus())) {
658                         pmfmStrategy.setAnalysisDepartment(null);
659                     }
660                 }
661                 pmfmStrategy.setId(null);
662             }
663         }
664 
665         // Return value duplicated
666         return duplicateStrategy;
667     }
668 
669     /**
670      * {@inheritDoc}
671      */
672     @Override
673     public List<ProgramDTO> getRemoteProgramsByUser(AuthenticationInfo authenticationInfo) {
674         List<ProgramVO> programs = synchroRestClientService.getWritableProgramsForUser(authenticationInfo);
675 
676         if (CollectionUtils.isEmpty(programs)) {
677             return null;
678         }
679 
680         // Convert from VO to DTO
681         return programs.stream().map(this::toProgramDTO).collect(Collectors.toList());
682     }
683 
684     /**
685      * {@inheritDoc}
686      */
687     @Override
688     public boolean hasRemoteAccessRightOnProgram(AuthenticationInfo authenticationInfo) {
689         return CollectionUtils.isNotEmpty(getRemoteProgramsByUser(authenticationInfo));
690     }
691 
692     @Override
693     public ProgramDTO isProgramExists(String programCode) {
694         List<ProgramDTO> foundPrograms = programDao.findProgramsByCodeAndName(StatusFilter.ALL.toStatusCodes(), programCode, null);
695         return CollectionUtils.isEmpty(foundPrograms)
696                 ? null
697                 : foundPrograms.get(0);
698     }
699 
700     /* -- Internal methods -- */
701 
702     /**
703      * <p>toProgramDTO.</p>
704      *
705      * @param source a {@link fr.ifremer.quadrige3.core.vo.administration.program.ProgramVO} object.
706      * @return a {@link fr.ifremer.reefdb.dto.configuration.programStrategy.ProgramDTO} object.
707      */
708     private ProgramDTO toProgramDTO(ProgramVO source) {
709         ProgramDTO target = ReefDbBeanFactory.newProgramDTO();
710         target.setCode(source.getProgCd());
711         target.setName(source.getProgNm());
712         return target;
713     }
714 
715     private List<ProgramDTO> toProgramDTOs(Collection<ProgramVO> sources) {
716         return CollectionUtils.isEmpty(sources)
717                 ? null
718                 : sources.stream().map(this::toProgramDTO).collect(Collectors.toList());
719     }
720 
721     /**
722      * Save remote programs, using synchro server
723      *
724      * @param remoteProgramCodes a {@link java.util.Set} object.
725      * @param authenticationInfo a {@link fr.ifremer.quadrige3.core.security.AuthenticationInfo} object.
726      */
727     protected void saveProgramOnServer(AuthenticationInfo authenticationInfo, Set<String> remoteProgramCodes) {
728         // check not empty
729         if (CollectionUtils.isEmpty(remoteProgramCodes)) {
730             return;
731         }
732 
733         if (LOG.isDebugEnabled()) {
734             LOG.debug(String.format("Sending programs [%s] to server", Joiner
735                     .on(',').join(remoteProgramCodes)));
736         }
737 
738         // Convert to VOs
739         List<ProgramVO> programsToSend = ImmutableList.copyOf(remoteProgramCodes).stream()
740                 .map(progCd -> (ProgramVO) programDao.load(ProgramDao.TRANSFORM_PROGRAMVO, progCd))
741                 .collect(Collectors.toList());
742 
743         // Do a remote save
744         List<ProgramVO> savedPrograms = synchroRestClientService.savePrograms(
745                 authenticationInfo,
746                 programsToSend);
747 
748         if (LOG.isDebugEnabled()) {
749             LOG.debug(String.format("Updating programs [%s] from server response", Joiner
750                     .on(',').join(remoteProgramCodes)));
751         }
752         for (ProgramVO savedProgram : savedPrograms) {
753             programDao.save(savedProgram);
754         }
755     }
756 
757     /**
758      * <p>filterProgramDTOsByConfig.</p>
759      *
760      * @param source a {@link java.util.List} object.
761      * @return a {@link java.util.List} object.
762      */
763     @Deprecated
764     protected List<ProgramDTO> filterProgramDTOsByConfig(List<ProgramDTO> source) {
765 
766         final Set<String> allowedProgramCodes = QuadrigeConfiguration.getInstance().getSynchroProgramCodeIncludes();
767 
768         // Do not filter if programs list from config is empty
769         // or if given list is empty
770         if (CollectionUtils.isEmpty(allowedProgramCodes)
771                 || CollectionUtils.isEmpty(source)) {
772             return source;
773         }
774 
775         // Keep programs found in the config list
776         return ReefDbBeans.filterCollection(source, program -> program != null
777                 && program.getCode() != null
778                 && allowedProgramCodes.contains(program.getCode())
779         );
780     }
781 
782     private List<ProgramDTO> filterWritablePrograms(List<ProgramDTO> programs) {
783 
784         return filterWritablePrograms(SecurityContextHelper.getQuadrigeUserId(), programs);
785     }
786 
787     private List<ProgramDTO> filterWritablePrograms(int userId, List<ProgramDTO> programs) {
788 
789         if (CollectionUtils.isEmpty(programs)) {
790             return new ArrayList<>();
791         }
792 
793         // Filter programs with write rights for user (or local program)
794         return programs.stream()
795                 .filter(filterAllowedOrLocal(getWritableProgramCodesByQuserId(userId)))
796                 .collect(Collectors.toList());
797 
798     }
799 
800     private ProgramDTO filterWritableProgram(ProgramDTO program) {
801 
802         final Set<String> allowedProgramCodes = getWritableProgramCodesByQuserId(SecurityContextHelper.getQuadrigeUserId());
803         if (program == null) {
804             return null;
805         }
806         return allowedProgramCodes.contains(program.getCode()) || ReefDbBeans.isLocalStatus(program.getStatus()) ? program : null;
807     }
808 
809     private List<ProgramDTO> filterManagedPrograms(List<ProgramDTO> programs) {
810 
811         return filterManagedPrograms(SecurityContextHelper.getQuadrigeUserId(), programs);
812     }
813 
814     private List<ProgramDTO> filterManagedPrograms(int userId, List<ProgramDTO> programs) {
815 
816         if (CollectionUtils.isEmpty(programs)) {
817             return new ArrayList<>();
818         }
819 
820         // Filter programs with manager rights for user (or local program)
821         return programs.stream()
822                 .filter(filterAllowedOrLocal(getManagedProgramCodesByQuserId(userId)))
823                 .collect(Collectors.toList());
824 
825     }
826 
827     private Predicate<ProgramDTO> filterAllowedOrLocal(Collection<String> allowedProgramCodes) {
828         return program -> program != null && program.getCode() != null && program.getStatus() != null
829                 && (
830                 // This program is allowed
831                 allowedProgramCodes.contains(program.getCode())
832                         ||
833                         // Or it is a local program
834                         ReefDbBeans.isLocalStatus(program.getStatus())
835         );
836     }
837 }