diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0e429a60..ba630d0f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,5 +1,5 @@
repos:
-- repo: https://github.com/pre-commit/pre-commit-hooks
+ - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-json
@@ -9,3 +9,18 @@ repos:
- --indent=4
- --no-ensure-ascii
- --no-sort-keys
+ - repo: https://github.com/psf/black
+ rev: 23.9.1
+ hooks:
+ - id: black
+ - repo: https://github.com/pycqa/flake8
+ rev: 6.1.0
+ hooks:
+ - id: flake8
+ args:
+ - --extend-ignore=E203,E501
+ - repo: https://github.com/pycqa/isort
+ rev: 5.12.0
+ hooks:
+ - id: isort
+ name: isort (python)
diff --git a/README.md b/README.md
index 7af8eee9..ea5ae18c 100644
--- a/README.md
+++ b/README.md
@@ -9,27 +9,28 @@ Further illustration can be found at the [CASE narratives gallery](https://caseo
Mapping notes & respective JSON-LD output:
-- [Accounts](examples/illustrations/accounts/accounts.json)
+- [Accounts](examples/illustrations/accounts/accounts.json) ([*info*](examples/illustrations/accounts/))
- [Analysis](examples/illustrations/analysis/analysis.json) ([*info*](examples/illustrations/analysis/))
- [Bulk Extractor Forensic Path](examples/illustrations/bulk_extractor_forensic_path/bulk_extractor_forensic_path.json) (*[info](examples/illustrations/bulk_extractor_forensic_path/)*)
- [Call Log](examples/illustrations/call_log/call_log.json)
- [Cell Site](examples/illustrations/cell_site/cell_site.json) ([*info*](examples/illustrations/cell_site/))
- [Configured Tool](examples/illustrations/configured_tool/configured_tool.json)
+- [Database records](examples/illustrations/database_records/database_records.json) ([*info*](examples/illustrations/database_records/))
- [Device](examples/illustrations/device/device.json)
- [EXIF Data](examples/illustrations/exif_data/exif_data.json)
- [Event](examples/illustrations/event/event.json)
- [Files](examples/illustrations/file/file.json) (*[info](examples/illustrations/file/)*)
-- [Forensic Lifecycle](examples/illustrations/forensic_lifecycle/forensic_lifecycle.json)
+- [Forensic Lifecycle](examples/illustrations/forensic_lifecycle/forensic_lifecycle.json) ([*info*](examples/illustrations/forensic_lifecycle/))
- [Location](examples/illustrations/location/location.json)
-- [Message](examples/illustrations/message/message.json)
-- [Mobile device and SIM card](examples/illustrations/mobile_device_and_sim_card/mobile_device_and_sim_card.json)
+- [Message](examples/illustrations/message/message.json) ([*info*](examples/illustrations/message/))
+- [Mobile device and SIM card](examples/illustrations/mobile_device_and_sim_card/mobile_device_and_sim_card.json) ([*info*](examples/illustrations/mobile_device_and_sim_card/))
- [Multipart File](examples/illustrations/multipart_file/multipart_file.json) (*[info](examples/illustrations/multipart_file/)*)
-- [Network connection](examples/illustrations/network_connection/network_connection.json)
+- [Network connection](examples/illustrations/network_connection/network_connection.json) (*[info](examples/illustrations/network_connection/)*)
- [Oresteia](examples/illustrations/Oresteia/Oresteia.json) (*[info](examples/illustrations/Oresteia/)*)
-- [Raw Data](examples/illustrations/raw_data/raw_data.json)
+- [Raw Data](examples/illustrations/raw_data/raw_data.json) (*[info](examples/illustrations/raw_data/)*)
- [Reconstructed File](examples/illustrations/reconstructed_file/reconstructed_file.json) (*[info](examples/illustrations/reconstructed_file/)*)
- [Recoverability](examples/illustrations/recoverability/recoverability.json) (*[info](examples/illustrations/recoverability/)*)
-- [SMS and Contacts](examples/illustrations/sms_and_contacts/sms_and_contacts.json)
+- [SMS and Contacts](examples/illustrations/sms_and_contacts/sms_and_contacts.json) (*[info](examples/illustrations/sms_and_contacts/)*)
- [Spear Phishing](examples/illustrations/spear_phishing/spear_phishing.json), scenario drafted by [Open Cybersecurity Alliance](https://github.com/opencybersecurityalliance/oca-ontology) (*[info](examples/illustrations/spear_phishing/)*)
diff --git a/examples/illustrations/Oresteia/Makefile b/examples/illustrations/Oresteia/Makefile
index a8a5de30..1c6e5e7a 100644
--- a/examples/illustrations/Oresteia/Makefile
+++ b/examples/illustrations/Oresteia/Makefile
@@ -13,4 +13,6 @@
RENDER_PROV := yes
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/Oresteia/README.md b/examples/illustrations/Oresteia/README.md
index d6b5234d..e227f372 100644
--- a/examples/illustrations/Oresteia/README.md
+++ b/examples/illustrations/Oresteia/README.md
@@ -7,9 +7,16 @@ collectively called The Oresteia. For illustrative purposes, the characters
of these ancient stories are given access to modern technology.
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/Oresteia-relationships.svg](figures/Oresteia-relationships.svg)
+
+
## Provenance illustrations
-Following visual-design practices of [PROV-O](https://www.w3.org/TR/prov-o/), the following renders of this scenario are available:
+Following visual-design practices of [PROV-O](https://www.w3.org/TR/prov-o/), the following renders of this scenario's provenance are available:
| Figure's contents | Time hidden | Time displayed |
| --- | --- | --- |
diff --git a/examples/illustrations/Oresteia/figures/Oresteia-relationships.svg b/examples/illustrations/Oresteia/figures/Oresteia-relationships.svg
new file mode 100644
index 00000000..3a9d6ff4
--- /dev/null
+++ b/examples/illustrations/Oresteia/figures/Oresteia-relationships.svg
@@ -0,0 +1,465 @@
+
+
+
+
+
diff --git a/examples/illustrations/accounts/Makefile b/examples/illustrations/accounts/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/accounts/Makefile
+++ b/examples/illustrations/accounts/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/accounts/README.md b/examples/illustrations/accounts/README.md
new file mode 100644
index 00000000..536f6ee8
--- /dev/null
+++ b/examples/illustrations/accounts/README.md
@@ -0,0 +1,8 @@
+# Accounts Example
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/accounts-relationships.svg](figures/accounts-relationships.svg)
diff --git a/examples/illustrations/accounts/figures/accounts-relationships.svg b/examples/illustrations/accounts/figures/accounts-relationships.svg
new file mode 100644
index 00000000..a378c9e6
--- /dev/null
+++ b/examples/illustrations/accounts/figures/accounts-relationships.svg
@@ -0,0 +1,134 @@
+
+
+
+
+
diff --git a/examples/illustrations/bulk_extractor_forensic_path/Makefile b/examples/illustrations/bulk_extractor_forensic_path/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/bulk_extractor_forensic_path/Makefile
+++ b/examples/illustrations/bulk_extractor_forensic_path/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/bulk_extractor_forensic_path/README.md b/examples/illustrations/bulk_extractor_forensic_path/README.md
index 7ed2d43d..4bb3f023 100644
--- a/examples/illustrations/bulk_extractor_forensic_path/README.md
+++ b/examples/illustrations/bulk_extractor_forensic_path/README.md
@@ -22,3 +22,10 @@ extract out the email addresses.
- *relationship3* (Compression : GZIP) -> **decompressed_gzip0**
- *relationship0* (DataRange : 1600) -> **extracted_email_address0**
- *relationship1* (DataRange : 16095) -> **extracted_email_address1**
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/bulk_extractor_forensic_path-relationships.svg](figures/bulk_extractor_forensic_path-relationships.svg)
diff --git a/examples/illustrations/bulk_extractor_forensic_path/figures/bulk_extractor_forensic_path-relationships.svg b/examples/illustrations/bulk_extractor_forensic_path/figures/bulk_extractor_forensic_path-relationships.svg
new file mode 100644
index 00000000..0734725c
--- /dev/null
+++ b/examples/illustrations/bulk_extractor_forensic_path/figures/bulk_extractor_forensic_path-relationships.svg
@@ -0,0 +1,178 @@
+
+
+
+
+
diff --git a/examples/illustrations/cell_site/Makefile b/examples/illustrations/cell_site/Makefile
index 1e1a0893..61da515e 100644
--- a/examples/illustrations/cell_site/Makefile
+++ b/examples/illustrations/cell_site/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-basedir.mk
diff --git a/examples/illustrations/cell_site/README.md b/examples/illustrations/cell_site/README.md
index b3c15890..4f2d2d0d 100644
--- a/examples/illustrations/cell_site/README.md
+++ b/examples/illustrations/cell_site/README.md
@@ -350,6 +350,15 @@ Per practice of this example organization, the derivation of the relationship is
]
```
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/cell_site-relationships.svg](figures/cell_site-relationships.svg)
+
+
+
[^1]: OpenCelliD Project is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
[^2]: The example organization has not discussed policy on how to handle matters such as cell sites being retired from service. Such a policy would be necessary to process that no record is currently found with `cellSiteIdentifier` value `29220952` within their data source. This is a practice that would be better explored by interested investigative community members.
[^3]: SOSA defines that a `sosa:Observation` uniquely has one `Property` that is the subject of the measurement. As OWL ontology data, that property does not necessarily need to be recorded. By leaving the property absent, we still express that the feature of interest, the cell site, was observed; we just do not say what about it we were observing. "The property that expresses the location of this object" is not something for which CASE or UCO yet has a clear representation practice, so we omit it in this example.
diff --git a/examples/illustrations/cell_site/figures/cell_site-relationships.svg b/examples/illustrations/cell_site/figures/cell_site-relationships.svg
new file mode 100644
index 00000000..bc95d51b
--- /dev/null
+++ b/examples/illustrations/cell_site/figures/cell_site-relationships.svg
@@ -0,0 +1,136 @@
+
+
+
+
+
diff --git a/examples/illustrations/cell_site/src/README.md.in b/examples/illustrations/cell_site/src/README.md.in
index db320949..cbca73ab 100644
--- a/examples/illustrations/cell_site/src/README.md.in
+++ b/examples/illustrations/cell_site/src/README.md.in
@@ -122,6 +122,15 @@ Per practice of this example organization, the derivation of the relationship is
@CELL_SITE_CDR_SITE_LOCATION_JSON@
```
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/cell_site-relationships.svg](figures/cell_site-relationships.svg)
+
+
+
[^1]: OpenCelliD Project is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
[^2]: The example organization has not discussed policy on how to handle matters such as cell sites being retired from service. Such a policy would be necessary to process that no record is currently found with `cellSiteIdentifier` value `29220952` within their data source. This is a practice that would be better explored by interested investigative community members.
[^3]: SOSA defines that a `sosa:Observation` uniquely has one `Property` that is the subject of the measurement. As OWL ontology data, that property does not necessarily need to be recorded. By leaving the property absent, we still express that the feature of interest, the cell site, was observed; we just do not say what about it we were observing. "The property that expresses the location of this object" is not something for which CASE or UCO yet has a clear representation practice, so we omit it in this example.
diff --git a/examples/illustrations/database_records/Makefile b/examples/illustrations/database_records/Makefile
index 1e1a0893..61da515e 100644
--- a/examples/illustrations/database_records/Makefile
+++ b/examples/illustrations/database_records/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-basedir.mk
diff --git a/examples/illustrations/database_records/README.md b/examples/illustrations/database_records/README.md
index 680370af..bc5ab2c1 100644
--- a/examples/illustrations/database_records/README.md
+++ b/examples/illustrations/database_records/README.md
@@ -177,3 +177,10 @@ This allows an identified artifact (such as a message) to be linked to a databas
]
```
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/database_records-relationships.svg](figures/database_records-relationships.svg)
diff --git a/examples/illustrations/database_records/figures/database_records-relationships.svg b/examples/illustrations/database_records/figures/database_records-relationships.svg
new file mode 100644
index 00000000..a2fa39bb
--- /dev/null
+++ b/examples/illustrations/database_records/figures/database_records-relationships.svg
@@ -0,0 +1,122 @@
+
+
+
+
+
diff --git a/examples/illustrations/database_records/src/README.md.in b/examples/illustrations/database_records/src/README.md.in
index 75440707..a2cc57a8 100644
--- a/examples/illustrations/database_records/src/README.md.in
+++ b/examples/illustrations/database_records/src/README.md.in
@@ -43,3 +43,10 @@ This allows an identified artifact (such as a message) to be linked to a databas
@DATABASE_RECORDS_ARTIFACT_LINKING_JSON@
```
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/database_records-relationships.svg](figures/database_records-relationships.svg)
diff --git a/examples/illustrations/file/Makefile b/examples/illustrations/file/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/file/Makefile
+++ b/examples/illustrations/file/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/file/README.md b/examples/illustrations/file/README.md
index 58f740a7..bd3f0662 100644
--- a/examples/illustrations/file/README.md
+++ b/examples/illustrations/file/README.md
@@ -50,3 +50,10 @@ encoding algorithm used to decode the `target` file.
For the extraction of an embedded section of raw bytes within the `target` file, we
use the **DataRange** property bundle which provides the offset location and range size within the `target` file.
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/file-relationships.svg](figures/file-relationships.svg)
diff --git a/examples/illustrations/file/figures/file-relationships.svg b/examples/illustrations/file/figures/file-relationships.svg
new file mode 100644
index 00000000..e3b12b34
--- /dev/null
+++ b/examples/illustrations/file/figures/file-relationships.svg
@@ -0,0 +1,256 @@
+
+
+
+
+
diff --git a/examples/illustrations/forensic_lifecycle/Makefile b/examples/illustrations/forensic_lifecycle/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/forensic_lifecycle/Makefile
+++ b/examples/illustrations/forensic_lifecycle/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/forensic_lifecycle/README.md b/examples/illustrations/forensic_lifecycle/README.md
new file mode 100644
index 00000000..477a142f
--- /dev/null
+++ b/examples/illustrations/forensic_lifecycle/README.md
@@ -0,0 +1,8 @@
+# Forensic lifecycle Example
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/forensic_lifecycle-relationships.svg](figures/forensic_lifecycle-relationships.svg)
diff --git a/examples/illustrations/forensic_lifecycle/figures/forensic_lifecycle-relationships.svg b/examples/illustrations/forensic_lifecycle/figures/forensic_lifecycle-relationships.svg
new file mode 100644
index 00000000..0cb5ad9a
--- /dev/null
+++ b/examples/illustrations/forensic_lifecycle/figures/forensic_lifecycle-relationships.svg
@@ -0,0 +1,205 @@
+
+
+
+
+
diff --git a/examples/illustrations/message/Makefile b/examples/illustrations/message/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/message/Makefile
+++ b/examples/illustrations/message/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/message/README.md b/examples/illustrations/message/README.md
new file mode 100644
index 00000000..5c32fb37
--- /dev/null
+++ b/examples/illustrations/message/README.md
@@ -0,0 +1,8 @@
+# Message Example
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/message-relationships.svg](figures/message-relationships.svg)
diff --git a/examples/illustrations/message/figures/message-relationships.svg b/examples/illustrations/message/figures/message-relationships.svg
new file mode 100644
index 00000000..7e53f54c
--- /dev/null
+++ b/examples/illustrations/message/figures/message-relationships.svg
@@ -0,0 +1,100 @@
+
+
+
+
+
diff --git a/examples/illustrations/message/test_message.py b/examples/illustrations/message/test_message.py
index 080f9136..e65a611a 100644
--- a/examples/illustrations/message/test_message.py
+++ b/examples/illustrations/message/test_message.py
@@ -11,12 +11,11 @@
#
# We would appreciate acknowledgement if the software is used.
-import logging
-import os
import pathlib
import rdflib.plugins.sparql
+
def test_messages_have_sent_times() -> None:
expected = set()
computed = set()
@@ -24,13 +23,15 @@ def test_messages_have_sent_times() -> None:
graph = rdflib.Graph()
srcdir_path = pathlib.Path(__file__).parent
message_json_path = srcdir_path / "message.json"
- assert message_json_path.exists(), "message.json not found in same directory as test."
+ assert (
+ message_json_path.exists()
+ ), "message.json not found in same directory as test."
# TODO - Remove 'format' parameter when an rdflib release with this PR is issued:
# https://github.com/RDFLib/rdflib/pull/1403
graph.parse(str(message_json_path), format="json-ld")
- nsdict = {k:v for (k,v) in graph.namespace_manager.namespaces()}
+ nsdict = {k: v for (k, v) in graph.namespace_manager.namespaces()}
query_str = """\
SELECT ?nMessage
@@ -39,7 +40,7 @@ def test_messages_have_sent_times() -> None:
?nMessageFacet a uco-observable:MessageFacet .
FILTER NOT EXISTS {
?nMessageFacet uco-observable:sentTime ?lSentTime .
- }
+ }
}
"""
query_object = rdflib.plugins.sparql.prepareQuery(query_str, initNs=nsdict)
@@ -47,4 +48,6 @@ def test_messages_have_sent_times() -> None:
n_message = result[0]
computed.add(n_message.toPython())
- assert expected == computed, "Message objects with IRIs in the 'computed' set do not have sent-times. Please ensure this example has sent-times."
+ assert (
+ expected == computed
+ ), "Message objects with IRIs in the 'computed' set do not have sent-times. Please ensure this example has sent-times."
diff --git a/examples/illustrations/mobile_device_and_sim_card/Makefile b/examples/illustrations/mobile_device_and_sim_card/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/mobile_device_and_sim_card/Makefile
+++ b/examples/illustrations/mobile_device_and_sim_card/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/mobile_device_and_sim_card/README.md b/examples/illustrations/mobile_device_and_sim_card/README.md
new file mode 100644
index 00000000..62a3a23e
--- /dev/null
+++ b/examples/illustrations/mobile_device_and_sim_card/README.md
@@ -0,0 +1,8 @@
+# Mobile Device and SIM Card Example
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/mobile_device_and_sim_card-relationships.svg](figures/mobile_device_and_sim_card-relationships.svg)
diff --git a/examples/illustrations/mobile_device_and_sim_card/figures/mobile_device_and_sim_card-relationships.svg b/examples/illustrations/mobile_device_and_sim_card/figures/mobile_device_and_sim_card-relationships.svg
new file mode 100644
index 00000000..7340579f
--- /dev/null
+++ b/examples/illustrations/mobile_device_and_sim_card/figures/mobile_device_and_sim_card-relationships.svg
@@ -0,0 +1,74 @@
+
+
+
+
+
diff --git a/examples/illustrations/multipart_file/Makefile b/examples/illustrations/multipart_file/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/multipart_file/Makefile
+++ b/examples/illustrations/multipart_file/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/multipart_file/README.md b/examples/illustrations/multipart_file/README.md
index 2ee16b01..38dc1631 100644
--- a/examples/illustrations/multipart_file/README.md
+++ b/examples/illustrations/multipart_file/README.md
@@ -11,3 +11,10 @@ that is explained in [*file*](../file/). By using **Relationship** objects with
each fragment of the file can be explicitly associated with the overall file. Each fragment object would then have a **Fragment** property bundle
that describes the position (*fragmentIndex*) of this particular fragment within the reconstructed file.
The *totalFragments* property may be used to help the serializer know how many total fragments to look for.
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/multipart_file-relationships.svg](figures/multipart_file-relationships.svg)
diff --git a/examples/illustrations/multipart_file/figures/multipart_file-relationships.svg b/examples/illustrations/multipart_file/figures/multipart_file-relationships.svg
new file mode 100644
index 00000000..14211cac
--- /dev/null
+++ b/examples/illustrations/multipart_file/figures/multipart_file-relationships.svg
@@ -0,0 +1,160 @@
+
+
+
+
+
diff --git a/examples/illustrations/network_connection/Makefile b/examples/illustrations/network_connection/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/network_connection/Makefile
+++ b/examples/illustrations/network_connection/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/network_connection/README.md b/examples/illustrations/network_connection/README.md
new file mode 100644
index 00000000..ce976dc5
--- /dev/null
+++ b/examples/illustrations/network_connection/README.md
@@ -0,0 +1,8 @@
+# Network Connection Example
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/network_connection-relationships.svg](figures/network_connection-relationships.svg)
diff --git a/examples/illustrations/network_connection/figures/network_connection-relationships.svg b/examples/illustrations/network_connection/figures/network_connection-relationships.svg
new file mode 100644
index 00000000..12f52f5e
--- /dev/null
+++ b/examples/illustrations/network_connection/figures/network_connection-relationships.svg
@@ -0,0 +1,74 @@
+
+
+
+
+
diff --git a/examples/illustrations/raw_data/Makefile b/examples/illustrations/raw_data/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/raw_data/Makefile
+++ b/examples/illustrations/raw_data/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/raw_data/README.md b/examples/illustrations/raw_data/README.md
new file mode 100644
index 00000000..91ee1066
--- /dev/null
+++ b/examples/illustrations/raw_data/README.md
@@ -0,0 +1,8 @@
+# Raw Data Example
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/raw_data-relationships.svg](figures/raw_data-relationships.svg)
diff --git a/examples/illustrations/raw_data/figures/raw_data-relationships.svg b/examples/illustrations/raw_data/figures/raw_data-relationships.svg
new file mode 100644
index 00000000..4fa4ac84
--- /dev/null
+++ b/examples/illustrations/raw_data/figures/raw_data-relationships.svg
@@ -0,0 +1,48 @@
+
+
+
+
+
diff --git a/examples/illustrations/reconstructed_file/Makefile b/examples/illustrations/reconstructed_file/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/reconstructed_file/Makefile
+++ b/examples/illustrations/reconstructed_file/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/reconstructed_file/README.md b/examples/illustrations/reconstructed_file/README.md
index e3bacf93..04f7dc9a 100644
--- a/examples/illustrations/reconstructed_file/README.md
+++ b/examples/illustrations/reconstructed_file/README.md
@@ -29,3 +29,10 @@ The reconstructed file can be extracted using dd as follows:
% shasum -a 256 reconstructed_file
ee8b9c17c44e128e9e95d60fe219e95feae53c463b01016a312f8c5b732f21de reconstructed_file.jpg
```
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/reconstructed_file-relationships.svg](figures/reconstructed_file-relationships.svg)
diff --git a/examples/illustrations/reconstructed_file/figures/reconstructed_file-relationships.svg b/examples/illustrations/reconstructed_file/figures/reconstructed_file-relationships.svg
new file mode 100644
index 00000000..23453b43
--- /dev/null
+++ b/examples/illustrations/reconstructed_file/figures/reconstructed_file-relationships.svg
@@ -0,0 +1,160 @@
+
+
+
+
+
diff --git a/examples/illustrations/sms_and_contacts/Makefile b/examples/illustrations/sms_and_contacts/Makefile
index e66880d6..078be6d2 100644
--- a/examples/illustrations/sms_and_contacts/Makefile
+++ b/examples/illustrations/sms_and_contacts/Makefile
@@ -11,4 +11,6 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
diff --git a/examples/illustrations/sms_and_contacts/README.md b/examples/illustrations/sms_and_contacts/README.md
new file mode 100644
index 00000000..e86da10d
--- /dev/null
+++ b/examples/illustrations/sms_and_contacts/README.md
@@ -0,0 +1,8 @@
+# SMS and Contacts Example
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/sms_and_contacts-relationships.svg](figures/sms_and_contacts-relationships.svg)
diff --git a/examples/illustrations/sms_and_contacts/figures/sms_and_contacts-relationships.svg b/examples/illustrations/sms_and_contacts/figures/sms_and_contacts-relationships.svg
new file mode 100644
index 00000000..ddb096de
--- /dev/null
+++ b/examples/illustrations/sms_and_contacts/figures/sms_and_contacts-relationships.svg
@@ -0,0 +1,100 @@
+
+
+
+
+
diff --git a/examples/illustrations/spear_phishing/Makefile b/examples/illustrations/spear_phishing/Makefile
index b37c0838..e4c65240 100644
--- a/examples/illustrations/spear_phishing/Makefile
+++ b/examples/illustrations/spear_phishing/Makefile
@@ -11,6 +11,8 @@
#
# We would appreciate acknowledgement if the software is used.
+RENDER_RELATIONSHIPS := yes
+
include ../src/illustration-nosrc.mk
all: spear_phishing.ttl
diff --git a/examples/illustrations/spear_phishing/README.md b/examples/illustrations/spear_phishing/README.md
index bc6317c7..79472d47 100644
--- a/examples/illustrations/spear_phishing/README.md
+++ b/examples/illustrations/spear_phishing/README.md
@@ -12,3 +12,10 @@ The UCO rule requiring all named individuals end with a UUID has been deactivate
As documentation and figures are transcribed to RDF, all named individuals are initially given only the type `uco-core:UcoThing`.
At the end of the scenario rendering process, all individuals will have the most specific types available within CASE and UCO, and all predicates in the graph with the prefix `unmapped:` will be replaced with approprate properties or `uco-core:Relationship` objects.
+
+
+## Relationship illustrations
+
+Using a proof-of-concept illustration system, a render of this scenario's `uco-core:Relationship` objects is available:
+
+![figures/spear_phishing-relationships.svg](figures/spear_phishing-relationships.svg)
diff --git a/examples/illustrations/spear_phishing/figures/spear_phishing-relationships.svg b/examples/illustrations/spear_phishing/figures/spear_phishing-relationships.svg
new file mode 100644
index 00000000..57718ac1
--- /dev/null
+++ b/examples/illustrations/spear_phishing/figures/spear_phishing-relationships.svg
@@ -0,0 +1,209 @@
+
+
+
+
+
diff --git a/examples/illustrations/src/generic_snippet_combiner.py b/examples/illustrations/src/generic_snippet_combiner.py
index 19d78dd8..e31ae6bc 100644
--- a/examples/illustrations/src/generic_snippet_combiner.py
+++ b/examples/illustrations/src/generic_snippet_combiner.py
@@ -22,13 +22,14 @@
__version__ = "0.1.2"
-import logging
import json
+import logging
import os
import sys
_logger = logging.getLogger(os.path.basename(__file__))
+
def main():
obj = None
_logger.info("args.in_json = %r.", args.in_json)
@@ -58,10 +59,16 @@ def main():
json.dump(obj, sys.stdout, indent=4, sort_keys=True)
sys.stdout.write("\n")
+
if __name__ == "__main__":
import argparse
+
parser = argparse.ArgumentParser()
- parser.add_argument("in_json", nargs="+", help="The first argument is the \"base\" JSON-LD file, including context dictionary.")
+ parser.add_argument(
+ "in_json",
+ nargs="+",
+ help='The first argument is the "base" JSON-LD file, including context dictionary.',
+ )
args = parser.parse_args()
logging.basicConfig(level=logging.INFO)
main()
diff --git a/examples/illustrations/src/illustration-basedir.mk b/examples/illustrations/src/illustration-basedir.mk
index 9a288076..2e069ede 100644
--- a/examples/illustrations/src/illustration-basedir.mk
+++ b/examples/illustrations/src/illustration-basedir.mk
@@ -18,6 +18,7 @@ top_srcdir := $(shell cd ../../.. ; pwd)
illustration_name := $(shell basename $$PWD)
RENDER_PROV ?=
+RENDER_RELATIONSHIPS ?=
# `diff` is used to determine if a copy operation should happen.
# If it happens each time, validation files will be regenerated on every
@@ -34,6 +35,7 @@ all:
$(illustration_name).json
$(MAKE) \
RENDER_PROV="$(RENDER_PROV)" \
+ RENDER_RELATIONSHIPS="$(RENDER_RELATIONSHIPS)" \
--file ../src/illustration-nosrc.mk
check:
@@ -46,6 +48,7 @@ check:
check
$(MAKE) \
RENDER_PROV="$(RENDER_PROV)" \
+ RENDER_RELATIONSHIPS="$(RENDER_RELATIONSHIPS)" \
--file ../src/illustration-nosrc.mk \
check
diff \
diff --git a/examples/illustrations/src/illustration-nosrc.mk b/examples/illustrations/src/illustration-nosrc.mk
index edbcdc42..4b7f0b2f 100644
--- a/examples/illustrations/src/illustration-nosrc.mk
+++ b/examples/illustrations/src/illustration-nosrc.mk
@@ -56,9 +56,18 @@ else
prov_svgs :=
endif
+RENDER_RELATIONSHIPS ?=
+ifeq ($(RENDER_RELATIONSHIPS),yes)
+relationship_svgs := \
+ figures/$(example_name)-relationships.svg
+else
+relationship_svgs :=
+endif
+
all: \
$(example_name)_validation.ttl \
$(prov_svgs) \
+ $(relationship_svgs) \
$(example_name)_validation-develop.ttl \
$(example_name)_validation-develop-2.0.0.ttl \
$(example_name)_validation-unstable.ttl \
@@ -479,3 +488,22 @@ figures/$(example_name)-prov-time-entities.dot: \
$(example_name)-prov.ttl \
$(example_name).json
mv $@_ $@
+
+figures/$(example_name)-relationships.dot: \
+ $(example_name).json \
+ $(top_srcdir)/.venv.done.log \
+ $(top_srcdir)/src/case_relationships_dot.py
+ mkdir -p figures
+ source $(top_srcdir)/venv/bin/activate \
+ && python3 $(top_srcdir)/src/case_relationships_dot.py \
+ $@_ \
+ $<
+ mv $@_ $@
+
+figures/$(example_name)-relationships.svg: \
+ figures/$(example_name)-relationships.dot
+ neato \
+ -T svg \
+ -o $@_ \
+ $<
+ mv $@_ $@
diff --git a/src/case_relationships_dot.py b/src/case_relationships_dot.py
new file mode 100644
index 00000000..85e89424
--- /dev/null
+++ b/src/case_relationships_dot.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+
+# Portions of this file contributed by NIST are governed by the
+# following statement:
+#
+# This software was developed at the National Institute of Standards
+# and Technology by employees of the Federal Government in the course
+# of their official duties. Pursuant to Title 17 Section 105 of the
+# United States Code, this software is not subject to copyright
+# protection within the United States. NIST assumes no responsibility
+# whatsoever for its use by other parties, and makes no guarantees,
+# expressed or implied, about its quality, reliability, or any other
+# characteristic.
+#
+# We would appreciate acknowledgement if the software is used.
+
+__version__ = "0.1.0"
+
+import argparse
+from typing import Dict, List, Set, Tuple
+
+import pydot
+import rdflib
+from case_prov.case_prov_dot import iri_to_gv_node_id, qname
+from case_utils.namespace import NS_UCO_CORE
+from rdflib import Literal, URIRef
+from rdflib.query import ResultRow
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("out_dot")
+ parser.add_argument("in_rdf", nargs="+")
+ args = parser.parse_args()
+
+ in_graph = rdflib.Graph()
+ out_graph = pydot.Dot(
+ "CASE render of directional relationships",
+ graph_type="digraph",
+ overlap="false",
+ )
+
+ for in_rdf in args.in_rdf:
+ in_graph.parse(in_rdf)
+
+ n_things_to_display: Set[URIRef] = set()
+ query = """\
+PREFIX uco-core:
+SELECT ?nSource ?nTarget
+WHERE {
+ ?nRelationship
+ uco-core:kindOfRelationship ?lKind ;
+ uco-core:source ?nSource ;
+ uco-core:target ?nTarget ;
+ .
+}
+"""
+ for result in in_graph.query(query):
+ assert isinstance(result, ResultRow)
+ assert isinstance(result[0], URIRef)
+ assert isinstance(result[1], URIRef)
+ n_things_to_display.add(result[0])
+ n_things_to_display.add(result[1])
+
+ edges_to_display: Set[Tuple[URIRef, URIRef, str]] = set()
+ query = """\
+PREFIX uco-core:
+SELECT ?nSource ?nTarget ?lIsDirectional ?lKind
+WHERE {
+ ?nRelationship
+ uco-core:isDirectional ?lIsDirectional ;
+ uco-core:kindOfRelationship ?lKind ;
+ uco-core:source ?nSource ;
+ uco-core:target ?nTarget ;
+ .
+}
+"""
+ for result in in_graph.query(query):
+ assert isinstance(result, ResultRow)
+ assert isinstance(result[0], URIRef)
+ assert isinstance(result[1], URIRef)
+ assert isinstance(result[2], Literal)
+ assert isinstance(result[3], Literal)
+ edges_to_display.add(
+ (result[0], result[1], result[2].toPython(), str(result[3]))
+ )
+
+ for n_thing in sorted(n_things_to_display):
+ label_parts: List[str] = ["ID - " + qname(in_graph, n_thing)]
+ for l_name in in_graph.objects(n_thing, NS_UCO_CORE.name):
+ assert isinstance(l_name, Literal)
+ label_parts.append(l_name)
+ for l_description in in_graph.objects(n_thing, NS_UCO_CORE.description):
+ assert isinstance(l_description, Literal)
+ label_parts.append(l_description)
+ dot_node = pydot.Node(
+ iri_to_gv_node_id(n_thing),
+ label="\n\n".join(label_parts),
+ tooltip=str(n_thing),
+ )
+ out_graph.add_node(dot_node)
+
+ # Represent the Relationship object as a "hinge" node to keep the
+ # edge label from overlapping with nodes.
+ for edge_to_display in sorted(edges_to_display):
+ dot_edge_hinge = pydot.Node(
+ iri_to_gv_node_id(
+ str(edge_to_display[0]) + str(edge_to_display[1]) + edge_to_display[3]
+ ),
+ label=edge_to_display[3],
+ shape="none",
+ tooltip="",
+ )
+
+ # Work around 'dir' for arrowhead directionality being a reserved word in Python.
+ # https://www.graphviz.org/docs/attrs/dir/
+ edge_attributes: Dict[str, str] = {
+ "dir": "forward" if edge_to_display[2] else "none"
+ }
+ dot_edge_0 = pydot.Edge(
+ iri_to_gv_node_id(edge_to_display[0]),
+ dot_edge_hinge,
+ **edge_attributes,
+ )
+ dot_edge_1 = pydot.Edge(
+ dot_edge_hinge,
+ iri_to_gv_node_id(edge_to_display[1]),
+ **edge_attributes,
+ )
+ out_graph.add_node(dot_edge_hinge)
+ out_graph.add_edge(dot_edge_0)
+ out_graph.add_edge(dot_edge_1)
+
+ out_graph.write_raw(args.out_dot)
+
+
+if __name__ == "__main__":
+ main()