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.plugins.odfpilotage.report.impl; 017 018import java.io.File; 019import java.io.FileOutputStream; 020import java.util.Comparator; 021import java.util.List; 022import java.util.Map; 023import java.util.stream.Collectors; 024 025import javax.xml.transform.Result; 026import javax.xml.transform.TransformerFactory; 027import javax.xml.transform.sax.SAXTransformerFactory; 028import javax.xml.transform.sax.TransformerHandler; 029import javax.xml.transform.stream.StreamResult; 030 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.cocoon.xml.AttributesImpl; 034import org.apache.cocoon.xml.XMLUtils; 035import org.apache.commons.io.FileUtils; 036import org.apache.commons.lang3.StringUtils; 037import org.xml.sax.SAXException; 038 039import org.ametys.cms.FilterNameHelper; 040import org.ametys.odf.program.Program; 041import org.ametys.plugins.odfpilotage.report.consistency.AnalysisExtensionPoint; 042import org.ametys.plugins.odfpilotage.report.consistency.ConsistencyAnalysis; 043import org.ametys.plugins.odfpilotage.report.consistency.ConsistencyAnalysisResult; 044import org.ametys.plugins.odfpilotage.schedulable.AbstractReportSchedulable; 045import org.ametys.plugins.odfpilotage.schedulable.OrgUnitConsistencyExtractSchedulable; 046import org.ametys.plugins.odfpilotage.schedulable.ProgramConsistencyExtractSchedulable; 047import org.ametys.runtime.i18n.I18nizableText; 048 049import com.google.common.collect.ImmutableList; 050 051/** 052 * Class to generate consistency extract as DOC. 053 */ 054public class ConsistencyExtract extends AbstractExtract 055{ 056 /** The key for the analysis */ 057 public static final String PARAMETER_ANALYSIS = "analysis"; 058 059 /** The analysis extension point. */ 060 protected AnalysisExtensionPoint _analysisEP; 061 062 /** The analyses to apply. */ 063 protected List<ConsistencyAnalysis> _analyses; 064 065 @Override 066 public void service(ServiceManager manager) throws ServiceException 067 { 068 _analysisEP = (AnalysisExtensionPoint) manager.lookup(AnalysisExtensionPoint.ROLE); 069 super.service(manager); 070 } 071 072 @Override 073 protected String getType() 074 { 075 return "consistency"; 076 } 077 078 @Override 079 public boolean isGeneric() 080 { 081 return false; 082 } 083 084 @Override 085 protected boolean isCompatibleSchedulable(AbstractReportSchedulable schedulable) 086 { 087 return schedulable instanceof OrgUnitConsistencyExtractSchedulable || schedulable instanceof ProgramConsistencyExtractSchedulable; 088 } 089 090 @Override 091 protected String launchByOrgUnit(Map<String, String> reportParameters) throws Exception 092 { 093 _setAdditionalParameters(reportParameters); 094 return super.launchByOrgUnit(reportParameters); 095 } 096 097 @Override 098 protected String launchByProgram(Map<String, String> reportParameters) throws Exception 099 { 100 _setAdditionalParameters(reportParameters); 101 return super.launchByProgram(reportParameters); 102 } 103 104 /** 105 * Sax the additional parameters. 106 * @param reportParameters The report parameters 107 */ 108 protected void _setAdditionalParameters(Map<String, String> reportParameters) 109 { 110 String analysisId = reportParameters.get(PARAMETER_ANALYSIS); 111 if (StringUtils.isNotEmpty(analysisId)) 112 { 113 _analyses = ImmutableList.of(_analysisEP.getExtension(analysisId)); 114 } 115 else 116 { 117 // All analyses 118 _analyses = _analysisEP.getExtensionsIds() 119 .stream() 120 .map(_analysisEP::getExtension) 121 .sorted(Comparator.comparing(ConsistencyAnalysis::getPriority).thenComparing(ConsistencyAnalysis::getId)) 122 .collect(Collectors.toList()); 123 } 124 } 125 126 @Override 127 protected String _buildZipName(String contextName) 128 { 129 return FilterNameHelper.filterName(getType() + "-" + (_analyses.size() == 1 ? _analyses.get(0).getShortId() : "all") + "-" + getOutputFormat() + "-" + contextName + "-" + _currentFormattedDate) + ".zip"; 130 } 131 132 @Override 133 protected void _saxProgram(Program program) 134 { 135 SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance(); 136 137 String fileName = _getReportFileName(program); 138 139 File xmlFile = new File(_tmpFolder, fileName + ".xml"); 140 FileUtils.deleteQuietly(xmlFile); 141 142 try (FileOutputStream fos = new FileOutputStream(xmlFile)) 143 { 144 TransformerHandler handler = factory.newTransformerHandler(); 145 Result result = new StreamResult(fos); 146 handler.setResult(result); 147 148 handler.startDocument(); 149 150 AttributesImpl attrs = new AttributesImpl(); 151 attrs.addCDATAAttribute("xmlns:i18n", "http://apache.org/cocoon/i18n/2.1"); 152 attrs.addCDATAAttribute("type", getType()); 153 if (_analyses.size() == 1) 154 { 155 attrs.addCDATAAttribute("analysis", _analyses.get(0).getShortId()); 156 } 157 attrs.addCDATAAttribute("date", _reportHelper.getReadableCurrentDate()); 158 159 XMLUtils.startElement(handler, "report", attrs); 160 _saxReport(handler, program); 161 XMLUtils.endElement(handler, "report"); 162 163 handler.endDocument(); 164 165 // Convert the report to configured output format 166 convertReport(_tmpFolder, fileName, xmlFile); 167 } 168 catch (Exception e) 169 { 170 getLogger().error("An error occured while generating 'Cohérence' report for program '{}' ({})", program.getTitle(), program.getCode(), e); 171 } 172 finally 173 { 174 FileUtils.deleteQuietly(xmlFile); 175 } 176 } 177 178 /** 179 * Get the report filename for a given program 180 * @param program The program 181 * @return the file name 182 */ 183 private String _getReportFileName(Program program) 184 { 185 StringBuilder sb = new StringBuilder(); 186 187 sb.append("consistency-"); 188 189 // Catalog 190 sb.append(program.getCatalog()); 191 sb.append("-"); 192 193 // Lang 194 sb.append(program.getLanguage()); 195 sb.append("-"); 196 197 // Mention or title 198 String mentionId = program.getMention(); 199 if (StringUtils.isBlank(mentionId)) 200 { 201 sb.append(program.getTitle()); 202 } 203 else 204 { 205 sb.append(_refTableHelper.getItemLabel(mentionId, program.getLanguage())); 206 } 207 208 // Code Ametys 209 String code = program.getCode(); 210 if (StringUtils.isNotBlank(code)) 211 { 212 sb.append("-"); 213 sb.append(code); 214 } 215 216 // Date 217 sb.append("-"); 218 sb.append(_currentFormattedDate); 219 220 return FilterNameHelper.filterName(sb.toString()); 221 } 222 223 private void _saxReport(TransformerHandler handler, Program program) throws SAXException 224 { 225 AttributesImpl attr = new AttributesImpl(); 226 attr.addCDATAAttribute("title", program.getTitle()); 227 XMLUtils.startElement(handler, "program", attr); 228 229 for (ConsistencyAnalysis analysis : _analyses) 230 { 231 XMLUtils.startElement(handler, "analysis"); 232 233 // Title 234 analysis.getLabel().toSAX(handler, "title"); 235 236 // Result 237 ConsistencyAnalysisResult result = analysis.analyze(program); 238 239 // Status 240 AttributesImpl attrs = new AttributesImpl(); 241 attrs.addCDATAAttribute("bgColor", result.getStatus().getBgColor()); 242 attrs.addCDATAAttribute("fontColor", result.getStatus().getFontColor()); 243 XMLUtils.startElement(handler, "status", attrs); 244 result.getStatusText().toSAX(handler); 245 XMLUtils.endElement(handler, "status"); 246 247 // Introduction 248 result.getIntroText().toSAX(handler, "intro"); 249 250 // Columns 251 XMLUtils.startElement(handler, "columns"); 252 Map<String, I18nizableText> columns = result.getColumns(); 253 List<String> indentableColumns = result.getIndentableColumns(); 254 for (String key : columns.keySet()) 255 { 256 AttributesImpl columnAttrs = new AttributesImpl(); 257 columnAttrs.addCDATAAttribute("name", key); 258 columnAttrs.addCDATAAttribute("indentable", String.valueOf(indentableColumns.contains(key))); 259 260 XMLUtils.startElement(handler, "column", columnAttrs); 261 columns.get(key).toSAX(handler); 262 XMLUtils.endElement(handler, "column"); 263 } 264 XMLUtils.endElement(handler, "columns"); 265 266 // Lines list 267 XMLUtils.startElement(handler, "lines"); 268 for (Map<String, Object> line : result.getLines()) 269 { 270 XMLUtils.startElement(handler, "line"); 271 for (Map.Entry<String, Object> entry : line.entrySet()) 272 { 273 if (entry.getValue() instanceof I18nizableText) 274 { 275 ((I18nizableText) entry.getValue()).toSAX(handler, entry.getKey()); 276 } 277 else 278 { 279 XMLUtils.createElement(handler, entry.getKey(), entry.getValue().toString()); 280 } 281 } 282 XMLUtils.endElement(handler, "line"); 283 } 284 285 286 287 XMLUtils.endElement(handler, "lines"); 288 289 XMLUtils.endElement(handler, "analysis"); 290 } 291 292 XMLUtils.endElement(handler, "program"); 293 } 294}