From c215157190f74990b9ad39a4a12d2a70d22374d1 Mon Sep 17 00:00:00 2001 From: NikolajDanger Date: Wed, 4 Jan 2023 17:03:44 +0100 Subject: [PATCH] :sparkles: --- Assignment-2/DCR_graph.py | 218 ++++++++++++++++++ .../__pycache__/DCR_graph.cpython-310.pyc | Bin 0 -> 7637 bytes .../conformance_testing.cpython-310.pyc | Bin 0 -> 825 bytes Assignment-2/__pycache__/log.cpython-310.pyc | Bin 0 -> 380 bytes Assignment-2/conformance_testing.py | 28 +++ Assignment-2/data/DCR-assignment2.xml | 2 - Assignment-2/log.py | 7 + Assignment-2/main.py | 28 +++ 8 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 Assignment-2/DCR_graph.py create mode 100644 Assignment-2/__pycache__/DCR_graph.cpython-310.pyc create mode 100644 Assignment-2/__pycache__/conformance_testing.cpython-310.pyc create mode 100644 Assignment-2/__pycache__/log.cpython-310.pyc create mode 100644 Assignment-2/conformance_testing.py create mode 100644 Assignment-2/log.py create mode 100644 Assignment-2/main.py diff --git a/Assignment-2/DCR_graph.py b/Assignment-2/DCR_graph.py new file mode 100644 index 0000000..ad3a8a5 --- /dev/null +++ b/Assignment-2/DCR_graph.py @@ -0,0 +1,218 @@ +from __future__ import annotations +from enum import Enum + +import xmltodict + +def listify(element): + if isinstance(element, list): + return element + return [element] + +class Event(): + def __init__(self, _id:str, name:str, id_dict:dict, parent:Process=None) -> None: + self._id = _id + id_dict[_id] = self + self.name = name.lower() + self.pending = False + self.executed = False + self.included = False + self.relations_to : list[Relationship] = [] + self.relations_from : list[Relationship] = [] + self.parent = parent + + def execute(self): + self.executed = True + self.pending = False + for relationship in self.relations_from: + relationship.execute() + + @property + def enabled(self): + if self.parent is not None: + included = self.included and self.parent.enabled + else: + included = self.included + + no_conditions = all( + condition.source.executed or not condition.source.included + for condition in [ + relation + for relation in self.relations_to + if relation.type == RelationsshipType.condition + ] + ) + no_milestones = all( + not milestone.source.pending or not milestone.source.included + for milestone in [ + relation + for relation in self.relations_to + if relation.type == RelationsshipType.milestone + ] + ) + + return included and no_conditions and no_milestones + + def enabled_list(self): + if self.included: + return [self] + else: + return [] + + def __repr__(self) -> str: + return self.name + + def pending_list(self): + if self.pending and self.included: + return [self] + else: + return [] + +class Process(Event): + def __init__(self, _id:str, name:str, label_mappings: dict, events: list, id_dict: dict,parent:Process=None) -> None: + super().__init__(_id,name,id_dict,parent) + self.process_process(label_mappings, events, id_dict) + + def process_process(self, label_mappings: dict, events: list, id_dict): + self.events = [] + for event in events: + _id = event["@id"] + label = label_mappings[_id] + if "@type" in event: + new_event = Process(_id, label, label_mappings, listify(event["event"]), id_dict, self) + else: + new_event = Event(_id, label, id_dict, self) + + self.events.append(new_event) + + def enabled_list(self): + if self.enabled: + enabled_events = [self] if self._id != "" else [] + for event in self.events: + enabled_events += event.enabled_list() + else: + enabled_events = [] + + return enabled_events + + def pending_list(self): + pending_events = [] + if self.pending and self.included: + pending_events.append(self) + + for event in self.events: + pending_events += event.pending_list() + + return pending_events + +class RelationsshipType(Enum): + condition = 0 + response = 1 + coresponse = 2 + exclude = 3 + include = 4 + milestone = 5 + update = 6 + spawn = 7 + templateSpawn = 8 + +class Relationship(): + def __init__(self, source:Event, target:Event, type) -> None: + self.source = source + self.target = target + self.type = type + + def execute(self): + if self.type == RelationsshipType.condition: + pass # does nothing + elif self.type == RelationsshipType.response: + self.target.pending = True + elif self.type == RelationsshipType.coresponse: + pass # Don't know what this one does + elif self.type == RelationsshipType.exclude: + self.target.included = False + elif self.type == RelationsshipType.include: + self.target.included = True + elif self.type == RelationsshipType.milestone: + pass # does nothing + elif self.type == RelationsshipType.update: + pass + elif self.type == RelationsshipType.spawn: + pass + elif self.type == RelationsshipType.templateSpawn: + pass + +class Graph(): + def __init__(self, process:Process, relationships:list[Relationship], id_dict: dict) -> None: + self.process = process + self.relationships = relationships + self.id_dict = id_dict + + @property + def enabled(self): + return self.process.enabled_list() + + @property + def pending(self): + return self.process.pending_list() + +def xml_to_dcr(xml_file): + with open(xml_file) as file_pointer: + dcr_dict = xmltodict.parse(file_pointer.read())["dcrgraph"] + + label_mappings = { + lm["@eventId"]:lm["@labelId"] + for lm in listify(dcr_dict["specification"]["resources"]["labelMappings"]["labelMapping"]) + } + + id_dict: dict[str,Event] = {} + graph = Process("", "", label_mappings, listify(dcr_dict["specification"]["resources"]["events"]["event"]), id_dict) + graph.included = True + + def extract_markings(key): + return [ + _id["@id"] + for _id in listify(dcr_dict["runtime"]["marking"][key]["event"]) + ] if dcr_dict["runtime"]["marking"][key] is not None else [] + + executed = extract_markings("executed") + for _id in executed: + id_dict[_id].executed = True + + included = extract_markings("included") + for _id in included: + id_dict[_id].included = True + + pending = extract_markings("pendingResponses") + for _id in pending: + id_dict[_id].pending = True + + + def extract_relationships(key): + return [ + (r["@sourceId"], r["@targetId"]) + for r in listify(dcr_dict["specification"]["constraints"][key][key[:-1]]) + ] if dcr_dict["specification"]["constraints"][key] is not None else [] + + conditions = extract_relationships("conditions") + responses = extract_relationships("responses") + coresponses = extract_relationships("coresponses") + excludes = extract_relationships("excludes") + includes = extract_relationships("includes") + milestones = extract_relationships("milestones") + updates = extract_relationships("updates") + spawns = extract_relationships("spawns") + templateSpawns = extract_relationships("templateSpawns") + + relationships: list[Relationship] = [] + + for i, relationship_list in enumerate([conditions,responses,coresponses,excludes,includes,milestones,updates,spawns,templateSpawns]): + for relationship in relationship_list: + source = id_dict[relationship[0]] + target = id_dict[relationship[1]] + relationships.append(Relationship(source,target, RelationsshipType(i))) + + for relationship in relationships: + relationship.source.relations_from.append(relationship) + relationship.target.relations_to.append(relationship) + + return Graph(graph, relationship, id_dict) diff --git a/Assignment-2/__pycache__/DCR_graph.cpython-310.pyc b/Assignment-2/__pycache__/DCR_graph.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c6ee3259101eefe7e2479fec129eb6a92b130d8 GIT binary patch literal 7637 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 literal 0 HcmV?d00001 diff --git a/Assignment-2/__pycache__/conformance_testing.cpython-310.pyc b/Assignment-2/__pycache__/conformance_testing.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..157a03aabe3ed07e48222a246a1e135359cc7d5b GIT binary patch literal 825 zcmZ8fy>8S%5T0GH_kKtqMI=Bj5Xu^);peWzFD=p+6J_5^RLl&3h={$Wn&0lqMPR! z1PGGCnBbeHH1ozD0pUrKFu|mE!h|pQH5>bajG)u_i5YOvA=ZDb!|AE@QZ=zH5$Ry! zaD?Ul8^@K0K14SK20;Y9hLR*OCkxnxkEEg{Bvg2UeIgZb=2l_bpxoO$|%`zE-fd=L?={m&dh*Z&CJ ztYe6=>=Cbsqg9g6^qh|13t)#V4>Q@JmUg$SKMT_fseL?OhhJ}drz(@ZJU&xtnDmB9 z$lh*PgeXVc>u>e@JN?bWy`AOdD`VnGp2@t}?Dry-pD3M$c_f2Enj+38-RYHW4@4Xl zk;(_YPzTLi6PPZ`2uH9|MDQh+`aN@dMVQaKanv{|@<@7#nQ-+UzhzB~{ z@@bfh&{#SZwzWGvyt#YYjTSLZVP7Ma`t(xadU$p9<{(qzB9+_jV$>QpVTC@Te*m>Z B&<6kj literal 0 HcmV?d00001 diff --git a/Assignment-2/__pycache__/log.cpython-310.pyc b/Assignment-2/__pycache__/log.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da85df8a490afe1ea0712a5cc36cc31df36d6a6d GIT binary patch literal 380 zcmYjNJx{|h5cN4xNm0wh%$}hHWd$MV$H3A7wk(y&nUIjgQJf-GN;g(EmTqO_2k>XS zGVvEWac&uM(tFS6yYu-@4Tr~!Y`gi6KS+P9(=|@c4MCq0Aw$Sn1jx~OV-ZJqV7~iH z5jKy(%D*|JE@48@XG9IlprvZ$?|cm{Y|?3!YUWgED8_ya>Z_IJ%#WyT@Ct)1)k9*^I(~^3JQ3IrJyTPNaK8Colk)*LG|4jb0^6 f6uP#9JJhFu@vaJm%UoXD9ci2Eu>t=Bboy1m literal 0 HcmV?d00001 diff --git a/Assignment-2/conformance_testing.py b/Assignment-2/conformance_testing.py new file mode 100644 index 0000000..ec91823 --- /dev/null +++ b/Assignment-2/conformance_testing.py @@ -0,0 +1,28 @@ +import pandas as pd + +from DCR_graph import Graph + +def conformance_test(log:pd.DataFrame, dcr_graph:Graph): + all_event_names = { + value.name:value for value in dcr_graph.id_dict.values() + } + + log = log.sort_values(by="Date") + + for _, event in log.iterrows(): + event_name = event.EventName.lower() + if event_name not in all_event_names: + if "_ROW_" not in all_event_names: + return False + + event_name = "_ROW_" + + if not all_event_names[event_name].enabled: + return False + + all_event_names[event_name].execute() + + if dcr_graph.pending != []: + return False + + return True diff --git a/Assignment-2/data/DCR-assignment2.xml b/Assignment-2/data/DCR-assignment2.xml index 9552ff5..52d0350 100644 --- a/Assignment-2/data/DCR-assignment2.xml +++ b/Assignment-2/data/DCR-assignment2.xml @@ -632,8 +632,6 @@ - - diff --git a/Assignment-2/log.py b/Assignment-2/log.py new file mode 100644 index 0000000..11529ec --- /dev/null +++ b/Assignment-2/log.py @@ -0,0 +1,7 @@ +import pandas as pd + +def read_log(log_file): + data = pd.read_csv(log_file, delimiter=";") + grouped = data.groupby(data.ID) + + return grouped \ No newline at end of file diff --git a/Assignment-2/main.py b/Assignment-2/main.py new file mode 100644 index 0000000..f1efa7a --- /dev/null +++ b/Assignment-2/main.py @@ -0,0 +1,28 @@ +""" +Usage: + main.py DCR LOG + +Options: + DCR The DCR graph in xml format + LOG The log in csv format +""" +import copy + +from docopt import docopt + +from DCR_graph import xml_to_dcr +from log import read_log +from conformance_testing import conformance_test + +def main(): + arguments = docopt(__doc__) + graph = xml_to_dcr(arguments["DCR"]) + logs = read_log(arguments["LOG"]) + + tests = [conformance_test(trace[1], copy.deepcopy(graph)) for trace in logs] + print("Success: ", tests.count(True)) + print("Failure: ", tests.count(False)) + + +if __name__ == "__main__": + main()