Les données utilisées dans les rapports personnalisés doivent être disponibles dans Process Reporting. Pour garantir la disponibilité des données, planifiez une tâche cron ou utilisez l’option Synchronisation sur l’interface utilisateur de Process Reporting.
Vous pouvez utiliser l’interface REST de QueryBuilder ou créer un service OSGi à l’aide de l’API QueryBuilder pour créer un rapport personnalisé.
Procédure générique de création d’un rapport personnalisé
Avant d’ajouter un rapport personnalisé, effectuez la procédure de modèle suivante :
-
-
La requête URL (encapsulant la requête souhaitée) doit renvoyer un objet de résultats de requête correct. Pour créer une requête, vous pouvez utiliser l’interface REST de QueryBuilder pour créer un service OSGi à l’aide de l’API QueryBuilder. Vous pouvez créer des requêtes dynamiques ou statiques.
-
Créez une interface utilisateur personnalisée pour afficher les résultats. Vous pouvez créer une interface utilisateur autonome ou intégrer le résultat dans une interface utilisateur de Process Reporting existante.
Utilisation de l’interface REST de QueryBuilder
L’interface REST de CRX QueryBuilder expose la fonctionnalité QueryBuilder de Asset Share via une API Java et une API REST. Découvrez comment utiliser l’interface REST de CRX QueryBuilder, avant d’effectuer les étapes suivantes :
-
Accédez à l’URL http://[serveur]:[port]/lc/bin/querybuilder.json
-
Créez une requête basée sur la structure du nœud de stockage et les propriétés de nœud de Process Reporting.
Vous pouvez définir des paramètres facultatifs pour spécifier le décalage, la limite, les accès, et les propriétés. Vous pouvez coder en dur les arguments pour les rapports statiques et récupérer les paramètres de l’interface utilisateur pour les rapports dynamiques.
Pour récupérer tous les noms de processus, la requête doit être :
http://[Serveur]:[Port]/lc/bin/querybuilder.json?exact=false&p.hits=selective&p.properties=pmProcessTitle&path=%2fcontent%2freporting%2fpm&property=pmNodeType&property.operation=equals&property.value=ProcessType&type=sling%3aFolder
Remarque :Dans chaque requête, le paramètre de chemin d’accès pointe sur l’emplacement de stockage de CRX et les caractères sont placés dans une séquence d’échappement, conformément à la norme d’URL.
Création d’un service à l’aide de l’API QueryBuilder
Pour créer un service à l’aide de l’API QueryBuilder, vous devez créer et déployer un bundle CQ OSGI et utiliser l’API Query Builder.
-
Création d’un service OSGi avec des annotations appropriées. Pour accéder à QueryBuilder, utilisez :
@Reference (referenceInterface = QueryBuilder.class)
private QueryBuilder queryBuilder; -
Créez un groupe d’attributs. Le code permettant de créer un groupe d’attributs est :
PredicateGroup predicateGroup = new PredicateGroup();
predicateGroup.setAllRequired(true); -
Ajoutez des attributs au groupe d’attributs (predicateGroup) nouvellement créé. Parmi les outils de construction d’attributs utiles figurent JcrBoolPropertyPredicateEvaluator, JcrPropertyPredicateEvaluator, RangePropertyPredicateEvaluator, DateRangePredicateEvaluator, and TypePredicateEvaluator.
Pour les rapports statiques, codez en dur les attributs. En revanche, pour les rapports dynamiques, récupérez les attributs de la requête.
L’exemple de code pour obtenir toutes les instances d’un processus est :
Predicate predicate; //Add the path Constraint predicate = new Predicate(PathPredicateEvaluator.PATH); predicate.set(PathPredicateEvaluator.PATH, "/content/reporting/pm"); // should point to the crx path being used to store data predicate.set(PathPredicateEvaluator.EXACT, "false"); predicateGroup.add(predicate); //type nt:unstructured predicate = new Predicate(TypePredicateEvaluator.TYPE); predicate.set(TypePredicateEvaluator.TYPE, "nt:unstructured"); predicateGroup.add(predicate); //NodeType: Process Instance predicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY); predicate.set(JcrPropertyPredicateEvaluator.PROPERTY, "pmNodeType"); predicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS); predicate.set(JcrPropertyPredicateEvaluator.VALUE, "ProcessInstance"); predicateGroup.add(predicate); //processName predicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY); predicate.set(JcrPropertyPredicateEvaluator.PROPERTY, "pmProcessName"); predicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS); predicate.set(JcrPropertyPredicateEvaluator.VALUE, processName); //processName variable stores the name of the process whose instances need to be searched predicateGroup.add(predicate);
-
Définissez la requête en utilisant le groupe d’attributs (predicateGroup).
Query query = queryBuilder.createQuery(predicateGroup, session);
-
Obtenez le résultat de la requête.
query.setStart(offset); // hardcode or fetch from request if(hits == -1) // hardcode or fetch from request hits = 0; query.setHitsPerPage(hits); SearchResult searchResult = query.getResult();
-
Itérez le résultat et transformez les résultats au format souhaité. Le code pour envoyer les résultats au format CSV est :
Iterator<Node> iter = searchResult.getNodes(); while(iter.hasNext()) { Node node = iter.next(); row = new StringBuilder(); for (String property : includeProperties) { // the properties of the node which needs to be returned, or one can return all the properties too. try { row.append(node.getProperties(property).nextProperty().getString() + COMMA_SEPARATOR); } catch (NoSuchElementException e) { //Adding separator for no value row.append(COMMA_SEPARATOR); } catch (RepositoryException e) { e.printStackTrace(); } } row.deleteCharAt(row.lastIndexOf(COMMA_SEPARATOR)); row.append(NEW_LINE); out.write(row.toString().getBytes());
-
Utilisezorg.apache.felix maven-bundle-plugin pour créer un lot OSGi pour le servlet.
-
Déployez le lot sur le serveur CRX.
Exemple de service
L’exemple de service suivant compte des instances d’un processus en COURS D’EXECUTION avec l’état TERMINE à la fin de chaque mois, trimestre et année.
package custom.reporting.service; import java.text.DateFormatSymbols; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import javax.jcr.Node; import javax.jcr.Session; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import com.day.cq.search.Predicate; import com.day.cq.search.PredicateGroup; import com.day.cq.search.Query; import com.day.cq.search.QueryBuilder; import com.day.cq.search.eval.JcrPropertyPredicateEvaluator; import com.day.cq.search.eval.PathPredicateEvaluator; import com.day.cq.search.eval.TypePredicateEvaluator; import com.day.cq.search.result.SearchResult; @Component(metatype = true, immediate = true, label = "PeriodicProcessVolume", description = "Service for supporting cutom reports pluggable to Process Reporting.") @Service(value = PeriodicProcessVolume.class) public class PeriodicProcessVolume { private static String[] monthNameList = new DateFormatSymbols().getMonths(); private static String[] quaterNameList = { "I", "II", "III", "IV" }; private final Map<Integer, Map<Integer, Long[]>> monthly = new HashMap<Integer, Map<Integer, Long[]>>(); private final Map<Integer, Map<Integer, Long[]>> quaterly = new HashMap<Integer, Map<Integer, Long[]>>(); private final Map<Integer, Long[]> yearly = new HashMap<Integer, Long[]>(); @Reference(referenceInterface = QueryBuilder.class) private QueryBuilder queryBuilder; private void addConstraints(PredicateGroup predicateGroup, String processName) { Predicate predicate; //Add the path Constraint predicate = new Predicate(PathPredicateEvaluator.PATH); predicate.set(PathPredicateEvaluator.PATH, "/content/reporting/pm"); predicate.set(PathPredicateEvaluator.EXACT, "false"); predicateGroup.add(predicate); //type nt:unstructured predicate = new Predicate(TypePredicateEvaluator.TYPE); predicate.set(TypePredicateEvaluator.TYPE, "nt:unstructured"); predicateGroup.add(predicate); //NodeType: Process Instance predicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY); predicate.set(JcrPropertyPredicateEvaluator.PROPERTY, "pmNodeType"); predicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS); predicate.set(JcrPropertyPredicateEvaluator.VALUE, "ProcessInstance"); predicateGroup.add(predicate); //processName if (processName != null) { predicate = new Predicate(JcrPropertyPredicateEvaluator.PROPERTY); predicate.set(JcrPropertyPredicateEvaluator.PROPERTY, "pmProcessName"); predicate.set(JcrPropertyPredicateEvaluator.OPERATION, JcrPropertyPredicateEvaluator.OP_EQUALS); predicate.set(JcrPropertyPredicateEvaluator.VALUE, processName); predicateGroup.add(predicate); } } private Long[] setFrequency(Long[] frequency, int index) { if (frequency == null) { frequency = new Long[2]; frequency[0] = 0L; frequency[1] = 0L; } frequency[index] = frequency[index] + 1L; return frequency; } public void populateValues(Session session, String processName) { PredicateGroup predicateGroup = new PredicateGroup(); predicateGroup.setAllRequired(true); try { addConstraints(predicateGroup, processName); long batchSize = 10000L; long start = 0l; while (true) { Query query = queryBuilder.createQuery(predicateGroup, session); query.setStart(start); query.setHitsPerPage(batchSize); SearchResult searchResult = query.getResult(); Iterator<Node> itr = searchResult.getNodes(); long length = 0; while (itr.hasNext()) { length++; Node n = itr.next(); Calendar calender = n.getProperty("pmCreateTime").getDate(); String status = n.getProperty("pmStatus").getString(); int index = 0; if ("COMPLETE".equals(status)) { index = 1; } else if ("RUNNING".equals(status)) { index = 0; } else { continue; } int month = calender.get(Calendar.MONTH); int year = calender.get(Calendar.YEAR); int quater; if (month < 3) { quater = 1; } else if (month < 6) { quater = 2; } else if (month < 9) { quater = 3; } else { quater = 4; } Long frequency[]; Map<Integer, Long[]> yearMonthMap = this.monthly.get(year); if (yearMonthMap == null) { yearMonthMap = new HashMap<Integer, Long[]>(); } frequency = yearMonthMap.get(month); frequency = setFrequency(frequency, index); yearMonthMap.put(month, frequency); this.monthly.put(year, yearMonthMap); Map<Integer, Long[]> yearQuaterMap = this.quaterly.get(year); if (yearQuaterMap == null) { yearQuaterMap = new HashMap<Integer, Long[]>(); } frequency = yearQuaterMap.get(quater); frequency = setFrequency(frequency, index); yearQuaterMap.put(quater, frequency); this.quaterly.put(year, yearQuaterMap); frequency = this.yearly.get(year); frequency = setFrequency(frequency, index); this.yearly.put(year, frequency); } if (length < batchSize) { break; } else { start = start + batchSize; } } } catch (Exception e) { e.printStackTrace(); } } public Map<String, Long[]> getMonthly() { Map<String, Long[]> result = new LinkedHashMap<String, Long[]>(); SortedSet<Integer> years = new TreeSet<Integer>(monthly.keySet()); for (Integer year : years) { Map<Integer, Long[]> yearMonthMap = monthly.get(year); SortedSet<Integer> months = new TreeSet<Integer>(yearMonthMap.keySet()); for (Integer month : months) { String str = monthNameList[month] + " " + year; result.put(str, yearMonthMap.get(month)); } } return result; } public Map<String, Long[]> getQuaterly() { Map<String, Long[]> result = new LinkedHashMap<String, Long[]>(); SortedSet<Integer> years = new TreeSet<Integer>(quaterly.keySet()); for (Integer year : years) { Map<Integer, Long[]> quaterMonthMap = quaterly.get(year); SortedSet<Integer> quaters = new TreeSet<Integer>(quaterMonthMap.keySet()); for (Integer quater : quaters) { String str = quaterNameList[quater - 1] + " " + year; result.put(str, quaterMonthMap.get(quater)); } } return result; } public Map<Integer, Long[]> getYearly() { return yearly; } }
L’exemple de fichierpom.xml à créer au-dessus du service est :
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- ====================================================================== --> <!-- P R O J E C T D E S C R I P T I O N --> <!-- ====================================================================== --> <groupId>com.custom</groupId> <artifactId>sample-report-core</artifactId> <packaging>bundle</packaging> <name>PR Sample Report</name> <description>Bundle providing support for a custom report pluggable to process reporting.</description> <version>1</version> <!-- ====================================================================== --> <!-- B U I L D D E F I N I T I O N --> <!-- ====================================================================== --> <build> <plugins> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.3.7</version> <extensions>true</extensions> <configuration> <instructions> <Bundle-Category>sample-report</Bundle-Category> <Export-Package> custom.reporting.service.*; </Export-Package> </instructions> </configuration> </plugin> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-scr-plugin</artifactId> <version>1.11.0</version> <executions> <execution> <id>generate-scr-scrdescriptor</id> <goals> <goal>scr</goal> </goals> <configuration> <!-- Private service properties for all services. --> <properties> <service.vendor>Sample Report</service.vendor> </properties> </configuration> </execution> </executions> </plugin> </plugins> </build> <!-- ====================================================================== --> <!-- D E P E N D E N C I E S --> <!-- ====================================================================== --> <dependencies> <dependency> <groupId>com.day.cq</groupId> <artifactId>cq-search</artifactId> <version>5.6.4</version> </dependency> <dependency> <groupId>javax.jcr</groupId> <artifactId>jcr</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.apache.felix</groupId> <artifactId>org.apache.felix.scr.annotations</artifactId> <version>1.9.0</version> </dependency> </dependencies> </project>
Création d’une interface utilisateur distincte
Pour créer une interface utilisateur distincte affichant les résultats, vous devez connaître les bases de Sling, savoir Créer un nœud CRX et fournir les droits d’accès appropriés.
-
Créez un nœud CRX au niveau du nœud /apps et accordez les droits d’accès appropriés. (PERM_PROCESS_REPORTING_USER)
-
Définissez le rendu au niveau du nœud /content.
-
Ajoutez les fichiers JSP ou HTML au niveau du nœud créé à l'étape 1. Vous pouvez également ajouter des fichiers CSS.
-
Ajoutez le code JavaScript pour lancer un appel Ajax vers l’API REST de Query Builder ou vers votre service. Vous pouvez également ajouter les arguments appropriés.
-
Ajoutez un gestionnaire de réussite approprié à l’appel Ajax pour analyser et afficher les résultats. Vous pouvez analyser le résultat dans plusieurs formats (json/csv/défini par l’utilisateur), puis l’afficher sous forme de tabulaire ou d’autres formulaires.
-
(Facultatif) Ajoutez un gestionnaire d’erreur approprié à l’appel Ajax.
Un exemple de code JSP utilisant le service OSGi et l’API QueryBuilder est :
<%@taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0"%> <%request.setAttribute("silentAuthor", new Boolean(true));%> <%@include file="/libs/foundation/global.jsp"%> <%@ page import="java.util.Map, java.util.Set, com.adobe.idp.dsc.registry.service.ServiceRegistry, javax.jcr.Session, org.apache.sling.api.resource.ResourceResolver, custom.reporting.service.PeriodicProcessVolume"%> <% response.setContentType("text/html"); response.setCharacterEncoding("utf-8"); %><!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/lc/apps/sample-report-process-reporting/custom-reports/periodicProcessVolume/style.css"> <title>REPORT Monthly / Qaterly / Yearly</title> <script type="text/javascript"> <% slingResponse.setCharacterEncoding("utf-8"); ResourceResolver resolver = slingRequest.getResourceResolver(); String processName = slingRequest.getParameter("processName"); Session session = resolver.adaptTo(Session.class); custom.reporting.service.PeriodicProcessVolume periodicProcessVolume = sling.getService(custom.reporting.service.PeriodicProcessVolume.class); periodicProcessVolume.populateValues(session, processName); if (processName == null) { processName = "All"; } %> var lineSeprator = "<td class='seprator'>----------------</td>"; var tableEnder = "<tr>" + lineSeprator + lineSeprator + lineSeprator + "</tr>"; var tableColHeader = "<td class='colHead colNum'>Running</td>"; tableColHeader += "<td class='colHead colNum'>Complete</td></tr>"; tableColHeader += tableEnder; var monthly = "<table><tr><td class='colHead colStr'>Month</td>"; monthly += tableColHeader; <% Map<String, Long[]> monthlyMap = periodicProcessVolume.getMonthly(); Set<String> monthKeys = monthlyMap.keySet(); for (String key: monthKeys) { Long[] frequencies = monthlyMap.get(key); %> monthly += "<tr><td class='colStr'> <%= key %> </td>"; monthly += "<td class='colNum'> <%= frequencies[0] %> </td>"; monthly += "<td class='colNum'> <%= frequencies[1] %> </td></tr>"; <% } %> monthly += tableEnder; var quaterly = "<table><tr><td class='colHead colStr'>Quater</td>"; quaterly += tableColHeader; <% Map<String, Long[]> quaterMap = periodicProcessVolume.getQuaterly(); Set<String> quaterKeys = quaterMap.keySet(); for (String key: quaterKeys) { Long[] frequencies = quaterMap.get(key); %> quaterly += "<tr><td class='colStr'> <%= key %> </td>"; quaterly += "<td class='colNum'> <%= frequencies[0] %> </td>"; quaterly += "<td class='colNum'> <%= frequencies[1] %> </td></tr>"; <% } %> quaterly += tableEnder; var yearly = "<table><tr><td class='colHead colStr'>Year</td>"; yearly += tableColHeader; <% Map<Integer, Long[]> yearMap = periodicProcessVolume.getYearly(); Set<Integer> yearKeys = yearMap.keySet(); for (Integer key: yearKeys) { Long[] frequencies = yearMap.get(key); %> yearly += "<tr><td class='colStr'> <%= key %> </td>"; yearly += "<td class='colNum'> <%= frequencies[0] %> </td>"; yearly += "<td class='colNum'> <%= frequencies[1] %> </td></tr>"; <% } %> yearly += tableEnder; function reloadFrame(value) { if (value === '-1') { window.location = "/lc/content/process-reporting-runtime/custom-reports/periodicProcessVolume.html"; } else { window.location = "/lc/content/process-reporting-runtime/custom-reports/periodicProcessVolume.html?processName=" + value; } } function populateTable(selection) { if (selection === 0) { document.getElementById('tableHeading').innerHTML = 'Monthly'; document.getElementById('volumeTable').innerHTML = monthly; } else if (selection === 1) { document.getElementById('tableHeading').innerHTML = 'Quaterly'; document.getElementById('volumeTable').innerHTML = quaterly; } else { document.getElementById('tableHeading').innerHTML = 'Yearly'; document.getElementById('volumeTable').innerHTML = yearly; } } function fetchProcesses() { var xmlhttp = new XMLHttpRequest(), request = ''; xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { var responseText, response, items, hits = [], responseSize = 0, processName, selectedIndex = 0, comboBox; responseText = xmlhttp.responseText; if (responseText !== undefined && responseText !== null) { response = JSON.parse(responseText); responseSize = response.results; hits = response.hits; } items = "<option value='-1'>All</option>"; for(var i = 0; i < responseSize; i++) { processName = hits[i].pmProcessTitle; if (processName === '<%= processName %>') { selectedIndex = i + 1; } items += "<option value='" + processName + "'>" + processName + "</option>" } comboBox = document.getElementById('processSelection'); comboBox.innerHTML = items; comboBox.selectedIndex = selectedIndex; } }; request = "/lc/bin/querybuilder.json?"; request += "exact=false&"; request += "p.hits=selective&"; request += "p.properties=pmProcessTitle&"; request += "path=%2fcontent%2freporting%2fpm&"; request += "property=pmNodeType&"; request += "property.operation=equals&"; request += "property.value=ProcessType&"; request += "type=sling%3aFolder"; xmlhttp.open("POST", request, true); xmlhttp.setRequestHeader("Content-type","application/json"); xmlhttp.send(); } </script> </head> <body onLoad="fetchProcesses();populateTable(0);"> Process: <select id="processSelection" onchange="reloadFrame(this.value);"></select>     Period Interval: <select name="periodSelection" onchange="populateTable(this.selectedIndex);"> <option value="1">Monthly</option> <option value="2">Quaterly</option> <option value="3">Yearly</option> </select> <br> <br> <br> <br> <div class="inline"> Process:   <b><%= processName %></b>     Period:   </div> <b> <div id="tableHeading" class="inline"> </div> </b> <br><br> <div id="volumeTable"> </div> </body> </html>
Intégration d’une interface utilisateur de rapport dans l’interface utilisateur de Process Reporting existante
Pour créer une interface utilisateur distincte affichant les résultats, vous devez connaître les bases de Sling, savoir Créer un nœud CRX et fournir les droits d’accès appropriés.
-
Créez une interface utilisateur distincte telle que décrite dans la section Créer une interface utilisateur distincte.
-
Créez un nœud enfantnt:unstructured au niveau du nœud /content/process-reporting-runtime/custom-reports pour chaque port enfichable.
- id - Indique le numéro d’identification unique du rapport.
- nom - Indique le nom du rapport. Le nom s’affiche dans l’interface utilisateur.
- lien - Spécifie le lien relatif vers le rendu de l’interface utilisateur distincte. Le lien est créé à l’étape 1.
- description - Spécifie la description d’une ligne du rapport. Vous pouvez laisser le champ de description vide.
- icône - Spécifie l’image pour représenter graphiquement le rapport. Vous pouvez laisser le champ d’icône vide.
-
L’interface utilisateur de rapport est intégrée à l’interface utilisateur Process Reporting. Après avoir intégré l’interface utilisateur, l’interface utilisateur mise à jour ressemble aux images suivantes :
Exemple de package
Importez le package sample-report-pkg-1.zip pour intégrer des rapports personnalisés et l’interface utilisateur décrits dans l’article dans l’interface utilisateur de Process Management.
Telechargement
Adobe
Recevez de l’aide plus rapidement et plus facilement
Nouvel utilisateur ?