001/*
002 *  Copyright 2019 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.odf.ose.export.impl.odf;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.Optional;
021import java.util.Set;
022import java.util.concurrent.atomic.AtomicInteger;
023
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.apache.commons.lang3.StringUtils;
027
028import org.ametys.odf.course.Course;
029import org.ametys.odf.coursepart.CoursePart;
030import org.ametys.odf.enumeration.OdfReferenceTableHelper;
031import org.ametys.odf.ose.db.ParameterizableQuery;
032import org.ametys.odf.ose.export.impl.odf.db.CheminPedagogiqueHelper;
033import org.ametys.odf.ose.export.impl.odf.db.EffectifsHelper;
034import org.ametys.odf.ose.export.impl.odf.db.ElementPedagogiqueHelper;
035import org.ametys.odf.ose.export.impl.odf.db.TypeInterventionEPHelper;
036import org.ametys.odf.ose.export.impl.odf.db.VolumeHoraireEnsHelper;
037import org.ametys.odf.program.Container;
038import org.ametys.plugins.odfpilotage.cost.entity.CostComputationData;
039import org.ametys.plugins.odfpilotage.cost.entity.Effectives;
040import org.ametys.plugins.odfpilotage.cost.entity.Groups;
041import org.ametys.plugins.odfpilotage.cost.entity.ProgramItemData;
042import org.ametys.runtime.model.ModelHelper;
043
044/**
045 * Exporter of courses.
046 */
047public class CourseExporter extends AbstractProgramElementExporter<Course>
048{
049    /** Avalon Role */
050    public static final String ROLE = CourseExporter.class.getName();
051    
052    /** The ODF enumeration helper */
053    protected OdfReferenceTableHelper _refTableHelper;
054    
055    @Override
056    public void service(ServiceManager manager) throws ServiceException
057    {
058        super.service(manager);
059        _refTableHelper = (OdfReferenceTableHelper) manager.lookup(OdfReferenceTableHelper.ROLE);
060    }
061
062    @Override
063    protected List<ParameterizableQuery> _getQueries(Course programElement, ProgramElementData data, Long oseCatalog, CostComputationData costData)
064    {
065        String courseCode = programElement.getCode();
066        String courseTitle = _getCourseTitle(programElement);
067
068        /*
069         *  ELEMENT_PEDAGOGIQUE
070         *  NOEUD
071         *  Si ELP de dernier niveau (course.getCourseLists().isEmpty())
072         *      Si heures d'enseignement partagées (portées ou non)
073         *          Pour chaque heures d'enseignement uniquement portées
074         *              ELEMENT_PEDAGOGIQUE
075         *              NOEUD
076         *              CHEMIN_PEDAGOGIQUE (1 par STEP)
077         *              EFFECTIFS
078         *              VOLUME_HORAIRE (courseLink = coursePart)
079         *              TYPE_INTERVENTION_EP (courseLink = coursePart)
080         *      Sinon
081         *          CHEMIN_PEDAGOGIQUE (1 par STEP)
082         *          EFFECTIFS (Premier CoursePart)
083         *          Pour chaque heures d'enseignement uniquement portées
084         *              VOLUME_HORAIRE (courseLink = course)
085         *              TYPE_INTERVENTION_EP (courseLink = course)
086         */
087        List<ParameterizableQuery> queries = new ArrayList<>();
088        queries.addAll(ElementPedagogiqueHelper.insertInto(courseCode, courseTitle, oseCatalog, data.getOrgUnit(), data.getStepHolder(), data.getPeriodType()));
089
090        if (programElement.getCourseLists().isEmpty())
091        {
092            // Check if one of the course part is shared
093            List<CoursePart> courseParts = programElement.getCourseParts();
094            boolean hasSharedCoursePart = _hasSharedCoursePart(courseParts);
095            CoursePart[] holdedCourseParts = courseParts.stream()
096                .filter(coursePart -> _isCourseHolder(coursePart, programElement))
097                .toArray(CoursePart[]::new);
098            
099            if (hasSharedCoursePart)
100            {
101                for (CoursePart coursePart : holdedCourseParts)
102                {
103                    String coursePartCode = coursePart.getCode();
104                    String coursePartNature = _refTableHelper.getItemCode(coursePart.getNature());
105                    String coursePartLabel = courseTitle + " - " + coursePartNature;
106                    
107                    queries.addAll(ElementPedagogiqueHelper.insertInto(coursePartCode, coursePartLabel, oseCatalog, data.getOrgUnit(), data.getStepHolder(), data.getPeriodType()));
108                    
109                    queries.addAll(_getCheminPedagogiqueQueries(data.getSteps(), coursePartCode, oseCatalog));
110                    
111                    _getEffectifsQuery(coursePart, coursePartCode, oseCatalog, costData)
112                        .ifPresent(queries::add);
113                    
114                    queries.addAll(_getCoursePartQueries(coursePart, coursePartNature, oseCatalog, coursePart.getCode(), costData));
115                }
116            }
117            else
118            {
119                queries.addAll(_getCheminPedagogiqueQueries(data.getSteps(), courseCode, oseCatalog));
120                if (!courseParts.isEmpty())
121                {
122                    _getEffectifsQuery(courseParts.get(0), courseCode, oseCatalog, costData)
123                        .ifPresent(queries::add);
124                    
125                    for (CoursePart coursePart : holdedCourseParts)
126                    {
127                        String coursePartNature = _refTableHelper.getItemCode(coursePart.getNature());
128                        queries.addAll(_getCoursePartQueries(coursePart, coursePartNature, oseCatalog, courseCode, costData));
129                    }
130                }
131            }
132        }
133        
134        return queries;
135    }
136    
137    private Optional<ParameterizableQuery> _getEffectifsQuery(CoursePart coursePart, String code, Long oseCatalog, CostComputationData costData)
138    {
139        return Optional.ofNullable(coursePart)
140            .map(c -> _getNumberOfStudents(c, costData))
141            .map(nbStudents -> EffectifsHelper.insertInto(coursePart.getCode(), nbStudents, oseCatalog, code));
142    }
143    
144    private List<ParameterizableQuery> _getCheminPedagogiqueQueries(Set<Container> steps, String code, Long oseCatalog)
145    {
146        List<ParameterizableQuery> queries = new ArrayList<>();
147        
148        AtomicInteger order = new AtomicInteger();
149        for (Container step : steps)
150        {
151            queries.add(CheminPedagogiqueHelper.insertInto(step.getCode(), code, oseCatalog, order));
152        }
153        
154        return queries;
155    }
156
157    private List<ParameterizableQuery> _getCoursePartQueries(CoursePart coursePart, String coursePartNature, Long oseCatalog, String courseLink, CostComputationData costData)
158    {
159        String coursePartCode = coursePart.getCode();
160        Double numberOfGroups = _getGroups(coursePart, costData);
161        return List.of(
162            VolumeHoraireEnsHelper.insertInto(coursePart, coursePartCode, coursePartNature, oseCatalog, courseLink, numberOfGroups),
163            TypeInterventionEPHelper.insertInto(coursePartCode, coursePartNature, courseLink, oseCatalog)
164        );
165    }
166
167    private boolean _hasSharedCoursePart(List<CoursePart> courseParts)
168    {
169        return courseParts.stream()
170                .map(CoursePart::getCourses)
171                .map(List::size)
172                .anyMatch(size -> size > 2);
173    }
174    
175    private boolean _isCourseHolder(CoursePart coursePart, Course currentCourse)
176    {
177        final String currentCourseId = currentCourse.getId();
178        return Optional.of(coursePart)
179                .map(CoursePart::getCourseHolder)
180                .map(Course::getId)
181                .map(id -> id.equals(currentCourseId))
182                .orElse(false);
183    }
184    
185    private Long _getNumberOfStudents(CoursePart coursePart, CostComputationData costData)
186    {
187        return Optional.of(coursePart)
188                .map(CoursePart::getId)
189                .map(costData::get)
190                .map(ProgramItemData::getEffectives)
191                .map(Effectives::getComputedEffective)
192                .map(Double::longValue)
193                .orElse(0L);
194    }
195    
196    private Double _getGroups(CoursePart coursePart, CostComputationData costData)
197    {
198        Optional<Groups> groups = Optional.of(coursePart)
199            .map(CoursePart::getId)
200            .map(costData::get)
201            .map(ProgramItemData::getGroups);
202        
203        if (groups.isPresent())
204        {
205            return groups.map(Groups::getGroupsToOpen)
206                    .or(() -> groups.map(Groups::getComputedGroups))
207                    .map(Long::doubleValue)
208                    .orElse(null);
209        }
210        
211        return null;
212    }
213    
214    private String _getCourseTitle(Course course)
215    {
216        String title = course.getTitle();
217        if (ModelHelper.hasModelItem("elpCode", course.getModel()))
218        {
219            String apogee = course.getValue("elpCode");
220            if (StringUtils.isNotEmpty(apogee))
221            {
222                title += " (" + apogee + ")";
223            }
224        }
225        
226        return title;
227    }
228}
229