001/*
002 *  Copyright 2021 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.pegase.scc;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.Map;
021import java.util.Objects;
022import java.util.Optional;
023import java.util.stream.Collectors;
024import java.util.stream.Stream;
025
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.commons.lang.StringUtils;
029import org.slf4j.Logger;
030
031import org.ametys.cms.contenttype.ContentAttributeDefinition;
032import org.ametys.cms.contenttype.ContentType;
033import org.ametys.cms.repository.Content;
034import org.ametys.cms.repository.ContentQueryHelper;
035import org.ametys.cms.repository.ContentTypeExpression;
036import org.ametys.odf.courselist.CourseList.ChoiceType;
037import org.ametys.odf.orgunit.OrgUnitFactory;
038import org.ametys.odf.program.ProgramFactory;
039import org.ametys.odf.program.SubProgramFactory;
040import org.ametys.plugins.odfsync.scc.operator.AbstractODFSynchronizingContentOperator;
041import org.ametys.plugins.odfsync.utils.ContentWorkflowDescription;
042import org.ametys.plugins.repository.AmetysObjectResolver;
043import org.ametys.plugins.repository.query.expression.AndExpression;
044import org.ametys.plugins.repository.query.expression.Expression.Operator;
045import org.ametys.plugins.repository.query.expression.StringExpression;
046
047/**
048 * Pegase synchronizing content operator extending {@link AbstractODFSynchronizingContentOperator} because we keep some mapping mechanisms.
049 */
050public class PegaseSynchronizingContentOperator extends AbstractODFSynchronizingContentOperator
051{
052    private AmetysObjectResolver _resolver;
053
054    @Override
055    public void service(ServiceManager manager) throws ServiceException
056    {
057        super.service(manager);
058        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
059    }
060    
061    @Override
062    protected String getHelperRole()
063    {
064        return "org.ametys.plugins.odfsync.pegase.scc.operator.PegaseSynchronizingContentOperatorHelper";
065    }
066    
067    @Override
068    public Map<String, List<Object>> transform(ContentType obsoleteCType, Map<String, List<Object>> remoteValues, Logger logger)
069    {
070        // Get the right content type from the category field
071        ContentType contentType = Optional.of(remoteValues)
072            .map(v -> v.get("workflowDescription"))
073            .map(l -> l.get(0))
074            .map(ContentWorkflowDescription.class::cast)
075            .map(ContentWorkflowDescription::getContentType)
076            .map(_contentTypeEP::getExtension)
077            .orElse(null);
078        
079        if (contentType == null)
080        {
081            return remoteValues;
082        }
083        
084        _transformSpecificValues(contentType.getId(), remoteValues);
085        
086        return super.transform(contentType, remoteValues, logger);
087    }
088
089    private void _transformSpecificValues(String contentTypeId, Map<String, List<Object>> remoteValues)
090    {
091        switch (contentTypeId)
092        {
093            case ProgramFactory.PROGRAM_CONTENT_TYPE:
094            case SubProgramFactory.SUBPROGRAM_CONTENT_TYPE:
095                _transformSpecificAbstractProgramValues(remoteValues);
096                break;
097            default:
098                // Nothing to do
099        }
100        
101        // Always transform courseList values in case of we have courses not under course list
102        _transformSpecificCourseListValues(remoteValues);
103    }
104    
105    private void _transformSpecificAbstractProgramValues(Map<String, List<Object>> remoteValues)
106    {
107        // Transform ects for AbstractProgram (ECTS value is a double, transform it to String with a Long appearance, 5.0 -> "5")
108        List<Object> ects = Optional.ofNullable(remoteValues.get("ects"))
109                .map(List::stream)
110                .orElseGet(Stream::empty)
111                .filter(Objects::nonNull)
112                .map(Object::toString)
113                .filter(StringUtils::isNotEmpty)
114                .map(Double::valueOf)
115                .map(Double::longValue)
116                .map(String::valueOf)
117                .collect(Collectors.toList());
118        
119        if (ects.isEmpty())
120        {
121            remoteValues.remove("ects");
122        }
123        else
124        {
125            remoteValues.put("ects", ects);
126        }
127    }
128    
129    private void _transformSpecificCourseListValues(Map<String, List<Object>> remoteValues)
130    {
131        // Set the choiceType for CourseList
132        String choiceType =
133            Optional.ofNullable(_getFirstValueAsString(remoteValues.remove("plageDeChoix")))
134                .map(Boolean::parseBoolean)
135                .orElse(false)
136            ? ChoiceType.CHOICE.toString()
137            : (
138                Optional.ofNullable(_getFirstValueAsString(remoteValues.remove("obligatoire")))
139                    .map(Boolean::parseBoolean)
140                    .orElse(false)
141                ? ChoiceType.MANDATORY.toString()
142                : ChoiceType.OPTIONAL.toString()
143            );
144        
145        remoteValues.put("choiceType", List.of(choiceType));
146    }
147    
148    @Override
149    protected List<Object> _transformContentAttributeValues(ContentAttributeDefinition definition, List<Object> values, Logger logger)
150    {
151        if (definition.getName().equals("orgUnit"))
152        {
153            List<Object> result = new ArrayList<>();
154            
155            for (Object value : values)
156            {
157                if (value != null)
158                {
159                    ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, OrgUnitFactory.ORGUNIT_CONTENT_TYPE);
160                    StringExpression valueExpr = new StringExpression("pegaseCode", Operator.EQ, (String) value);
161                    
162                    String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, valueExpr));
163    
164                    String orgUnitId = _resolver.<Content>query(xpathQuery).stream()
165                                                .findFirst()
166                                                .map(Content::getId)
167                                                .orElse(null);
168                    
169                    if (orgUnitId != null)
170                    {
171                        result.add(orgUnitId);
172                    }
173                }
174            }
175
176            return result;
177        }
178        
179        return super._transformContentAttributeValues(definition, values, logger);
180    }
181}