001/*
002 *  Copyright 2018 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.odfpilotage.report.impl;
017
018import java.io.File;
019import java.io.FileOutputStream;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.Comparator;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Set;
029import java.util.TreeMap;
030
031import javax.xml.transform.Result;
032import javax.xml.transform.TransformerFactory;
033import javax.xml.transform.sax.SAXTransformerFactory;
034import javax.xml.transform.sax.TransformerHandler;
035import javax.xml.transform.stream.StreamResult;
036
037import org.apache.avalon.framework.service.ServiceException;
038import org.apache.avalon.framework.service.ServiceManager;
039import org.apache.cocoon.xml.AttributesImpl;
040import org.apache.cocoon.xml.XMLUtils;
041import org.apache.commons.io.FileUtils;
042import org.xml.sax.SAXException;
043
044import org.ametys.cms.repository.Content;
045import org.ametys.odf.ProgramItem;
046import org.ametys.odf.orgunit.OrgUnit;
047import org.ametys.odf.program.Container;
048import org.ametys.plugins.odfpilotage.cost.CostComputationComponent;
049import org.ametys.plugins.odfpilotage.cost.entity.CostComputationData;
050import org.ametys.plugins.odfpilotage.cost.entity.VolumesOfHours;
051import org.ametys.plugins.odfpilotage.helper.ReportUtils;
052import org.ametys.plugins.repository.AmetysObjectIterable;
053import org.ametys.plugins.repository.AmetysObjectIterator;
054import org.ametys.plugins.repository.AmetysRepositoryException;
055import org.ametys.runtime.i18n.I18nizableText;
056import org.ametys.runtime.i18n.I18nizableTextParameter;
057import org.ametys.runtime.model.ModelItem;
058
059/**
060 * Pilotage report for cost modeling synthesis
061 */
062public class SyntheseReport extends AbstractReport
063{
064    /** CalculerEffectifComponent */
065    protected CostComputationComponent _costComputationComponent;
066    
067    @Override
068    public void service(ServiceManager manager) throws ServiceException
069    {
070        super.service(manager);
071        _costComputationComponent = (CostComputationComponent) manager.lookup(CostComputationComponent.ROLE);
072    }
073    
074    @Override
075    protected String getType()
076    {
077        return "synthese";
078    }
079
080    @Override
081    protected Set<String> getSupportedOutputFormats()
082    {
083        return Set.of(OUTPUT_FORMAT_XLS);
084    }
085    
086    @Override
087    protected void _launchByOrgUnit(String uaiCode, String catalog, String lang) throws Exception
088    {
089        AmetysObjectIterable<OrgUnit> orgUnits = _reportHelper.getRootOrgUnitsByUaiCode(uaiCode);
090        AmetysObjectIterator<OrgUnit> orgUnitsIterator = orgUnits.iterator();
091        OrgUnit orgUnit = orgUnitsIterator.next();
092        CostComputationData costData = _costComputationComponent.computeCostsOnOrgUnits(orgUnit, catalog, lang);
093        
094        _writeSyntheseReport(uaiCode, catalog, lang, orgUnit, costData);
095    }
096    
097    /**
098     * Create the synthese report for one organization unit 
099     * @param uaiCode the reference to the organization unit whose formations are going to be saxed
100     * @param catalog the catalog
101     * @param lang The language code
102     * @param orgUnit the orgUnit
103     * @param costData object containing informations about the formation
104     */
105    private void _writeSyntheseReport(String uaiCode, String catalog, String lang, OrgUnit orgUnit, CostComputationData costData)
106    {
107        SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
108        String fileName = _getReportFileName(catalog, lang, _reportHelper.getAccronymOrUaiCode(uaiCode));
109        
110        // Delete old files
111        File xmlFile = new File(_tmpFolder, fileName + ".xml");
112        FileUtils.deleteQuietly(xmlFile);
113        
114        // Write XML file
115        try (FileOutputStream fos = new FileOutputStream(xmlFile))
116        {
117            TransformerHandler handler = factory.newTransformerHandler();
118            
119            // Prepare the transformation
120            Result result = new StreamResult(fos);
121            handler.setResult(result);
122            handler.startDocument();
123        
124            AttributesImpl attrs = new AttributesImpl();
125            attrs.addCDATAAttribute("type", getType());
126            attrs.addCDATAAttribute("date", _reportHelper.getReadableCurrentDate());
127            XMLUtils.startElement(handler, "report", attrs);
128            
129            _writeColumns(handler, costData);
130            _writeLines(handler, costData, orgUnit);
131            
132            XMLUtils.endElement(handler, "report");
133            handler.endDocument();
134
135            // Convert the report to configured output format
136            convertReport(_tmpFolder, fileName, xmlFile);
137        }
138        catch (Exception e)
139        {
140            getLogger().error("An error occured while generating 'Synthese' report for orgunit '{}'", uaiCode, e);
141        }
142        finally
143        {
144            FileUtils.deleteQuietly(xmlFile);
145        }
146    }
147    
148    /**
149     * Write columns content of the report
150     * @param handler the sax transformer handler
151     * @param costData informations about the capacity 
152     * @throws SAXException to handle XMLUtils exceptions
153     */
154    private void _writeColumns(TransformerHandler handler, CostComputationData costData) throws SAXException
155    {
156        // Columns
157        XMLUtils.startElement(handler, "columns");
158        Set<String> natures = _getNatures(costData);
159        for (String natureId : natures)
160        {
161            _writeColumn(handler, natureId);
162        }
163        
164        XMLUtils.endElement(handler, "columns");
165    }
166    
167    private Set<String> _getNatures(CostComputationData costData)
168    {
169        Map<String, VolumesOfHours> volumesOfHours = costData.getVolumesOfHours();
170        Set<String> natures = new HashSet<>();
171        for (String programItemId : volumesOfHours.keySet())
172        {
173            Map<String, Double> volumes = volumesOfHours.get(programItemId).getVolumes();
174            for (String natureId : volumes.keySet())
175            {
176                natures.add(natureId);
177            }
178        }
179        return natures;
180    }
181    
182    /**
183     * Write columns content of the report
184     * @param handler the sax transformer handler
185     * @param natureId the nature of the volume of hours
186     * @throws SAXException to handle XMLUtils exceptions
187     */
188    protected void _writeColumn(TransformerHandler handler, String natureId) throws SAXException
189    {
190        AttributesImpl attrs = new AttributesImpl();
191        attrs.addCDATAAttribute("id", natureId);
192        XMLUtils.createElement(handler, "column", attrs, natureId);
193    }
194    
195    /**
196     * Write lines content of the report
197     * @param handler the transformer handler
198     * @param costData informations about the capacity
199     * @param orgUnit the orgUnit
200     * @throws SAXException to handle XMLUtils exceptions
201     */
202    private void _writeLines(TransformerHandler handler, CostComputationData costData, OrgUnit orgUnit) throws SAXException
203    {
204        XMLUtils.startElement(handler, "lines");
205        TreeMap<ProgramItem, Map<String, Container>> stepsByProgramItem = new TreeMap<>(new CompareProgramItemTitle());
206        stepsByProgramItem.putAll(costData.getSteps());
207        for (Entry<ProgramItem, Map<String, Container>> entry : stepsByProgramItem.entrySet())
208        {
209            List<Container> steps = new ArrayList<>(entry.getValue().values());
210            Collections.sort(steps, new CompareProgramItemTitle());
211            
212            for (Container step : steps)
213            {
214                _writeStepLine(handler, costData, entry.getKey(), step);
215            }
216            _writeProgramLine(handler, costData, orgUnit, entry.getKey());
217        }
218        _writeOrgUnitLine(handler, costData, orgUnit);
219        
220        XMLUtils.endElement(handler, "lines");
221    }
222    
223    private void _writeStepLine(TransformerHandler handler, CostComputationData costData, ProgramItem program, Container step) throws AmetysRepositoryException, SAXException
224    {
225        XMLUtils.startElement(handler, "line");
226        XMLUtils.createElement(handler, "type", "container");
227        XMLUtils.createElement(handler, "diplome", ((Content) program).getTitle());
228        XMLUtils.createElement(handler, "annee", step.getTitle());
229        String stepPath = _getPath(program, step, costData);
230        _writeValues(handler, costData, step, stepPath);
231        XMLUtils.endElement(handler, "line");
232    }
233    
234    private void _writeProgramLine(TransformerHandler handler, CostComputationData costData, OrgUnit orgUnit, ProgramItem program) throws AmetysRepositoryException, SAXException
235    {
236        XMLUtils.startElement(handler, "line");
237        XMLUtils.createElement(handler, "type", "program");
238        Map<String, I18nizableTextParameter> i18nParams = new HashMap<>();
239        i18nParams.put("program",  new I18nizableText(((Content) program).getTitle()));
240        XMLUtils.createElement(handler, "diplome", _i18nUtils.translate(new I18nizableText("plugin.odf-pilotage", "PLUGINS_ODF_PILOTAGE_SYNTHESIS_TOTAL", i18nParams), orgUnit.getLanguage()));
241        
242        String programPath = orgUnit.getName() + ModelItem.ITEM_PATH_SEPARATOR + program.getName();
243        _writeValues(handler, costData, (Content) program, programPath);
244        XMLUtils.endElement(handler, "line");
245    }
246    
247    private void _writeOrgUnitLine(TransformerHandler handler, CostComputationData costData, OrgUnit orgUnit) throws SAXException
248    {
249        XMLUtils.startElement(handler, "line");
250        XMLUtils.createElement(handler, "type", "orgunit");
251        XMLUtils.createElement(handler, "diplome", _i18nUtils.translate(new I18nizableText("plugin.odf-pilotage", "PLUGINS_ODF_PILOTAGE_SYNTHESIS_GRAND_TOTAL"), orgUnit.getLanguage()));
252        
253        String orgUnitPath = orgUnit.getName();
254        _writeValues(handler, costData, orgUnit, orgUnitPath);
255        XMLUtils.endElement(handler, "line");
256    }
257    
258    private void _writeValues(TransformerHandler handler, CostComputationData costData, Content program, String path) throws SAXException
259    {
260        // Total des volumes horaires
261        XMLUtils.createElement(handler, "total", ReportUtils.FORMAT_2_DIGITS.format(costData.getVolumesOfHours(program.getId()).getTotal()));
262        
263        // EqTD
264        Double proRatedEqTD = costData.getEqTD(program.getId()).getProRatedEqTD(path);
265        Double localEqTD = costData.getEqTD(program.getId()).getLocalEqTD(path);
266        XMLUtils.createElement(handler, "eqtdLocal", ReportUtils.FORMAT_2_DIGITS.format(localEqTD));
267        XMLUtils.createElement(handler, "eqtdproratise", ReportUtils.FORMAT_2_DIGITS.format(proRatedEqTD));
268        
269        //Effectif
270        Double globalEffective = costData.getGlobalComputedEffective(program.getId()).get();
271        XMLUtils.createElement(handler, "effectif", ReportUtils.FORMAT_2_DIGITS.format(globalEffective));
272        
273        Map<String, Double> volumesOfHours = costData.getVolumesOfHours(program.getId()).getVolumes();
274        
275        for (String natureId : volumesOfHours.keySet())
276        {
277            AttributesImpl attrs = new AttributesImpl();
278            attrs.addCDATAAttribute("id", natureId);
279            XMLUtils.createElement(handler, "volume", attrs, ReportUtils.FORMAT_2_DIGITS.format(volumesOfHours.get(natureId)));
280        }
281        
282        // Rapport H/E
283        XMLUtils.createElement(handler, "rapport", ReportUtils.FORMAT_2_DIGITS.format(globalEffective != 0 ? proRatedEqTD / globalEffective : 0));
284    }
285    
286    private String _getPath(ProgramItem programItem, Container step, CostComputationData costData)
287    {
288        Map<String, Container> c = costData.getSteps().get(programItem);
289        return c.entrySet()
290                .stream()
291                .filter(entry -> step.getId().equals(entry.getValue().getId()))
292                .map(Map.Entry::getKey)
293                .findFirst()
294                .orElse(null);
295    }
296    
297    /**
298     * Compare two programItem by their title
299     */
300    public class CompareProgramItemTitle implements Comparator<ProgramItem> 
301    {
302        public int compare(ProgramItem p1, ProgramItem p2)
303        {
304            return ((Content) p1).getTitle().compareTo(((Content) p2).getTitle());
305        }
306    }
307}