001/*
002 *  Copyright 2017 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.extraction.execution;
017
018import java.io.File;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Map.Entry;
025
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.commons.lang.StringUtils;
029import org.apache.excalibur.source.Source;
030import org.apache.excalibur.source.SourceResolver;
031import org.apache.excalibur.source.impl.FileSource;
032import org.quartz.JobKey;
033import org.quartz.SchedulerException;
034
035import org.ametys.core.schedule.Runnable;
036import org.ametys.core.ui.Callable;
037import org.ametys.core.ui.StaticClientSideElement;
038import org.ametys.core.user.User;
039import org.ametys.core.user.UserIdentity;
040import org.ametys.core.user.UserManager;
041import org.ametys.core.util.JSONUtils;
042import org.ametys.plugins.core.schedule.Scheduler;
043import org.ametys.plugins.extraction.ExtractionConstants;
044import org.ametys.plugins.extraction.ExtractionHelper;
045import org.ametys.runtime.i18n.I18nizableText;
046
047/**
048 * This client site element creates a button to execute an extraction
049 */
050public class ExecuteExtractionClientSideElement extends StaticClientSideElement
051{
052    private UserManager _userManager;
053    private ExtractionDefinitionReader _reader;
054    private SourceResolver _sourceResolver;
055    private JSONUtils _jsonUtils;
056    private Scheduler _scheduler;
057    private ExtractionHelper _extractionHelper;
058    
059    @Override
060    public void service(ServiceManager serviceManager) throws ServiceException
061    {
062        super.service(serviceManager);
063        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
064        _reader = (ExtractionDefinitionReader) serviceManager.lookup(ExtractionDefinitionReader.ROLE);
065        _sourceResolver = (SourceResolver) serviceManager.lookup(SourceResolver.ROLE);
066        _jsonUtils = (JSONUtils) serviceManager.lookup(JSONUtils.ROLE);
067        _scheduler = (Scheduler) serviceManager.lookup(Scheduler.ROLE);
068        _extractionHelper = (ExtractionHelper) serviceManager.lookup(ExtractionHelper.ROLE);
069    }
070    
071    /**
072     * Retrieve needed extraction parameters.
073     * @param definitionFile The extraction definition file path
074     * @return a <code>Map</code> containing parameters infos used to configure form to fill the parameters
075     * @throws Exception if an error occurs
076     */
077    @Callable (right = "Extraction_Rights_ExecuteExtraction")
078    public Map<String, Object> getExecutionParameters(String definitionFile) throws Exception
079    {
080        Map<String, Object> executionParamerters = new LinkedHashMap<>();
081
082        String definitionFilePath = ExtractionConstants.DEFINITIONS_DIR + definitionFile;
083        Source src = _sourceResolver.resolveURI(definitionFilePath);
084        File file = ((FileSource) src).getFile();
085        
086        if (!file.exists())
087        {
088            throw new IllegalArgumentException("The file " + definitionFilePath + " does not exist.");
089        }
090
091        executionParamerters.put("format", _getFormatInputConfig());
092        executionParamerters.put("stylesheetForXML", _getStylesheetInputConfigForXMLFormat());
093        executionParamerters.put("stylesheetForPDF", _getStylesheetInputConfigForPDFFormat());
094        
095        Extraction extraction = _reader.readExtractionDefinitionFile(file);
096        
097        Map<String, String> clasuesVariables = extraction.getQueryVariablesNamesAndContentTypes();
098        List<String> optionalColumns = extraction.getDisplayOptionalColumnsNames();
099        if (!clasuesVariables.isEmpty() || !optionalColumns.isEmpty())
100        {
101            List<Map<String, Object>> variablesFieldSets = new ArrayList<>();
102            if (!clasuesVariables.isEmpty())
103            {
104                Map<String, Object> clausesVariablesFieldSet = new HashMap<>();
105                clausesVariablesFieldSet.put("role", "fieldset");
106                clausesVariablesFieldSet.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_CLAUSES_VARIABLES_FIELDSET_LABEL"));
107            
108                Map<String, Object> clausesVariablesFieldSetElements = new HashMap<>();
109                for (Map.Entry<String, String> clausesVariable : clasuesVariables.entrySet())
110                {
111                    clausesVariablesFieldSetElements.put(clausesVariable.getKey(), _getClauseVariableInputConfig(clausesVariable));
112                }
113                clausesVariablesFieldSet.put("elements", clausesVariablesFieldSetElements);
114                
115                variablesFieldSets.add(clausesVariablesFieldSet);
116            }
117            
118            if (!optionalColumns.isEmpty())
119            {
120                Map<String, Object> optionalColumnsFieldSet = new HashMap<>();
121                optionalColumnsFieldSet.put("role", "fieldset");
122                optionalColumnsFieldSet.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_OPTIONAL_COLUMNS_FIELDSET_LABEL"));
123            
124                Map<String, Object> optionalColumnsFieldSetElements = new HashMap<>();
125                for (String optionalColumn : optionalColumns)
126                {
127                    optionalColumnsFieldSetElements.put(optionalColumn, _getOptionalColumnsInputConfig(optionalColumn));
128                }
129                optionalColumnsFieldSet.put("elements", optionalColumnsFieldSetElements);
130                
131                variablesFieldSets.add(optionalColumnsFieldSet);
132            }
133            
134            executionParamerters.put("fieldsets", variablesFieldSets);
135        }
136
137        executionParamerters.put("recipient", _getRecipientInputConfig());
138        
139        return executionParamerters;
140    }
141    
142    private Map<String, Object> _getFormatInputConfig()
143    {
144        Map<String, Object> inputConfig = new HashMap<>();
145        
146        inputConfig.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_FORMAT_INPUT_LABEL"));
147        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_FORMAT_INPUT_DESCRIPTION"));
148        inputConfig.put("type", "string");
149        
150        List<Map<String, Object>> enumeration = new ArrayList<>();
151    
152        // XML format
153        Map<String, Object> xmlOption = new HashMap<>();
154        xmlOption.put("value", "xml");
155        xmlOption.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_FORMAT_INPUT_XML_LABEL"));
156        enumeration.add(xmlOption);
157    
158        // PDF format
159        Map<String, Object> pdfOption = new HashMap<>();
160        pdfOption.put("value", "pdf");
161        pdfOption.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_FORMAT_INPUT_PDF_LABEL"));
162        enumeration.add(pdfOption);
163        
164        inputConfig.put("enumeration", enumeration);
165        inputConfig.put("default-value", "xml");
166        inputConfig.put("validation", _getMandatoryValidation());
167        
168        return inputConfig;
169    }
170    
171    private Map<String, Object> _getStylesheetInputConfigForPDFFormat()
172    {
173        Map<String, Object> inputConfig = _getStylesheetInputConfig();
174        inputConfig.put("validation", _getMandatoryValidation());
175        inputConfig.put("disableCondition", _getStylesheetInputDisabledCondition("pdf"));
176        return inputConfig;
177    }
178    
179    private Map<String, Object> _getStylesheetInputConfigForXMLFormat()
180    {
181        Map<String, Object> inputConfig = _getStylesheetInputConfig();
182        inputConfig.put("disableCondition", _getStylesheetInputDisabledCondition("xml"));
183        return inputConfig;
184    }
185    
186    private Map<String, Object> _getStylesheetInputConfig()
187    {
188        Map<String, Object> inputConfig = new HashMap<>();
189        
190        inputConfig.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_XSLT_FILE_INPUT_LABEL"));
191        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_XSLT_FILE_INPUT_DESCRIPTION"));
192        inputConfig.put("type", "string");
193        
194        try
195        {
196            // Try to build enumeration for XSLT files
197            List<Map<String, Object>> enumeration = new ArrayList<>();
198            
199            Map<Object, I18nizableText> entries = _extractionHelper.getExtractionStylesheets();
200            for (Object entryKey : entries.keySet())
201            {
202                Map<String, Object> option = new HashMap<>();
203                option.put("value", entryKey);
204                option.put("label", entries.get(entryKey));
205                enumeration.add(option);
206            }
207            
208            inputConfig.put("enumeration", enumeration);
209        }
210        catch (Exception e)
211        {
212            getLogger().error("Unable to provide enumeration elements for extraction stylesheets", e);
213        }
214        
215        return inputConfig;
216    }
217    
218    private Map<String, Object> _getStylesheetInputDisabledCondition(String value)
219    {
220        Map<String, Object> condition = new HashMap<>();
221        condition.put("id", "format");
222        condition.put("operator", "neq");
223        condition.put("value", value);
224        
225        List<Map<String, Object>> conditions = new ArrayList<>();
226        conditions.add(condition);
227        
228        Map<String, Object> disabledCondition = new HashMap<>();
229        disabledCondition.put("condition", conditions);
230        return disabledCondition;
231    }
232    
233    private Map<String, Object> _getClauseVariableInputConfig(Entry<String, String> clausesVariable)
234    {
235        Map<String, Object> inputConfig = new HashMap<>();
236        
237        inputConfig.put("label", clausesVariable.getKey());
238        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_CLAUSES_VARIABLE_INPUTS_DESCRIPTION"));
239        
240        inputConfig.put("type", "content");
241        inputConfig.put("widget", "edition.select-content");
242        
243        Map<String, Object> widgetParams = new HashMap<>();
244        widgetParams.put("contentType", clausesVariable.getValue());
245        inputConfig.put("widget-params", widgetParams);
246        
247        inputConfig.put("validation", _getMandatoryValidation());
248        
249        return inputConfig;
250    }
251    
252    private Map<String, Object> _getOptionalColumnsInputConfig(String optionalColumn)
253    {
254        Map<String, Object> inputConfig = new HashMap<>();
255        
256        inputConfig.put("label", optionalColumn);
257        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_OPTIONAL_COLUMNS_INPUTS_DESCRIPTION"));
258        
259        inputConfig.put("type", "boolean");
260        inputConfig.put("widget", "edition.checkbox");
261        
262        inputConfig.put("validation", _getMandatoryValidation());
263        
264        return inputConfig;
265    }
266    
267    private Map<String, Object> _getRecipientInputConfig()
268    {
269        Map<String, Object> inputConfig = new HashMap<>();
270        
271        // Get current user email
272        String currentUserEmail = null;
273        UserIdentity currentUser = _currentUserProvider.getUser();
274        
275        String login = currentUser.getLogin();
276        if (StringUtils.isNotBlank(login))
277        {
278            String userPopulationId = currentUser.getPopulationId();
279            User user = _userManager.getUser(userPopulationId, login);
280            currentUserEmail = user.getEmail();
281        }
282        
283        inputConfig.put("label", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_RECIPIENT_INPUT_LABEL"));
284        inputConfig.put("description", new I18nizableText(ExtractionConstants.PLUGIN_NAME, "PLUGINS_EXTRACTION_EXECUTE_EXTRACTION_RECIPIENT_INPUT_DESCRIPTION"));
285        inputConfig.put("type", "string");
286        inputConfig.put("default-value", currentUserEmail);
287        
288        return inputConfig;
289    }
290    
291    private Map<String, Object> _getMandatoryValidation()
292    {
293        Map<String, Object> mandatoryValidation = new HashMap<>();
294        mandatoryValidation.put("mandatory", true);
295        return mandatoryValidation;
296    }
297    
298    /**
299     * Execute the extraction
300     * @param definitionFilePath The extraction definition file path
301     * @param variables clauses variables and optional columns
302     * @param recipient An email will be sent at this address when the extraction is complete
303     * @param format The format of the extraction result
304     * @param xsltFilePath The stylesheet to apply on extraction result
305     * @return a Map with error if one occurs
306     * @throws Exception if an error occurs
307     */
308    @Callable (right = "Extraction_Rights_ExecuteExtraction")
309    public Map<String, Object> executeExtraction(String definitionFilePath, Map<String, Object> variables, String recipient, String format, String xsltFilePath) throws Exception
310    {
311        Map<String, Object> result = new HashMap<>();
312        
313        try
314        {
315            String variablesAsString = _jsonUtils.convertObjectToJson(variables);
316            Runnable executeExtractionRunnable = new ExecuteExtractionRunnable(definitionFilePath, variablesAsString, recipient, format, xsltFilePath);
317            JobKey jobKey = new JobKey(executeExtractionRunnable.getId(), Scheduler.JOB_GROUP);
318            if (_scheduler.getScheduler().checkExists(jobKey))
319            {
320                _scheduler.getScheduler().deleteJob(jobKey);
321            }
322            _scheduler.scheduleJob(executeExtractionRunnable);
323            getLogger().info("Scheduled extraction execution of " + definitionFilePath);
324        }
325        catch (SchedulerException e)
326        {
327            if (getLogger().isErrorEnabled())
328            {
329                getLogger().error("An error occured when trying to schedule the extraction execution of " + definitionFilePath, e);
330            }
331            result.put("error", "scheduler-error");
332            return result;
333        }
334        
335        result.put("definitionFilePath", definitionFilePath);
336        return result;
337    }
338}