Skip to content

Commit 7434c6f

Browse files
committed
Add and confirm prefix customization; start doctests
This patch is an isolated and expanded change drawn from PR 38. This patch was written to integrate into the current class hierarchy without disruption to existing code. This patch does not necessarily indicate agreement with the current class hierarchy. A follow-on patch will regenerate Make-managed files. References: * #38 Signed-off-by: Alex Nelson <alexander.nelson@nist.gov>
1 parent 4ad2ddb commit 7434c6f

File tree

5 files changed

+80
-11
lines changed

5 files changed

+80
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ __pycache__/
1919
# Build Artifacts
2020
build/
2121
dist/
22+
case.ttl

Makefile

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,26 @@ case.jsonld: \
8181
_$@
8282
mv _$@ $@
8383

84+
case.ttl: \
85+
case.jsonld
86+
source venv/bin/activate \
87+
&& rdfpipe \
88+
--output-format turtle \
89+
$< \
90+
> _$@
91+
source venv/bin/activate \
92+
&& case_validate \
93+
_$@
94+
@# In instances where graph nodes omit use of a prefix, a 'default' prefix-base is used that incorporates the local directory as a file URL. This is likely to be an undesired behavior, so for the generated example JSON-LD in the top source directory, check that "file:///" doesn't start any of the graph individuals' IRIs.
95+
test \
96+
0 \
97+
-eq \
98+
$$(grep 'file:' _$@ | wc -l) \
99+
|| ( echo "ERROR:Some graph IRIs do not supply a resolving prefix. Look for the string 'file:///' in the file _$@ to see these instances." ; exit 1)
100+
mv _$@ $@
101+
84102
check: \
85-
all
103+
all \
104+
case.ttl
86105
source venv/bin/activate \
87-
&& poetry run pytest
106+
&& poetry run pytest --doctest-modules

case_mapping/base.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
from datetime import datetime
3+
from typing import Any
34

45
from cdo_local_uuid import local_uuid
56

@@ -20,8 +21,35 @@ def wrapper(self, *args, **kwargs):
2021

2122

2223
class FacetEntity(dict):
23-
def __init__(self):
24-
self["@id"] = str(local_uuid())
24+
def __init__(
25+
self,
26+
*args: Any,
27+
prefix_iri: str = "http://example.org/kb/",
28+
prefix_label: str = "kb",
29+
**kwargs: Any,
30+
) -> None:
31+
"""
32+
:param prefix_iri: The IRI to be concatenated with the prefixed name's local part in order to form the node's absolute IRI.
33+
:param prefix_label: The prefix separated from the compact IRI's local name by a colon.
34+
35+
References
36+
==========
37+
38+
.. [#turtle_prefixed_name] https://www.w3.org/TR/turtle/#prefixed-name
39+
40+
Examples
41+
========
42+
43+
When instantiating a ``FacetEntity``, the JSON dictionary's ``@id`` key will start with the object's ``prefix_label``.
44+
45+
>>> x = FacetEntity()
46+
>>> assert x["@id"][0:3] == "kb:"
47+
>>> y = FacetEntity(prefix_label="ex")
48+
>>> assert y["@id"][0:3] == "ex:"
49+
"""
50+
self.prefix_iri = prefix_iri
51+
self.prefix_label = prefix_label
52+
self["@id"] = prefix_label + ":" + str(local_uuid())
2553

2654
def __str__(self):
2755
return json.dumps(self, indent=4)
@@ -159,6 +187,9 @@ def __handle_var_type_errors(var_name, var_val, expected_type):
159187

160188

161189
class ObjectEntity(FacetEntity):
190+
def __init__(self, *args: Any, **kwargs: Any) -> None:
191+
super().__init__(*args, **kwargs)
192+
162193
@unpack_args_array
163194
def append_facets(self, *args):
164195
"""

case_mapping/uco/core.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Any
2+
13
from ..base import ObjectEntity, unpack_args_array
24

35

@@ -8,11 +10,13 @@ def __init__(
810
uco_core_name=None,
911
spec_version=None,
1012
description=None,
11-
):
13+
*args: Any,
14+
**kwargs: Any,
15+
) -> None:
1216
"""
1317
The main CASE Object for representing a case and its activities and objects.
1418
"""
15-
super().__init__()
19+
super().__init__(*args, **kwargs)
1620
self.build = []
1721
self["@context"] = {
1822
"@vocab": "http://caseontology.org/core#",
@@ -31,6 +35,17 @@ def __init__(
3135
"uco-vocabulary": "https://ontology.unifiedcyberontology.org/uco/vocabulary/",
3236
"xsd": "http://www.w3.org/2001/XMLSchema#",
3337
}
38+
39+
# Assign caller-selectible prefix label and IRI, after checking
40+
# for conflicts with hard-coded prefixes.
41+
# https://www.w3.org/TR/turtle/#prefixed-name
42+
if self.prefix_label in self["@context"]:
43+
raise ValueError(
44+
"Requested prefix label already in use in hard-coded dictionary: '%s'. Please revise caller to use another label."
45+
% self.prefix_label
46+
)
47+
self["@context"][self.prefix_label] = self.prefix_iri
48+
3449
self["@type"] = "uco-core:Bundle"
3550
self._str_vars(
3651
**{

case_mapping/uco/observable.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,10 @@ def __init__(
269269
}
270270

271271
if hash_method is not None or hash_value is not None or hash_value != "-":
272-
data = {"@id": str(local_uuid()), "@type": "uco-types:Hash"}
272+
data = {
273+
"@id": self.prefix_label + ":" + str(local_uuid()),
274+
"@type": "uco-types:Hash",
275+
}
273276
if hash_method is not None:
274277
data["uco-types:hashMethod"] = {
275278
"@type": "uco-vocabulary:HashNameVocab",
@@ -416,7 +419,7 @@ def __init__(self, browser=None, history_entries=None):
416419
self["uco-observable:urlHistoryEntry"] = []
417420
for entry in history_entries:
418421
history_entry = {}
419-
history_entry["@id"] = local_uuid()
422+
history_entry["@id"] = self.prefix_label + ":" + local_uuid()
420423
history_entry["@type"] = "uco-observable:URLHistoryEntry"
421424
for key, var in entry.items():
422425
if key in keys_str:
@@ -858,14 +861,14 @@ def __init__(self, **kwargs):
858861
self["@type"] = "uco-observable:EXIFFacet"
859862

860863
self["uco-observable:exifData"] = {
861-
"@id": str(local_uuid()),
864+
"@id": self.prefix_label + ":" + str(local_uuid()),
862865
"@type": "uco-types:ControlledDictionary",
863866
"uco-types:entry": [],
864867
}
865868
for k, v in kwargs.items():
866869
if v not in ["", " "]:
867870
item = {
868-
"@id": str(local_uuid()),
871+
"@id": self.prefix_label + ":" + str(local_uuid()),
869872
"@type": "uco-types:ControlledDictionaryEntry",
870873
"uco-types:key": k,
871874
"uco-types:value": v,
@@ -1356,7 +1359,7 @@ def __init__(
13561359
self._node_reference_vars(**{"uco-observable:participant": participants})
13571360

13581361
self["uco-observable:messageThread"] = {
1359-
"@id": str(local_uuid()),
1362+
"@id": self.prefix_label + ":" + local_uuid(),
13601363
"@type": "uco-types:Thread",
13611364
}
13621365

0 commit comments

Comments
 (0)