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}