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