001/*
002 *  Copyright 2017 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.plugins.odfsync.apogee.scc.impl;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Optional;
024import java.util.Set;
025
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.commons.lang3.tuple.Pair;
029import org.slf4j.Logger;
030
031import org.ametys.cms.data.ContentSynchronizationResult;
032import org.ametys.cms.repository.Content;
033import org.ametys.cms.repository.ModifiableContent;
034import org.ametys.cms.repository.WorkflowAwareContent;
035import org.ametys.core.user.population.UserPopulationDAO;
036import org.ametys.odf.course.Course;
037import org.ametys.odf.course.ShareableCourseHelper;
038import org.ametys.odf.courselist.CourseList;
039import org.ametys.odf.coursepart.CoursePart;
040import org.ametys.odf.coursepart.CoursePartFactory;
041import org.ametys.odf.enumeration.OdfReferenceTableEntry;
042import org.ametys.odf.enumeration.OdfReferenceTableHelper;
043import org.ametys.plugins.odfsync.apogee.scc.AbstractApogeeSynchronizableContentsWithCatalogCollection;
044import org.ametys.plugins.repository.data.holder.values.SynchronizationResult;
045import org.ametys.runtime.model.View;
046
047import com.opensymphony.workflow.WorkflowException;
048
049/**
050 * SCC for course contents.
051 */
052public class CourseSynchronizableContentsCollection extends AbstractApogeeSynchronizableContentsWithCatalogCollection
053{
054    /** SCC model id */
055    public static final String MODEL_ID = "org.ametys.plugins.odfsync.apogee.scc.course";
056    
057    /** The ODF reference table helper */
058    protected OdfReferenceTableHelper _odfRefTableHelper;
059    
060    /** The shareable course helper */
061    protected ShareableCourseHelper _shareableCourseHelper;
062    
063    @Override
064    public void service(ServiceManager manager) throws ServiceException
065    {
066        super.service(manager);
067        _odfRefTableHelper = (OdfReferenceTableHelper) manager.lookup(OdfReferenceTableHelper.ROLE);
068        _shareableCourseHelper = (ShareableCourseHelper) manager.lookup(ShareableCourseHelper.ROLE);
069    }
070    
071    @Override
072    protected List<Map<String, Object>> _search(Map<String, Object> searchParameters, Logger logger)
073    {
074        return _convertBigDecimal(_apogeeDAO.searchCourses(getDataSourceId(), getParameterValues(), searchParameters));
075    }
076
077    @Override
078    protected String getMappingName()
079    {
080        return "course";
081    }
082    
083    @Override
084    protected Map<String, Object> getAdditionalAttributeValues(String idValue, Content content, Map<String, Object> additionalParameters, boolean create, Logger logger)
085    {
086        Map<String, Object> result = super.getAdditionalAttributeValues(idValue, content, additionalParameters, create, logger);
087        
088        // Handle course parts children
089        List<ModifiableContent> courseParts = importCourseParts(content, logger);
090        if (!courseParts.isEmpty())
091        {
092            result.put(Course.CHILD_COURSE_PARTS, courseParts.toArray(new ModifiableContent[courseParts.size()]));
093        }
094        
095        return result;
096    }
097    
098    @Override
099    protected Pair<String, Object> getParentAttribute(ModifiableContent parent)
100    {
101        if (parent instanceof CourseList)
102        {
103            return Pair.of(Course.PARENT_COURSE_LISTS, new ModifiableContent[] {parent});
104        }
105
106        return super.getParentAttribute(parent);
107    }
108    
109    @Override
110    protected Set<String> getNotSynchronizedRelatedContentIds(Content content, Map<String, Object> contentValues, Map<String, Object> additionalParameters, String lang, Logger logger)
111    {
112        Set<String> contentIds = super.getNotSynchronizedRelatedContentIds(content, contentValues, additionalParameters, lang, logger);
113        
114        if (contentValues.containsKey(Course.CHILD_COURSE_PARTS))
115        {
116            Content[] courseParts = (Content[]) contentValues.get(Course.CHILD_COURSE_PARTS);
117            Arrays.stream(courseParts)
118                .map(Content::getId)
119                .forEach(contentIds::add);
120        }
121        
122        return contentIds;
123    }
124
125    @Override
126    public ContentSynchronizationResult additionalImportOperations(ModifiableContent content, Map<String, Object> additionalParameters, Logger logger)
127    {
128        ContentSynchronizationResult result = super.additionalImportOperations(content, additionalParameters, logger);
129        
130        CourseList parentContent = getParentFromAdditionalParameters(additionalParameters)
131                .map(CourseList.class::cast)
132                .orElse(null);
133        
134        SynchronizationResult additionalResult = new SynchronizationResult();
135        boolean hasChanges = _shareableCourseHelper.initializeShareableFields((Course) content, parentContent, UserPopulationDAO.SYSTEM_USER_IDENTITY, true);
136        additionalResult.setHasChanged(hasChanges);
137        
138        result.aggregateResult(additionalResult);
139        return result;
140    }
141    
142    @Override
143    protected String getChildrenSCCModelId()
144    {
145        return CourseListSynchronizableContentsCollection.MODEL_ID;
146    }
147    
148    @Override
149    protected String getChildrenAttributeName()
150    {
151        return Course.CHILD_COURSE_LISTS;
152    }
153    
154    /**
155     * Creates the course parts of the current {@link Course}.
156     * @param course The current {@link Course}
157     * @param logger The logger
158     * @return The list of the imported course parts
159     */
160    protected List<ModifiableContent> importCourseParts(Content course, Logger logger)
161    {
162        List<ModifiableContent> courseParts = new ArrayList<>();
163        String prefixTitle = course.getTitle() + " - ";
164        
165        Map<String, Object> searchParams = putIdParameter(course.getValue(getIdField()));
166        List<Map<String, Object>> coursePartsData = _apogeeDAO.getCourseParts(getDataSourceId(), getParameterValues(), searchParams);
167        
168        for (Map<String, Object> coursePartData : coursePartsData)
169        {
170            String typeHeure = coursePartData.get("COD_TYP_HEU").toString();
171            String nature = Optional.ofNullable(_odfRefTableHelper.getItemFromCode(OdfReferenceTableHelper.ENSEIGNEMENT_NATURE, typeHeure))
172                .map(OdfReferenceTableEntry::getId)
173                .orElse(null);
174            if (nature == null)
175            {
176                logger.warn("The nature '{}' is unknown for the course '{}'.", typeHeure, course.getTitle());
177            }
178            else
179            {
180                // Create the course part
181                String coursePartTitle = prefixTitle + typeHeure;
182                CoursePart coursePart = (CoursePart) createContentAction(CoursePartFactory.COURSE_PART_CONTENT_TYPE, "course-part", 1, course.getLanguage(), coursePartTitle, logger);
183                if (coursePart != null)
184                {
185                    try
186                    {
187                        Map<String, Object> coursePartValues = new HashMap<>();
188                        coursePartValues.put("nature", nature);
189                        coursePartValues.put("nbHours", coursePartData.get("NBR_HEU_ELP"));
190                        coursePartValues.put("courseHolder", course.getId());
191                        
192                        _editContent(coursePart, Optional.of(View.of(coursePart.getModel(), coursePartValues.keySet().toArray(new String[coursePartValues.size()]))), coursePartValues, Map.of(), true, Set.of(), logger);
193                        courseParts.add(coursePart);
194                    }
195                    catch (WorkflowException e)
196                    {
197                        _nbError++;
198                        logger.warn("An error occurred while synchronizing course part {}", coursePart, e);
199                    }
200                }
201            }
202        }
203        
204        return courseParts;
205    }
206    
207    @Override
208    protected void _addEditInputsForSCC(Map<String, Object> inputs, WorkflowAwareContent content, Logger logger)
209    {
210        if (content instanceof Course)
211        {
212            super._addEditInputsForSCC(inputs, content, logger);
213        }
214    }
215}