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