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.Comparator; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Optional; 026import java.util.Set; 027import java.util.TreeMap; 028import java.util.TreeSet; 029import java.util.function.Function; 030import java.util.stream.Collectors; 031 032import javax.xml.transform.Result; 033import javax.xml.transform.TransformerFactory; 034import javax.xml.transform.sax.SAXTransformerFactory; 035import javax.xml.transform.sax.TransformerHandler; 036import javax.xml.transform.stream.StreamResult; 037 038import org.apache.avalon.framework.service.ServiceException; 039import org.apache.avalon.framework.service.ServiceManager; 040import org.apache.cocoon.xml.AttributesImpl; 041import org.apache.cocoon.xml.XMLUtils; 042import org.apache.commons.io.FileUtils; 043import org.xml.sax.SAXException; 044 045import org.ametys.cms.repository.Content; 046import org.ametys.odf.ProgramItem; 047import org.ametys.odf.orgunit.OrgUnit; 048import org.ametys.odf.program.Container; 049import org.ametys.odf.program.Program; 050import org.ametys.plugins.odfpilotage.cost.CostComputationComponent; 051import org.ametys.plugins.odfpilotage.cost.entity.CostComputationData; 052import org.ametys.plugins.odfpilotage.cost.entity.Effectives; 053import org.ametys.plugins.odfpilotage.cost.entity.EqTD; 054import org.ametys.plugins.odfpilotage.cost.entity.ProgramItemData; 055import org.ametys.plugins.odfpilotage.cost.entity.VolumesOfHours; 056import org.ametys.plugins.repository.AmetysRepositoryException; 057import org.ametys.runtime.i18n.I18nizableText; 058import org.ametys.runtime.i18n.I18nizableTextParameter; 059import org.ametys.runtime.model.ModelItem; 060 061/** 062 * Pilotage report for cost modeling synthesis 063 */ 064public class SyntheseReport extends AbstractReport 065{ 066 /** CalculerEffectifComponent */ 067 protected CostComputationComponent _costComputationComponent; 068 069 @Override 070 public void service(ServiceManager manager) throws ServiceException 071 { 072 super.service(manager); 073 _costComputationComponent = (CostComputationComponent) manager.lookup(CostComputationComponent.ROLE); 074 } 075 076 @Override 077 protected String getType() 078 { 079 return "synthese"; 080 } 081 082 @Override 083 protected Set<String> getSupportedOutputFormats() 084 { 085 return Set.of(OUTPUT_FORMAT_XLS); 086 } 087 088 @Override 089 protected void _launchByOrgUnit(String uaiCode, String catalog, String lang) throws Exception 090 { 091 OrgUnit orgUnit = _odfHelper.getOrgUnitByUAICode(uaiCode); 092 CostComputationData costData = _costComputationComponent.computeCostsOnOrgUnit(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 _reportHelper.saxNaturesEnseignement(handler, getLogger()); 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 lines content of the report 150 * @param handler the transformer handler 151 * @param costData informations about the capacity 152 * @param orgUnit the orgUnit 153 * @throws SAXException to handle XMLUtils exceptions 154 */ 155 private void _writeLines(TransformerHandler handler, CostComputationData costData, OrgUnit orgUnit) throws SAXException 156 { 157 XMLUtils.startElement(handler, "lines"); 158 Map<Program, Set<Container>> stepsByProgram = _getStepsByProgram(costData); 159 for (Entry<Program, Set<Container>> entry : stepsByProgram.entrySet()) 160 { 161 for (Container step : entry.getValue()) 162 { 163 _writeStepLine(handler, costData, entry.getKey(), step); 164 } 165 _writeProgramLine(handler, costData, entry.getKey()); 166 } 167 _writeOrgUnitLine(handler, costData, orgUnit); 168 169 XMLUtils.endElement(handler, "lines"); 170 } 171 172 private Map<Program, Set<Container>> _getStepsByProgram(CostComputationData costData) 173 { 174 Comparator<Content> titleComparator = new TitleComparator(); 175 String yearId = _pilotageHelper.getYearId().orElse(null); 176 177 Map<Program, Set<Container>> stepsByProgram = new TreeMap<>(titleComparator); 178 179 if (yearId == null) 180 { 181 stepsByProgram.putAll( 182 costData.getComputedPrograms() 183 .stream() 184 .collect(Collectors.toMap(Function.identity(), __ -> Set.<Container>of())) 185 ); 186 } 187 else 188 { 189 for (Program program : costData.getComputedPrograms()) 190 { 191 Set<Container> steps = new TreeSet<>(titleComparator); 192 steps.addAll(_getChildrendStepsForProgramItem(program, yearId)); 193 stepsByProgram.put(program, steps); 194 } 195 } 196 197 return stepsByProgram; 198 } 199 200 private Set<Container> _getChildrendStepsForProgramItem(ProgramItem programItem, String yearId) 201 { 202 Set<Container> containers = new HashSet<>(); 203 204 // Search if the current element is a container and is of type year 205 if (programItem instanceof Container) 206 { 207 Container container = (Container) programItem; 208 if (yearId.equals(container.getNature())) 209 { 210 containers.add(container); 211 } 212 } 213 214 // In all other cases, search in the parent elements 215 if (containers.isEmpty()) 216 { 217 for (ProgramItem child : _odfHelper.getChildProgramItems(programItem)) 218 { 219 containers.addAll(_getChildrendStepsForProgramItem(child, yearId)); 220 } 221 } 222 223 return containers; 224 } 225 226 private void _writeStepLine(TransformerHandler handler, CostComputationData costData, Program program, Container step) throws AmetysRepositoryException, SAXException 227 { 228 XMLUtils.startElement(handler, "line"); 229 XMLUtils.createElement(handler, "type", "container"); 230 XMLUtils.createElement(handler, "diplome", ((Content) program).getTitle()); 231 XMLUtils.createElement(handler, "annee", step.getTitle()); 232 _writeValues(handler, costData, step, program.getName() + ModelItem.ITEM_PATH_SEPARATOR + "(.*" + ModelItem.ITEM_PATH_SEPARATOR + ")*" + step.getName()); 233 XMLUtils.endElement(handler, "line"); 234 } 235 236 private void _writeProgramLine(TransformerHandler handler, CostComputationData costData, Program program) throws AmetysRepositoryException, SAXException 237 { 238 XMLUtils.startElement(handler, "line"); 239 XMLUtils.createElement(handler, "type", "program"); 240 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 241 i18nParams.put("program", new I18nizableText(((Content) program).getTitle())); 242 XMLUtils.createElement(handler, "diplome", _i18nUtils.translate(new I18nizableText("plugin.odf-pilotage", "PLUGINS_ODF_PILOTAGE_SYNTHESIS_TOTAL", i18nParams), program.getLanguage())); 243 _writeValues(handler, costData, program, program.getName()); 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 String orgUnitPath = orgUnit.getName(); 253 _writeValues(handler, costData, orgUnit, orgUnitPath); 254 XMLUtils.endElement(handler, "line"); 255 } 256 257 private void _writeValues(TransformerHandler handler, CostComputationData costData, Content item, String regexPath) throws SAXException 258 { 259 ProgramItemData itemData = Optional.of(item) 260 .map(Content::getId) 261 .map(costData::get) 262 .orElse(null); 263 264 if (itemData != null) 265 { 266 // Volumes of hours 267 VolumesOfHours volumesOfHours = itemData.getVolumesOfHours(); 268 if (volumesOfHours != null) 269 { 270 for (Entry<String, Double> volumeOfHours : volumesOfHours.getVolumes().entrySet()) 271 { 272 AttributesImpl attrs = new AttributesImpl(); 273 attrs.addCDATAAttribute("id", volumeOfHours.getKey()); 274 XMLUtils.createElement(handler, "volume", attrs, String.valueOf(volumeOfHours.getValue())); 275 } 276 XMLUtils.createElement(handler, "total", String.valueOf(volumesOfHours.getTotal())); 277 } 278 279 // EqTD 280 EqTD eqTD = itemData.getEqTD(); 281 if (eqTD != null) 282 { 283 Double localEqTD = 0D; 284 Map<String, Double> localEqTDByPath = eqTD.getLocalEqTD(); 285 for (String eqTDpath : localEqTDByPath.keySet()) 286 { 287 if (eqTDpath.matches(regexPath)) 288 { 289 localEqTD += localEqTDByPath.get(eqTDpath); 290 } 291 } 292 XMLUtils.createElement(handler, "eqtdLocal", String.valueOf(localEqTD)); 293 XMLUtils.createElement(handler, "eqtdProrated", String.valueOf(eqTD.getProratedEqTD(""))); 294 } 295 296 // Effectives 297 Effectives effectives = itemData.getEffectives(); 298 if (effectives != null) 299 { 300 XMLUtils.createElement(handler, "effectif", String.valueOf(effectives.getComputedEffective().longValue())); 301 } 302 303 // Rapport H/E 304 XMLUtils.createElement(handler, "heRatio", String.valueOf(itemData.getHeRatio())); 305 } 306 } 307 308 /** 309 * Compare two contents by their title 310 */ 311 public class TitleComparator implements Comparator<Content> 312 { 313 public int compare(Content c1, Content c2) 314 { 315 return c1.getTitle().compareTo(c2.getTitle()); 316 } 317 } 318}