diff --git a/ontology/uco/action/action.ttl b/ontology/uco/action/action.ttl
index cb633135..dabd1226 100644
--- a/ontology/uco/action/action.ttl
+++ b/ontology/uco/action/action.ttl
@@ -1,4 +1,4 @@
-# imports: https://ontology.unifiedcyberontology.org/uco/core/1.0.0
+# imports: https://ontology.unifiedcyberontology.org/uco/identity/1.0.0
# imports: https://ontology.unifiedcyberontology.org/uco/location/1.0.0
# imports: https://ontology.unifiedcyberontology.org/uco/pattern/1.0.0
# imports: https://ontology.unifiedcyberontology.org/uco/types/1.0.0
@@ -6,6 +6,7 @@
@prefix action: .
@prefix core: .
+@prefix identity: .
@prefix location: .
@prefix owl: .
@prefix pattern: .
@@ -21,7 +22,7 @@
rdfs:label "uco-action"@en ;
rdfs:comment "This ontology defines classes and properties for characterizing actions."@en-US ;
owl:imports
- core:1.0.0 ,
+ identity:1.0.0 ,
location:1.0.0 ,
pattern:1.0.0 ,
types:1.0.0 ,
@@ -41,6 +42,22 @@ action:Action
rdfs:subClassOf core:UcoObject ;
rdfs:label "Action"@en ;
rdfs:comment "An action is something that may be done or performed."@en ;
+ sh:and (
+ [
+ a sh:PropertyShape ;
+ sh:class identity:Person ;
+ sh:path action:performer ;
+ ]
+ [
+ a sh:NodeShape ;
+ rdfs:seeAlso ;
+ sh:not [
+ a sh:PropertyShape ;
+ sh:hasValue "true"^^xsd:boolean ;
+ sh:path action:isAutomated ;
+ ] ;
+ ]
+ ) ;
sh:property
[
sh:class action:Action ;
@@ -89,6 +106,12 @@ action:Action
sh:nodeKind sh:IRI ;
sh:path action:location ;
] ,
+ [
+ sh:datatype xsd:boolean ;
+ sh:maxCount "1"^^xsd:integer ;
+ sh:nodeKind sh:Literal ;
+ sh:path action:isAutomated ;
+ ] ,
[
sh:datatype xsd:dateTime ;
sh:maxCount "1"^^xsd:integer ;
@@ -447,6 +470,13 @@ action:instrument
rdfs:range core:UcoObject ;
.
+action:isAutomated
+ a owl:DatatypeProperty ;
+ rdfs:label "isAutomated"@en ;
+ rdfs:comment "An assertion of whether or not the action is automated."@en ;
+ rdfs:range xsd:boolean ;
+ .
+
action:location
a owl:ObjectProperty ;
rdfs:label "location"@en ;
diff --git a/tests/examples/Makefile b/tests/examples/Makefile
index 7e79050d..71a897b3 100644
--- a/tests/examples/Makefile
+++ b/tests/examples/Makefile
@@ -29,6 +29,7 @@ all: \
has_facet_inverse_functional_XFAIL_validation.ttl \
hash_PASS_validation.ttl \
hash_XFAIL_validation.ttl \
+ isAutomated_XFAIL_validation.ttl \
location_PASS_validation.ttl \
location_XFAIL_validation.ttl \
message_thread_PASS_validation.ttl \
@@ -95,6 +96,7 @@ check: \
has_facet_inverse_functional_XFAIL_validation.ttl \
hash_PASS_validation.ttl \
hash_XFAIL_validation.ttl \
+ isAutomated_XFAIL_validation.ttl \
location_PASS_validation.ttl \
location_XFAIL_validation.ttl \
message_thread_PASS_validation.ttl \
diff --git a/tests/examples/isAutomated_XFAIL.json b/tests/examples/isAutomated_XFAIL.json
new file mode 100644
index 00000000..9c8d84e2
--- /dev/null
+++ b/tests/examples/isAutomated_XFAIL.json
@@ -0,0 +1,18 @@
+{
+ "@context": {
+ "action": "https://ontology.unifiedcyberontology.org/uco/action/",
+ "identity": "https://ontology.unifiedcyberontology.org/uco/identity/",
+ "kb": "http://example.org/kb/",
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#"
+ },
+ "@graph": {
+ "@id": "kb:person-automated-action-c51e0065-ba55-4177-bc08-e71963c22c26",
+ "@type": "action:Action",
+ "rdfs:comment": "This will fail SHACL validation because an automated action is required to not have a human performer.",
+ "action:isAutomated": true,
+ "action:performer": {
+ "@id": "kb:person-c6c7ccc6-2b6c-41e0-ad74-d895bd209ac8",
+ "@type": "identity:Person"
+ }
+ }
+}
diff --git a/tests/examples/isAutomated_XFAIL_validation.ttl b/tests/examples/isAutomated_XFAIL_validation.ttl
new file mode 100644
index 00000000..e41802a4
--- /dev/null
+++ b/tests/examples/isAutomated_XFAIL_validation.ttl
@@ -0,0 +1,21 @@
+@prefix action: .
+@prefix owl: .
+@prefix rdf: .
+@prefix rdfs: .
+@prefix sh: .
+@prefix xsd: .
+
+[]
+ a sh:ValidationReport ;
+ sh:conforms "false"^^xsd:boolean ;
+ sh:result [
+ a sh:ValidationResult ;
+ sh:focusNode ;
+ sh:resultMessage 'Node kb:person-automated-action-c51e0065-ba55-4177-bc08-e71963c22c26 does not conform to all shapes in [ rdf:type sh:PropertyShape ; sh:class identity:Person ; sh:path action:performer ] , [ rdf:type sh:NodeShape ; rdfs:seeAlso ; sh:not [ rdf:type sh:PropertyShape ; sh:hasValue Literal("true" = True, datatype=xsd:boolean) ; sh:path action:isAutomated ] ]' ;
+ sh:resultSeverity sh:Violation ;
+ sh:sourceConstraintComponent sh:AndConstraintComponent ;
+ sh:sourceShape action:Action ;
+ sh:value ;
+ ] ;
+ .
+
diff --git a/tests/examples/test_validation.py b/tests/examples/test_validation.py
index d76ac979..fef190ab 100644
--- a/tests/examples/test_validation.py
+++ b/tests/examples/test_validation.py
@@ -254,6 +254,10 @@ def test_co_XFAIL_validation() -> None:
}
)
+def test_isAutomated_XFAIL_validation() -> None:
+ g = load_validation_graph("isAutomated_XFAIL_validation.ttl", False)
+ assert isinstance(g, rdflib.Graph)
+
def test_location_PASS_validation() -> None:
"""
Confirm the PASS instance data passes validation.