def get_traces_from_log(log): classifier = XEventNameClassifier() traces = list() for trace in log: decoded_trace = list() for event in trace: decoded_trace.append(classifier.get_class_identity(event)) traces.append(decoded_trace) return traces
def log(): log = XFactory.create_log() # add log classifier clf = XEventNameClassifier() log.get_classifiers().append(clf) # add global trace attributes glb_t_attr = XFactory.create_attribute_discrete('glb_t_attr', 0) log.get_global_trace_attributes().append(glb_t_attr) # add global event attributes glb_e_attr = XFactory.create_attribute_discrete('glb_e_attr', 0) log.get_global_event_attributes().append(glb_e_attr) # add log attributes str_attr = XFactory.create_attribute_literal('l_attr', 'UNKNOWN') log.get_attributes()['l_attr'] = str_attr # add extension meta_concept = XExtensionParser().parse( "http://www.xes-standard.org/meta_concept.xesext") log.get_extensions().add(meta_concept) # add a trace tracelen = 2 trace0 = XFactory.create_trace() # add some trace attributes bool_attr = XFactory.create_attribute_boolean('t_attr', True) # add some trace features trace0.get_attributes()['t_attr'] = bool_attr for i in range(tracelen): event = XFactory.create_event() # add an attribute int_attr = XFactory.create_attribute_discrete('e_attr', 0) event.get_attributes()['e_attr0'] = int_attr trace0.append(event) log.append(trace0) return log
def create_event_int_mapping(log): classifier = XEventNameClassifier() event_name_list = [] for trace in log[0]: for event in trace: event_name = classifier.get_class_identity(event) if not str(event_name) in event_name_list: event_name_list.append(event_name) event_int_mapping = {} event_int_mapping[TRACE_START] = 0 current_int = 1 for event_name in event_name_list: event_int_mapping[event_name] = current_int current_int = current_int + 1 event_int_mapping[TRACE_END] = current_int return event_int_mapping
def get_df_frequencies(log, event_int_mapping): classifier = XEventNameClassifier() df_relations = np.zeros((len(event_int_mapping),len(event_int_mapping)), dtype=int) for trace in log[0]: current_event = TRACE_START for event in trace: next_event = classifier.get_class_identity(event) current_event_int = event_int_mapping[current_event] next_event_int = event_int_mapping[next_event] df_relations[current_event_int, next_event_int] += 1 current_event = next_event current_event_int = event_int_mapping[current_event] next_event = TRACE_END next_event_int = event_int_mapping[next_event] df_relations[current_event_int, next_event_int] += 1 return df_relations
def get_prefix_frequencies_from_log(log): classifier = XEventNameClassifier() prefix_frequencies = {} for trace in log[0]: current_prefix = "" for event in trace: current_prefix = current_prefix + classifier.get_class_identity( event) + EVENT_DELIMETER if current_prefix in prefix_frequencies: prefix_frequencies[current_prefix] += 1 else: prefix_frequencies[current_prefix] = 1 current_prefix = current_prefix + TRACE_END if current_prefix in prefix_frequencies: prefix_frequencies[current_prefix] += 1 else: prefix_frequencies[current_prefix] = 1 return prefix_frequencies
class XMxmlParser: """Parser for the MXML format for event logs (deprecated). :param factory: The factory to use for XES model building. :type factory: XFactory """ MXML_CLASSIFIERS = [ XEventAttributeClassifier("MXML Legacy Classifier", ["concept:name", "lifecycle:transition"]), XEventNameClassifier(), XEventResourceClassifier() ] def __init__(self, factory=None): if factory: self.factory = factory else: self.factory = XFactoryRegistry().current_default() def can_parse(self, file): """Checks whether this parser can handle the given file. :param file: path of the file to check against parser. :type file: str :return: Whether this parser can handle the given file. :rtype: bool """ return self.ends_with_ignore_case( file, ".mxml") or self.ends_with_ignore_case(file, ".xml") def parse(self, file): """Parses a set of logs from the given input stream, which is supposed to deliver an MXML serialization. :param file: file generated by the function 'open(path)', which is supposed to deliver an MXML serialization. :type file: _io.TextIOWrapper :return: The parsed list of logs. :rtype: list[XLog] """ handler = XMxmlParser.MxmlHandler() xml_parse(file, handler) return handler.get_logs() @staticmethod def ends_with_ignore_case(name, suffix): """Returns whether the given file name ends (ignoring the case) with the given suffix. :param name: The given file name. :type name: str :param suffix: The given suffix. :type suffix: str :return: Whether the given file name ends (ignoring the case) with the given suffix. :rtype: bool """ i = len(name) - len(suffix) if i > 0: return suffix in name[i:] return False class MxmlHandler(ContentHandler): """SAX handler class for XES in XML representation. """ def __init__(self): super().__init__() self.__buffer = list() self.__logs = list() self.__currentProcess = None self.__currentInstance = None self.__entry = None self.__sourceAttribute = None self.__genericAttribute = None self.__eventTypeAttribute = None self.__originatorAttribute = None self.__sourceOpen = False self.__timestamp = None self.__lastTimestamp = None self.__numUnorderedEntries = 0 def get_logs(self): """Retrieves the parsed list of logs. :return: The parsed list of logs. :rtype: list[XLog] """ return self.__logs def startElement(self, element_name, attributes): """ Overrides startElement in class ContentHandler :param element_name: Contains the raw XML 1.0 name of the element type. :type element_name: str :param attributes: An instance of the Attributes class containing the attributes of the element :type attributes: xml.sax.xmlreader.AttributesImpl """ tag_name = element_name if tag_name != "WorkflowLog": if tag_name == "Source": self.__sourceOpen = True description_string = attributes.get("program") self.__sourceAttribute = XMxmlParser( ).factory.create_attribute_literal("source", description_string, None) self.__add_model_reference__(attributes, self.__sourceAttribute) elif tag_name == "Process": description_string = attributes.get("id") description = attributes.get("description") self.__currentProcess = XMxmlParser().factory.create_log() self.__currentProcess.get_extensions().add( XConceptExtension()) self.__currentProcess.get_extensions().add( XOrganizationalExtension()) self.__currentProcess.get_extensions().add( XLifecycleExtension()) self.__currentProcess.get_extensions().add( XSemanticExtension()) self.__currentProcess.get_extensions().add( XTimeExtension()) if self.__sourceAttribute: self.__currentProcess.get_attributes()[ self.__sourceAttribute.get_key( )] = self.__sourceAttribute XConceptExtension().assign_name(self.__currentProcess, description_string) XLifecycleExtension().assign_model(self.__currentProcess, "standard") if description and len(description.lower()) > 0: description1 = XMxmlParser( ).factory.create_attribute_literal( "description", description, None) self.__currentProcess.get_attributes()[ description1.get_key()] = description1 self.__add_model_reference__(attributes, self.__currentProcess) elif tag_name == "ProcessInstance": self.__currentInstance = XMxmlParser( ).factory.create_trace() name = attributes.get("id") if name is None: name = "None" XConceptExtension().assign_name(self.__currentInstance, name) description_string = attributes.get("description") if description_string and len( description_string.strip()) > 0: description2 = XMxmlParser( ).factory.create_attribute_literal( "description", description_string, None) self.__currentInstance.get_attributes()[ description2.get_key()] = description2 self.__add_model_reference__(attributes, self.__currentInstance) elif tag_name == "AuditTrailEntry": self.__entry = XMxmlParser().factory.create_event() elif tag_name == "Attribute": self.__genericAttribute = XMxmlParser( ).factory.create_attribute_literal( attributes.get("name").strip(), "DEFAULT_VALUE", None) self.__add_model_reference__(attributes, self.__genericAttribute) elif tag_name == "EventType": self.__eventTypeAttribute = XLifecycleExtension( ).ATTR_TRANSITION.clone() unknown_type = attributes.get("unknowntype") if unknown_type: self.__eventTypeAttribute.set_value(unknown_type) else: self.__eventTypeAttribute.set_value("UNKNOWN") self.__add_model_reference__(attributes, self.__eventTypeAttribute) elif tag_name == "WorkflowModelElement": self.__add_model_reference__(attributes, self.__entry) elif tag_name == "Originator": self.__originatorAttribute = XOrganizationalExtension( ).ATTR_RESOURCE.clone() self.__add_model_reference__(attributes, self.__originatorAttribute) def endElement(self, local_name): """ Overrides endElement in class ContentHandler :param local_name: The name of the element type, just as with the startElement event :type local_name: str """ tag_name = local_name if tag_name == "WorkflowLog": if self.__numUnorderedEntries > 0: XLogging().log( "LogData: Log contains " + str(self.__numUnorderedEntries) + " audit trail entries in non-natural order!", XLogging.Importance.ERROR) XLogging().log( "LogData: The log file you have loaded is not MXML compliant! (error compensated transparently)", XLogging.Importance.ERROR) elif tag_name == "Process": self.__currentProcess.get_classifiers().extend( XMxmlParser.MXML_CLASSIFIERS) self.__currentProcess.get_global_trace_attributes().append( XConceptExtension().ATTR_NAME.clone()) self.__currentProcess.get_global_event_attributes().append( XConceptExtension().ATTR_NAME.clone()) self.__currentProcess.get_global_event_attributes().append( XLifecycleExtension().ATTR_TRANSITION.clone()) self.__logs.append(self.__currentProcess) self.__currentProcess = None elif tag_name == "Process": self.__sourceOpen = False elif tag_name == "ProcessInstance": if len(self.__currentInstance) > 0: self.__currentProcess.append(self.__currentInstance) self.__currentInstance = None self.__lastTimestamp = None elif tag_name == "AuditTrailEntry": if self.__timestamp is None: self.__currentInstance.append(self.__entry) elif self.__lastTimestamp is None: self.__currentInstance.append(self.__entry) self.__lastTimestamp = self.__timestamp elif self.__timestamp > self.__lastTimestamp: self.__currentInstance.append(self.__entry) self.__lastTimestamp = self.__timestamp else: self.__currentInstance.append(self.__entry) self.__entry = None else: if tag_name == "Attribute": originator = "".join(self.__buffer).strip() if len(originator) > 0: self.__genericAttribute.set_value("".join( self.__buffer).strip()) if self.__entry: self.__entry.get_attributes()[ self.__genericAttribute.get_key( )] = self.__genericAttribute elif self.__currentInstance: self.__currentInstance.get_attributes()[ self.__genericAttribute.get_key( )] = self.__genericAttribute elif self.__currentProcess: self.__currentProcess.get_attributes()[ self.__genericAttribute.get_key( )] = self.__genericAttribute elif self.__sourceOpen: self.__sourceAttribute.get_attributes()[ self.__genericAttribute.get_key( )] = self.__genericAttribute self.__genericAttribute = None elif tag_name == "EventType": if self.__eventTypeAttribute.get_value() == "UNKNOWN": originator = "".join(self.__buffer).strip() if len(originator) > 0: self.__eventTypeAttribute.set_value(originator) self.__entry.get_attributes()[ self.__eventTypeAttribute.get_key( )] = self.__eventTypeAttribute else: self.__entry.get_attributes()[ self.__eventTypeAttribute.get_key( )] = self.__eventTypeAttribute self.__eventTypeAttribute = None elif tag_name == "WorkflowModelElement": XConceptExtension().assign_name( self.__entry, "".join(self.__buffer).strip()) elif tag_name == "Timestamp": originator = "".join(self.__buffer).strip() self.__timestamp = parse_date_time(originator) if self.__timestamp: timestamp_attribute = XTimeExtension( ).ATTR_TIMESTAMP.clone() timestamp_attribute.set_value(self.__timestamp) self.__entry.get_attributes( )[timestamp_attribute.get_key()] = timestamp_attribute elif tag_name == "Originator": originator = "".join(self.__buffer).strip() if len(originator) > 0: self.__originatorAttribute.set_value(originator) self.__entry.get_attributes()[ self.__originatorAttribute.get_key( )] = self.__originatorAttribute self.__originatorAttribute = None self.__buffer.clear() @staticmethod def __add_model_reference__(attrib, subject): try: model_reference = attrib.getValue("modelReference") except KeyError: model_reference = None if model_reference: attribute = XSemanticExtension().ATTR_MODELREFERENCE.clone() attribute.set_value(model_reference) subject.get_attributes()[attribute.get_key()] = attribute def ignorableWhitespace(self, whitespace): """ Overrides ignorableWhitespace in class ContentHandler :param whitespace: The whitespace characters. :type whitespace: str """ self.__buffer.append(whitespace) def characters(self, content): """ Overrides characters in class ContentHandler :param content: The characters. :type content: str """ self.__buffer.append(content)
class XLogInfo: """This class implements a bare-bones log info summary which can be created on demand by using applications. The log info summary is based on an event classifier, which is used to identify event class abstractions. :param log: The event log to create an info summary for. :type log: XLog :param default_classifier: The default event classifier to be used :type default_classifier: XEventAttributeClassifier :param classifiers: A collection of additional event classifiers to be covered by the created log info instance. :type classifiers: list[XEventAttributeClassifier] """ STANDARD_CLASSIFIER = XEventAttributeClassifier( "MXML Legacy Classifier", ["concept:name", "lifecycle:transition"]) NAME_CLASSIFIER = XEventNameClassifier() RESOURCE_CLASSIFIER = XEventResourceClassifier() LIFECYCLE_TRANSITION_CLASSIFIER = XEventLifeTransClassifier() @staticmethod def create(log, default_classifier=None, classifiers=None): """Creates a new log info summary with the standard event classifier. :param log: The event log to create an info summary for. :type log: XLog :param default_classifier: The default event classifier to be used :type default_classifier: XEventAttributeClassifier :param classifiers: A collection of additional event classifiers to be covered by the created log info instance. :type classifiers: list[XEventAttributeClassifier] :return: The log info summary for this log. :rtype: XLogInfo """ if default_classifier is None: return XLogInfo(log, XLogInfo.STANDARD_CLASSIFIER, classifiers) return XLogInfo(log, default_classifier, classifiers) def __init__(self, log, default_classifier, classifiers): self.__log = log self.__default_classifier = default_classifier if classifiers is None: classifiers = list() self.__event_classes = dict() for classifier in classifiers: self.__event_classes[classifier] = XEventClasses(classifier) self.__event_classes[self.__default_classifier] = XEventClasses( self.__default_classifier) self.__event_classes[self.NAME_CLASSIFIER] = XEventClasses( self.NAME_CLASSIFIER) self.__event_classes[self.RESOURCE_CLASSIFIER] = XEventClasses( self.RESOURCE_CLASSIFIER) self.__event_classes[ self.LIFECYCLE_TRANSITION_CLASSIFIER] = XEventClasses( self.LIFECYCLE_TRANSITION_CLASSIFIER) self.__number_of_events = 0 self.__number_of_traces = 0 self.__log_boundaries = XTimeBounds() self.__trace_boundaries = dict() self.__log_attribute_info = XAttributeInfo() self.__trace_attribute_info = XAttributeInfo() self.__event_attribute_info = XAttributeInfo() self.__meta_attribute_info = XAttributeInfo() self.setup() def setup(self): """Creates the internal data structures of this summary on setup from the log. """ self.register_attributes(self.__log_attribute_info, self.__log) for trace in self.__log: self.__number_of_traces += 1 self.register_attributes(self.__trace_attribute_info, trace) trace_bounds = XTimeBounds() for event in trace: self.__number_of_events += 1 self.register_attributes(self.__event_attribute_info, event) for classes in self.__event_classes.values(): classes.register(event) trace_bounds.register(event) self.__trace_boundaries[trace] = trace_bounds self.__log_boundaries.register(trace_bounds) for classes in self.__event_classes.values(): classes.harmonize_indices() def register_attributes(self, attribute_info, attributable): """Registers all attributes of a given attributable, i.e. model type hierarchy element, in the given attribute info registry. :param attribute_info: Attribute info registry to use for registration. :type attribute_info: XAttributeInfo :param attributable: Attributable whose attributes to register. :type attributable: XAttributable """ if attributable.has_attributes(): for attribute in attributable.get_attributes().values(): attribute_info.register(attribute) self.register_attributes(self.__meta_attribute_info, attribute) def get_log(self): """Retrieves the log used for this summary. :return: The event log which this summary describes. :rtype: XLog """ return self.__log def get_number_of_event(self): """Retrieves the total number of events in this log. :return: Total number of events. :rtype: int """ return self.__number_of_events def get_number_of_traces(self): """Retrieves the number of traces in this log. :return: Number of traces available in this log. :rtype: int """ return self.__number_of_traces def get_event_classes(self, classifier=None): """Retrieves the event classes for a given classifier. *Note:* The given event classifier must be covered by this log info, i.e., the log info must have been created with this classifier. Otherwise, this method will return null. You can retrieve the collection of event classifiers covered by this log info instance by calling the method getEventClassifiers(). :param classifier: The classifier for which to retrieve the event classes. :type classifier: XEventAttributeClassifier or None :return: The requested event classes, or null if the given event classifier is not covered by this log info instance. :rtype: XEventClasses """ if classifier is None: return self.__event_classes.get(self.__default_classifier) return self.__event_classes.get(classifier) def get_event_classifiers(self): """Retrieves the set of event classifiers covered by this log info, i.e., for which event classes are registered in this log info instance. :return: The tuple of event classifiers covered by this log info instance. :rtype: tuple """ return set(self.__event_classes.keys()) def get_resource_classes(self): """Retrieves the resource classes of the summarized log. :return: The resource classes of the summarized log. :rtype: XEventClasses """ return self.__event_classes.get(self.RESOURCE_CLASSIFIER) def get_name_classes(self): """Retrieves the event name classes of the summarized log. :return: The event name classes of the summarized log. :rtype: XEventClasses """ return self.__event_classes.get(self.NAME_CLASSIFIER) def get_transition_classes(self): """Retrieves the lifecycle transition classes of the summarized log. :return: The lifecycle transition classes of the summarized log. :rtype: XEventClasses """ return self.__event_classes.get(self.LIFECYCLE_TRANSITION_CLASSIFIER) def get_log_time_boundaries(self): """Retrieves the global timestamp boundaries of this log. :return: Timestamp boundaries for the complete log. :rtype XTimeBounds """ return self.__log_boundaries def get_trace_time_boundaries(self, trace): """ Retrieves the timestamp boundaries for a specified trace. :param trace: Trace to be queried for. :return: Timestamp boundaries for the indicated trace. :rtype: XTimeBounds """ return self.__trace_boundaries.get(trace) def get_log_attribute_info(self): """Retrieves attribute information about all attributes this log contains on the log level. :return: Attribute information on the log level. :rtype: XAttributeInfo """ return self.__log_attribute_info def get_trace_attribute_info(self): """Retrieves attribute information about all attributes this log contains on the trace level. :return: Attribute information on the trace level. :rtype: XAttributeInfo """ return self.__trace_attribute_info def get_event_attribute_info(self): """Retrieves attribute information about all attributes this log contains on the event level. :return: Attribute information on the event level. :rtype: XAttributeInfo """ return self.__event_attribute_info def get_meta_attribute_info(self): """Retrieves attribute information about all attributes this log contains on the meta (i.e., attribute) level. :return: Attribute information on the meta level. :rtype: XAttributeInfo """ return self.__meta_attribute_info
['3', 'Archive order', 1.0, 'complete', 'sales', dt(year=2017, month=3, day=17, hour=8, minute=15, second=0)] ] EVENT_DF = pd.DataFrame(EVENTS, columns=EVENT_DF_COLUMNS) TRACE_DF_COLUMNS = [const.CONCEPT_NAME, const.COST_TOTAL] TRACES = [ ['1', 100.0], ['2', 1300.0], ['3', 1400.0] ] TRACE_DF = pd.DataFrame(TRACES, columns=TRACE_DF_COLUMNS) NAME_AND_LIFECYCLE_CLF = XEventAndClassifier([XEventNameClassifier(), XEventLifeTransClassifier()]) CLASSIFIERS = { XEventNameClassifier().name(): [const.CONCEPT_NAME], NAME_AND_LIFECYCLE_CLF.name(): [const.CONCEPT_NAME, const.LIFECYCLE_TRANS] } LOG_TABLE = LogTable(event_df=EVENT_DF, trace_df=TRACE_DF, attributes=LOG_ATTRIBUTE_DICT, classifiers=CLASSIFIERS) XLOG = XFactory.create_log() XLOG_NAME = 'Test log' CONCEPT_EXTENSION.assign_name(XLOG, XLOG_NAME) TOTAL_TIME = 100 TOTAL_TIME_ATTRIBUTE = XFactory.create_attribute_continuous('total_time', TOTAL_TIME) XLOG.get_attributes()['total_time'] = TOTAL_TIME_ATTRIBUTE