-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add model processing for event loops
- add models for event loops, event and flag reactions, which query relevant info from an RDF graph - add relevant URIs and namespace - add unittest for event loop models - change SHACL check func to use Dataset
- Loading branch information
Showing
6 changed files
with
233 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# SPDX-License-Identifier: MPL-2.0 | ||
from rdflib import Graph, URIRef | ||
from rdf_utils.models.common import ModelBase | ||
from rdf_utils.namespace import NS_MM_EL | ||
|
||
|
||
URI_EL_TYPE_EVT_LOOP = NS_MM_EL["EventLoop"] | ||
URI_EL_TYPE_EVT = NS_MM_EL["Event"] | ||
URI_EL_TYPE_FLG = NS_MM_EL["Flag"] | ||
URI_EL_TYPE_EVT_REACT = NS_MM_EL["EventReaction"] | ||
URI_EL_TYPE_FLG_REACT = NS_MM_EL["FlagReaction"] | ||
URI_EL_PRED_REF_EVT = NS_MM_EL["ref-event"] | ||
URI_EL_PRED_HAS_EVT = NS_MM_EL["has-event"] | ||
URI_EL_PRED_REF_FLG = NS_MM_EL["ref-flag"] | ||
URI_EL_PRED_HAS_FLG = NS_MM_EL["has-flag"] | ||
URI_EL_PRED_HAS_EVT_REACT = NS_MM_EL["has-evt-reaction"] | ||
URI_EL_PRED_HAS_FLG_REACT = NS_MM_EL["has-flg-reaction"] | ||
|
||
|
||
class EventReactionModel(ModelBase): | ||
event_id: URIRef | ||
|
||
def __init__(self, graph: Graph, reaction_id: URIRef) -> None: | ||
super().__init__(graph=graph, node_id=reaction_id) | ||
|
||
evt_uri = graph.value(subject=self.id, predicate=URI_EL_PRED_REF_EVT) | ||
assert evt_uri is not None and isinstance( | ||
evt_uri, URIRef | ||
), f"EventReaction '{self.id}' does not refer to a valid event URI: {evt_uri}" | ||
self.event_id = evt_uri | ||
|
||
|
||
class FlagReactionModel(ModelBase): | ||
flag_id: URIRef | ||
|
||
def __init__(self, graph: Graph, reaction_id: URIRef) -> None: | ||
super().__init__(graph=graph, node_id=reaction_id) | ||
|
||
flg_uri = graph.value(subject=self.id, predicate=URI_EL_PRED_REF_FLG) | ||
assert flg_uri is not None and isinstance( | ||
flg_uri, URIRef | ||
), f"FlagReaction '{self.id}' does not refer to a valid flag URI: {flg_uri}" | ||
self.flag_id = flg_uri | ||
|
||
|
||
class EventLoopModel(ModelBase): | ||
events_triggered: dict[URIRef, bool] | ||
flag_values: dict[URIRef, bool] | ||
event_reactions: dict[URIRef, EventReactionModel] | ||
flag_reactions: dict[URIRef, FlagReactionModel] | ||
|
||
def __init__(self, graph: Graph, el_id: URIRef) -> None: | ||
super().__init__(graph=graph, node_id=el_id) | ||
|
||
self.events_triggered = {} | ||
self.flag_values = {} | ||
self.event_reactions = {} | ||
self.flag_reactions = {} | ||
|
||
for evt_uri in graph.objects(subject=self.id, predicate=URI_EL_PRED_HAS_EVT): | ||
assert isinstance( | ||
evt_uri, URIRef | ||
), f"Event '{evt_uri}' is not of type URIRef: {type(evt_uri)}" | ||
self.events_triggered[evt_uri] = False | ||
|
||
for flg_uri in graph.objects(subject=self.id, predicate=URI_EL_PRED_HAS_FLG): | ||
assert isinstance( | ||
flg_uri, URIRef | ||
), f"Flag '{flg_uri}' is not of type URIRef: {type(flg_uri)}" | ||
self.flag_values[flg_uri] = False | ||
|
||
for evt_re_uri in graph.objects(subject=self.id, predicate=URI_EL_PRED_HAS_EVT_REACT): | ||
assert isinstance( | ||
evt_re_uri, URIRef | ||
), f"EventReaction '{evt_re_uri}' is not of type URIRef: {type(evt_re_uri)}" | ||
evt_re_model = EventReactionModel(graph=graph, reaction_id=evt_re_uri) | ||
assert ( | ||
evt_re_model.event_id in self.events_triggered | ||
), f"'{evt_re_model.id}' reacts to event '{evt_re_model.event_id}', which is not in event loop '{self.id}'" | ||
self.event_reactions[evt_re_model.event_id] = evt_re_model | ||
|
||
for flg_re_uri in graph.objects(subject=self.id, predicate=URI_EL_PRED_HAS_FLG_REACT): | ||
assert isinstance( | ||
flg_re_uri, URIRef | ||
), f"FlagReaction '{flg_re_uri}' is not of type URIRef: {type(flg_re_uri)}" | ||
flg_re_model = FlagReactionModel(graph=graph, reaction_id=flg_re_uri) | ||
assert ( | ||
flg_re_model.flag_id in self.flag_values | ||
), f"'{flg_re_model.id}' reacts to flag '{flg_re_model.flag_id}', which is not in event loop '{self.id}'" | ||
self.flag_reactions[flg_re_model.flag_id] = flg_re_model |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
# SPDX-License-Identifier: MPL-2.0 | ||
import unittest | ||
from rdflib import Graph, URIRef | ||
from rdf_utils.resolver import install_resolver | ||
from rdf_utils.constraints import check_shacl_constraints | ||
from rdf_utils.uri import URL_MM_EL_JSON, URL_MM_EL_SHACL, URL_SECORO_M | ||
from rdf_utils.models.event_loop import ( | ||
URI_EL_TYPE_EVT_LOOP, | ||
URI_EL_TYPE_EVT, | ||
URI_EL_TYPE_EVT_REACT, | ||
URI_EL_TYPE_FLG, | ||
URI_EL_TYPE_FLG_REACT, | ||
EventLoopModel, | ||
) | ||
|
||
|
||
URI_TEST_EL = f"{URL_SECORO_M}/models/tests/el" | ||
URI_TEST_LOOP = f"{URI_TEST_EL}/test-loop" | ||
URIREF_TEST_LOOP = URIRef(URI_TEST_LOOP) | ||
|
||
EVT_LOOP_MODEL_NODES = f""" | ||
{{ | ||
"@context": [ "{URL_MM_EL_JSON}" ], | ||
"@graph": [ | ||
{{ "@id": "{URI_TEST_EL}/event1", "@type": "{URI_EL_TYPE_EVT.toPython()}" }}, | ||
{{ "@id": "{URI_TEST_EL}/event2", "@type": "{URI_EL_TYPE_EVT.toPython()}" }}, | ||
{{ "@id": "{URI_TEST_EL}/flag1", "@type": "{URI_EL_TYPE_FLG.toPython()}" }}, | ||
{{ "@id": "{URI_TEST_EL}/flag2", "@type": "{URI_EL_TYPE_FLG.toPython()}" }}, | ||
{{ "@id": "{URI_TEST_EL}/evt_reaction", "@type": "{URI_EL_TYPE_EVT_REACT.toPython()}" }}, | ||
{{ "@id": "{URI_TEST_EL}/flg_reaction", "@type": "{URI_EL_TYPE_FLG_REACT.toPython()}" }}, | ||
{{ "@id": "{URI_TEST_LOOP}", "@type": "{URI_EL_TYPE_EVT_LOOP.toPython()}" }} | ||
] | ||
}} | ||
""" | ||
EVT_LOOP_MODEL_CORRECT_COMP = f""" | ||
{{ | ||
"@context": [ "{URL_MM_EL_JSON}" ], | ||
"@graph": [ | ||
{{ | ||
"@id": "{URI_TEST_EL}/evt_reaction", "@type": "{URI_EL_TYPE_EVT_REACT.toPython()}", | ||
"ref-event" : "{URI_TEST_EL}/event1" | ||
}}, | ||
{{ | ||
"@id": "{URI_TEST_EL}/flg_reaction", "@type": "{URI_EL_TYPE_FLG_REACT.toPython()}", | ||
"ref-flag" : "{URI_TEST_EL}/flag1" | ||
}}, | ||
{{ | ||
"@id": "{URI_TEST_LOOP}", "@type": "{URI_EL_TYPE_EVT_LOOP.toPython()}", | ||
"has-event": [ "{URI_TEST_EL}/event1", "{URI_TEST_EL}/event2" ], | ||
"has-evt-reaction": "{URI_TEST_EL}/evt_reaction", | ||
"has-flag": [ "{URI_TEST_EL}/flag1", "{URI_TEST_EL}/flag2" ], | ||
"has-flg-reaction": "{URI_TEST_EL}/flg_reaction" | ||
}} | ||
] | ||
}} | ||
""" | ||
EVT_LOOP_MODEL_WRONG_EVT = f""" | ||
{{ | ||
"@context": [ "{URL_MM_EL_JSON}" ], | ||
"@graph": [ | ||
{{ | ||
"@id": "{URI_TEST_EL}/evt_reaction", "@type": "{URI_EL_TYPE_EVT_REACT.toPython()}", | ||
"ref-event" : "{URI_TEST_EL}/event1" | ||
}}, | ||
{{ | ||
"@id": "{URI_TEST_LOOP}", "@type": "{URI_EL_TYPE_EVT_LOOP.toPython()}", | ||
"has-event": [ "{URI_TEST_EL}/event2" ], | ||
"has-evt-reaction": "{URI_TEST_EL}/evt_reaction" | ||
}} | ||
] | ||
}} | ||
""" | ||
EVT_LOOP_MODEL_WRONG_FLG = f""" | ||
{{ | ||
"@context": [ "{URL_MM_EL_JSON}" ], | ||
"@graph": [ | ||
{{ | ||
"@id": "{URI_TEST_EL}/flg_reaction", "@type": "{URI_EL_TYPE_FLG_REACT.toPython()}", | ||
"ref-flag" : "{URI_TEST_EL}/flag1" | ||
}}, | ||
{{ | ||
"@id": "{URI_TEST_LOOP}", "@type": "{URI_EL_TYPE_EVT_LOOP.toPython()}", | ||
"has-flag": [ "{URI_TEST_EL}/flag2" ], | ||
"has-flg-reaction": "{URI_TEST_EL}/flg_reaction" | ||
}} | ||
] | ||
}} | ||
""" | ||
|
||
|
||
class EventLoopModelTest(unittest.TestCase): | ||
def setUp(self): | ||
install_resolver() | ||
|
||
def test_correct_el_model(self): | ||
graph = Graph() | ||
graph.parse(data=EVT_LOOP_MODEL_NODES, format="json-ld") | ||
|
||
self.assertFalse( | ||
check_shacl_constraints( | ||
graph=graph, shacl_dict={URL_MM_EL_SHACL: "turtle"}, quiet=True | ||
), | ||
"SHACL violation not raised for missing refs from reactions to events and flags", | ||
) | ||
|
||
graph.parse(data=EVT_LOOP_MODEL_CORRECT_COMP, format="json-ld") | ||
|
||
self.assertTrue( | ||
check_shacl_constraints(graph=graph, shacl_dict={URL_MM_EL_SHACL: "turtle"}) | ||
) | ||
|
||
_ = EventLoopModel(graph=graph, el_id=URIREF_TEST_LOOP) | ||
|
||
def test_wrong_reactions(self): | ||
wrong_evt_g = Graph() | ||
wrong_evt_g.parse(data=EVT_LOOP_MODEL_WRONG_EVT, format="json-ld") | ||
with self.assertRaises( | ||
AssertionError, msg="not raised for reaction to an event not in loop" | ||
): | ||
_ = EventLoopModel(graph=wrong_evt_g, el_id=URIREF_TEST_LOOP) | ||
wrong_flg_g = Graph() | ||
wrong_flg_g.parse(data=EVT_LOOP_MODEL_WRONG_FLG, format="json-ld") | ||
with self.assertRaises(AssertionError, msg="not raised for reaction to a flag not in loop"): | ||
_ = EventLoopModel(graph=wrong_flg_g, el_id=URIREF_TEST_LOOP) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters