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.List;
019import java.util.Optional;
020import java.util.Set;
021
022import org.apache.avalon.framework.component.Component;
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.avalon.framework.service.Serviceable;
026import org.apache.commons.lang3.StringUtils;
027
028import org.ametys.cms.repository.Content;
029import org.ametys.cms.repository.ContentQueryHelper;
030import org.ametys.cms.repository.ContentTypeExpression;
031import org.ametys.cms.repository.LanguageExpression;
032import org.ametys.odf.ProgramItem;
033import org.ametys.odf.enumeration.OdfReferenceTableEntry;
034import org.ametys.odf.orgunit.OrgUnit;
035import org.ametys.odf.ose.db.ParameterizableQuery;
036import org.ametys.odf.ose.export.OSEConstants;
037import org.ametys.odf.ose.export.impl.odf.db.EtapeHelper;
038import org.ametys.odf.ose.export.utils.ElementRetriever;
039import org.ametys.odf.program.Container;
040import org.ametys.odf.program.ContainerFactory;
041import org.ametys.plugins.odfpilotage.helper.PilotageHelper;
042import org.ametys.plugins.repository.AmetysObjectIterable;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.plugins.repository.EmptyIterable;
045import org.ametys.plugins.repository.query.expression.AndExpression;
046import org.ametys.plugins.repository.query.expression.BooleanExpression;
047import org.ametys.plugins.repository.query.expression.Expression;
048import org.ametys.plugins.repository.query.expression.Expression.Operator;
049import org.ametys.plugins.repository.query.expression.NotExpression;
050import org.ametys.plugins.repository.query.expression.StringExpression;
051import org.ametys.runtime.config.Config;
052import org.ametys.runtime.model.ModelHelper;
053import org.ametys.runtime.plugin.component.AbstractLogEnabled;
054
055/**
056 * Exporter of steps
057 */
058public class StepExporter extends AbstractLogEnabled implements Component, Serviceable
059{
060    /** Avalon Role */
061    public static final String ROLE = StepExporter.class.getName();
062    
063    /** The Ametys object resolver */
064    protected AmetysObjectResolver _resolver;
065    /** The retriever of elements from ODF */
066    protected ElementRetriever _elementRetriever;
067    /** The pilotage helper */
068    protected PilotageHelper _pilotageHelper;
069    
070    @Override
071    public void service(ServiceManager manager) throws ServiceException
072    {
073        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
074        _elementRetriever = (ElementRetriever) manager.lookup(ElementRetriever.ROLE);
075        _pilotageHelper = (PilotageHelper) manager.lookup(PilotageHelper.ROLE);
076    }
077    
078    /**
079     * Get all the {@link Container} that are steps (nature is "annee") and are exportable (attribute "noOseExport" is not <code>true</code>)
080     * @param ametysCatalog The Ametys catalog
081     * @return The list of exportable steps
082     */
083    public AmetysObjectIterable<Container> getStepsToExport(String ametysCatalog)
084    {
085        // Add values for steps (container with nature equals to year)
086        return _pilotageHelper.getYearId()
087            .map(anneeId -> _matchingStepsExpression(anneeId, ametysCatalog))
088            .map(ContentQueryHelper::getContentXPathQuery)
089            .map(_resolver::<Container>query)
090            .orElseGet(EmptyIterable::new);
091    }
092    
093    private Expression _matchingStepsExpression(String anneeId, String ametysCatalog)
094    {
095        Expression contentTypeExpr = new ContentTypeExpression(Operator.EQ, ContainerFactory.CONTAINER_CONTENT_TYPE);
096        String lang = Config.getInstance().getValue("odf.programs.lang");
097        Expression langExpr = new LanguageExpression(Operator.EQ, lang);
098        Expression catalogExpr = new StringExpression(ProgramItem.CATALOG, Operator.EQ, ametysCatalog);
099        Expression natureExpr = new StringExpression(Container.NATURE, Operator.EQ, anneeId);
100        Expression doExportExpression = new NotExpression(new BooleanExpression(OSEConstants.NO_OSE_EXPORT_ATTRIBUTE_NAME, true));
101        
102        return new AndExpression(contentTypeExpr, langExpr, catalogExpr, natureExpr, doExportExpression);
103    }
104
105    /**
106     * Get queries to export the given step.
107     * @param step The step to export
108     * @param oseCatalog The OSE catalog
109     * @return A {@link List} of {@link ParameterizableQuery} to export the {@link Container}, it can be empty if there is a problem (see logs)
110     */
111    public List<ParameterizableQuery> getQueries(Container step, Long oseCatalog)
112    {
113        // Degree : Remonter aux formations parentes et prendre les degrees, s'il n'y en a qu'une prendre son code, sinon erreur
114        Set<OdfReferenceTableEntry> degrees = _elementRetriever.retrieveDegree(step);
115        
116        // Structure : Remonter parent par parent pour obtenir le ou les structures rattachées (sur abstractProgram)
117        Set<OrgUnit> orgUnits = _elementRetriever.retrieveOrgUnits(step);
118        
119        if (degrees.size() != 1 || orgUnits.size() != 1)
120        {
121            LogUtils.stepImpossibilityDegreesOrOrgunits(getLogger(), step, degrees, orgUnits);
122            return List.of();
123        }
124        
125        OdfReferenceTableEntry degree = _getFirstDegree(degrees);
126        String degreeCode = degree.getCode();
127        String orgUnitCode = _getFirstOrgUnitCode(orgUnits);
128        Optional<String> functionalDomain = _getFunctionalDomain(degree);
129        
130        if (functionalDomain.isEmpty())
131        {
132            LogUtils.stepImpossibilityFunctDomain(getLogger(), step);
133            return List.of();
134        }
135        
136        return _getQueries(step, oseCatalog, degreeCode, orgUnitCode, functionalDomain.get());
137    }
138    
139    private Optional<String> _getFunctionalDomain(OdfReferenceTableEntry degree)
140    {
141        Expression expr = new AndExpression(
142            new ContentTypeExpression(Operator.EQ, "odf-enumeration.DegreeCategory"),
143            new StringExpression("degrees", Operator.EQ, degree.getId())
144        );
145        
146        String xpathQuery = ContentQueryHelper.getContentXPathQuery(expr);
147        AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery);
148        
149        return contents
150                    .stream()
151                    .findFirst()
152                    .map(c -> c.<String>getValue("functionalDomain"));
153    }
154    
155    private OdfReferenceTableEntry _getFirstDegree(Set<OdfReferenceTableEntry> degrees)
156    {
157        return degrees.stream()
158                .findFirst()
159                .get();
160    }
161    
162    private String _getFirstOrgUnitCode(Set<OrgUnit> orgUnits)
163    {
164        return orgUnits.stream()
165                .findFirst()
166                .map(OrgUnit::getUAICode)
167                .get();
168    }
169    
170    private List<ParameterizableQuery> _getQueries(Container container, Long oseCatalog, String degree, String orgUnit, String functionalDomain)
171    {
172        String title = container.getTitle();
173        if (ModelHelper.hasModelItem("etpCode", container.getModel()))
174        {
175            String apogee = container.getValue("etpCode");
176            if (StringUtils.isNotEmpty(apogee))
177            {
178                title += " (" + apogee + ")";
179            }
180        }
181        String code = container.getCode();
182        
183        return EtapeHelper.insertInto(code, title, oseCatalog, degree, orgUnit, functionalDomain);
184    }
185}
186