5.1 Generazione dei log di eventi
5.1.2 Denizione di una sintassi per la generazione dei log
Per quanto riguarda la generazione dei log, il sistema fornisce anche altri metodi per raggiungere tale scopo, senza necessariamente la conoscenza del codice XML come abbiamo visto invece nella Sezione 5.1.1.
Infatti l'utilizzo del formato XML, seppur semplicato grazie alle funzioni presentate nella sezione precedente, prevede comunque una conoscenza della struttura XML di un log XES, in modo da costruire il documento nel rispetto dello standard, sia per quanto riguarda la corretta posizione ed utilizzo dei tag XML, sia per quanto riguarda l'utilizzo delle chiavi e dei valori dei vari attributi.
Per permettere all'utente di denire un log in linguaggio più naturale, è stata creata una sintassi per la denizione della struttura di un log, che verrà
poi processata e trasformata dal sistema in un documento XML, senza che quindi l'utente ne conosca nel dettaglio la forma.
La sintassi si basa sul formato CSV per la denizione del log da generare e va inserita all'interno di un editor vuoto dell'applicazione.
Quindi anche in questo caso, come nel precedente, si utilizza il pulsante "New" dell'interfaccia e si sceglie di aprire un nuovo editor (Fig. 5.1). Al suo interno potremo a questo punto utilizzare la sintassi che andremo adesso a vedere. 1 trace:id,concept:name,org:resource,time:timestamp 2 1,A,Luca,2015-07-19T17:01:56.399+02:00 3 1,B,Giovanni,2015-07-19T17:01:57.399+02:00 4 1,C,Marco,2015-07-19T17:01:58.399+02:00 5 1,D,Francesco,2015-07-19T17:01:59.399+02:00 6 2,A,Luca,2015-07-19T17:02:00.399+02:00 7 2,B,Giovanni,2015-07-19T17:02:01.399+02:00 8 2,D,Francesco,2015-07-19T17:02:02.399+02:00
Seguendo l'esempio appena mostrato e le speciche del formato CSV, la prima riga non vuota del documento dovrà contenere i nomi delle colonne, separate da virgola.
Il primo di questi nomi sarà trace:id e conterrà gli identicatori delle tracce del log. Vedremo in seguito come questa colonna viene utilizzata, per ora ci limitiamo a denirne il nome.
Tutti i successivi nomi devono corrispondere ad una chiave di un attri- buto di una delle estensioni standard di XES, viste precedentemente. Ad esempio, potremo avere come nomi colonna concept:name eorg:resource, che indicano rispettivamente l'attributo che memorizza il nome di un even- to e quello che ne memorizza la risorsa che lo ha eseguito, oppure il nome
colonna time:timestamp, che registra il momento di esecuzione dell'evento. Nelle successive righe del documento vengono inseriti i valori per queste colonne, come da formato CSV.
Ogni riga corrisponde ad un evento del log che stiamo generando, dove il primo valore della colonna trace:id corrisponde alla traccia alla quale questo evento appartiene, mentre i successivi valori corrispondono ai valori degli attributi deniti nella riga delle colonne, tutti ovviamente separati da virgola.
Ogni evento dunque sarà rappresentato da una riga nel documento, indi- cherà la traccia di appartenenza e i suoi valori per gli attributi elencati nei campi colonna, se presenti. Nel caso infatti un evento non abbia un dato valore per l'attributo specicato, questo può essere lasciato vuoto.
Ogni traccia di conseguenza sarà rappresentata da un identicatore e da un insieme di eventi, ovvero le righe del documento che riportano il suo identicatore come primo elemento.
Procedendo con questa sintassi si raggiunge la denizione di un log, che sarà costituito quindi da tutte le tracce ed eventi elencati nel documento, con questi ultimi contenenti a loro volta gli attributi per i quali hanno specicato un valore.
Una volta denito un log in questo modo, si può procedere alla genera- zione del log vero e proprio in formato XML.
Questa operazione può essere eseguita attraverso il pulsante "Generate" presente nel tab "Discovery" dell'interfaccia (Fig. 4.2).
Questo comando utilizza per la trasformazione del documento CSV in un log XES una classe CsvXesParser di cui abbiamo già parlato nella Sezio-
ne 3.3.4.3.
Questa classe come già accennato estende la classe XParser delle librerie OpenXES per denire un parser XES a partire da un documento scritto con la sintassi spiegata precedentemente.
Per prima cosa il parser ricerca la prima riga non vuota del documento, che come sappiamo contiene i nomi delle colonne.
Se riesce a trovarla (ovvero il documento non è vuoto), legge i nomi delle colonne e li valida, controllando che questi appartengano all'insieme degli attributi delle estensioni standard di XES.
A questo punto il parser inizia a leggere ogni riga del documento, ge- nerando passo dopo passo un oggetto XLog contenente tante tracce XTrace quanti sono gli identicatori che le rappresentano e contenenti tanti eventi XEvent quante sono le righe associate allo stesso identicatore.
1 ...
2 // create a XLog
3 XLog log = xesFactory.createLog(); 4 ...
5 String [] values;
6 // we start reading all the other lines to convert them in XES elements 7 while ((values = reader.readNext()) != null) {
8 // update the counter
9 lineNumber++;
10 // if the line is empty, skip to next line 11 if (values[0].trim().isEmpty()) {
12 continue;
13 }
14 ...
15 // if we are looking at the first trace in the file or to a new one 16 if (traceID == null || !values[0].equals(traceID)) {
17 // update the trace id
19
20 // create a valid XID based on the passed id
21 XID traceXID;
22 try {
23 // transform the id into a XID
24 traceXID = XID.parse(traceID);
25 } catch(RuntimeException e) {
26 // otherwise generate a new random XID
27 traceXID = new XID(UUID.randomUUID());
28 }
29 // if this XID has already been generated for a trace in this log 30 while (traceXIDList.contains(traceXID)) {
31 // get another XID and try to check again
32 traceXID = new XID(UUID.randomUUID());
33 }
34 // add the unique XID to the xidList
35 traceXIDList.add(traceXID);
36 // clear eventXID list because we have a new trace now
37 eventXIDList.clear();
38 // create the id attribute for the trace
39 XAttributeID traceXIDAttribute = xesFactory.createAttributeID("identity:id", traceXID, null);
40 // create the trace attribute map
41 XAttributeMap traceMap = xesFactory.createAttributeMap(); 42 // add the id attribute to the map
43 traceMap.put("identity:id", traceXIDAttribute);
44 // create a new trace, using the attribute map just defined
45 trace = xesFactory.createTrace(traceMap);
46 // add the trace to the log
47 log.add(trace);
48 }
49 // create and event attribute map
50 XAttributeMap eventMap = xesFactory.createAttributeMap();
51 // add to the map the attributes that correspond to the column names 52 eventMap.putAll(attributeMap);
53 // fix a limit based on the number of the values in this line 54 int limit = values.length;
55 // for every column name in the column line 56 for (int i=1; i<columns.length; i++) {
57 // get the attribute that corresponds to this column
58 XAttribute attribute = eventMap.get(columns[i]);
59 // get the value to add to this attribute
60 String value;
61 // if the column index is lower than the value index (for this column there is a value) and if the value is non empty
62 if ((i < limit) && (!values[i].trim().isEmpty())) { 63 // read the value corresponding to this column
64 value = values[i];
65 } else {
66 // remove the attribute from the event map because for this event this attribute is not defined
67 eventMap.remove(columns[i]);
68 // go to next column
69 continue;
70 }
71 boolean error = false;
72 // depending on the attribute type, add the value to this attribute (and if an error occurs, show it)
73 if (attributeinstanceof XAttributeLiteral) {
74 try { 75 ((XAttributeLiteral)attribute).setValue(value); 76 } catch(RuntimeException e) { 77 error = true; 78 } 79 } else if ...
80 // if an error occurred while setting the value
81 if (error) {
82 ....
83 }
84 }
85 // if there are more values than column names, we report this to the user 86 if (columns.length < values.length) {
87 // set a warning
value/s. \n\n";
89 warnings.append(warning);
90 }
91 // create an event using the attribute map just defined 92 XEvent event = xesFactory.createEvent(eventMap); 93 // add the event to its trace
94 trace.add(event);
95 } 96 ...
Per ognuno di questi eventi, il parser aggiunge a questi solo gli attributi per i quali vi è un valore, ignorando gli altri (una riga del tipo "1,AFrancesco" indica che per il terzo attributo non vi è valore per questo evento).
Questi valori vengono prima controllati per vericare che siano conformi al tipo che l'attributo denito dalla colonna richiede: passare il valore "Fran- cesco" nella colonna time:timestamp non è corretto e il parser fermerà la sua esecuzione noticando all'utente l'errore riscontrato.
Ad ogni traccia generata inoltre, il parser imposta un identicatore con- forme allo standard XES a partire da quello passato nel documento: se il valore passato è già conforme allo standard, viene utilizzato quello, altrimen- ti ne viene generato uno a partire da quello passato che viene trasformato in uno XID valido.
Il parser tiene conto di eventuali righe vuote all'interno del documento, saltandole.
Inoltre, gestisce sia il caso di righe contenenti meno valori rispetto al numero di colonne, sia il caso opposto. Per il primo caso, il parser assume come da standard CSV che l'evento denito dalla riga in esame non abbia un valore denito per le colonne in eccesso e quindi non aggiunge tali attributi a
quell'evento, non avendo per questi un valore da utilizzare; nel secondo caso, quando il numero di colonne è minore dei valori indicati nella riga, il parser dapprima ignora i valori in eccesso, in quanto non vi sono colonne che ne deniscono l'attributo a cui appartengono, successivamente, alla generazione del log XES, informerà l'utente con un avviso che alcune righe contenevano più valori di quelli deniti e che questi valori sono stati scartati.
Il procedimento di generazione termina con l'apertura di un EventLogE- ditor contenente il log generato in formato XML.
In Figura 5.4 è mostrato a sinistra un editor contenente l'esempio utiliz- zato in questa sezione, a destra il log da esso generato.