Skip to content

Commit e4203fb

Browse files
committed
Bump case-utils adoption to 0.9.0; validate with case_validate
This patch does the minimum steps needed to update `case_exiftool` to validate against CASE 1.1.0. One generated artifact, a list of undefined vocabulary, is removed, as its purpose is now supplanted by the IRI typo checker in `case_validate` (`case-utils` Issue 40). The minimum Python version is now bumped to 3.8 due to an update in NumPy (see `case-utils` PR 99). A follow-on patch will regenerate Make-managed files. References: * casework/CASE-Utilities-Python#40 * casework/CASE-Utilities-Python#99 Signed-off-by: Alex Nelson <[email protected]>
1 parent be3cfd3 commit e4203fb

File tree

10 files changed

+57
-81
lines changed

10 files changed

+57
-81
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ jobs:
3131
uses: actions/setup-python@v2
3232
with:
3333
python-version: ${{ matrix.python-version }}
34-
- name: Install Python virtualenv for Github runner
35-
run: |
36-
python -m pip install --upgrade pip
37-
pip install virtualenv
3834
- name: Start from clean state
3935
run: make clean
4036
- name: Run tests

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ all:
3030
cd dependencies \
3131
&& git diff . \
3232
| cat
33-
git submodule init
34-
git submodule update
33+
git submodule update --init
34+
$(MAKE) \
35+
--directory dependencies/CASE-Examples-QC \
36+
.git_submodule_init.done.log
3537
touch $@
3638

3739
check: \
@@ -57,8 +59,6 @@ dependencies/CASE-Examples-QC/tests/ontology_vocabulary.txt: \
5759
$(MAKE) \
5860
PYTHON3=$(PYTHON3) \
5961
--directory dependencies/CASE-Examples-QC \
60-
.git_submodule_init.done.log \
61-
.lib.done.log \
6262
.venv.done.log
6363
$(MAKE) \
6464
PYTHON3=$(PYTHON3) \

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,10 @@ This project follows [SEMVER 2.0.0](https://semver.org/) where versions are decl
5050

5151
## Ontology versions supported
5252

53-
This repository supports the CASE and UCO ontology versions that are linked as submodules in the [CASE Examples QC](https://github.com/ajnelson-nist/CASE-Examples-QC) repository. Currently, those are:
53+
This repository supports the CASE and UCO ontology versions that are distributed with the [CASE-Utilities-Python repository](https://github.com/casework/CASE-Utilities-Python), at the newest version below a ceiling-pin in [setup.cfg](setup.cfg). Currently, those ontology versions are:
5454

55-
* CASE 0.3.0
56-
* UCO 0.5.0
57-
58-
Classes and properties are tested for vocabulary conformance.
55+
* CASE 1.1.0
56+
* UCO 1.1.0
5957

6058

6159
## Repository locations

case_exiftool/__init__.py

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import rdflib.plugins.sparql
2626

2727
import case_utils
28+
from case_utils.namespace import NS_RDF, NS_RDFS, NS_UCO_CORE, NS_UCO_IDENTITY, NS_UCO_LOCATION, NS_UCO_OBSERVABLE, NS_UCO_TYPES, NS_XSD
2829

2930
_logger = logging.getLogger(os.path.basename(__file__))
3031

@@ -40,14 +41,6 @@
4041
NS_EXIFTOOL_PREVIEWIFD = rdflib.Namespace("http://ns.exiftool.ca/MakerNotes/PreviewIFD/1.0/")
4142
NS_EXIFTOOL_INTEROPIFD = rdflib.Namespace("http://ns.exiftool.ca/EXIF/InteropIFD/1.0/")
4243
NS_EXIFTOOL_IFD1 = rdflib.Namespace("http://ns.exiftool.ca/EXIF/IFD1/1.0/")
43-
NS_RDF = rdflib.RDF
44-
NS_RDFS = rdflib.RDFS
45-
NS_UCO_CORE = rdflib.Namespace("https://unifiedcyberontology.org/ontology/uco/core#")
46-
NS_UCO_LOCATION = rdflib.Namespace("https://unifiedcyberontology.org/ontology/uco/location#")
47-
NS_UCO_OBSERVABLE = rdflib.Namespace("https://unifiedcyberontology.org/ontology/uco/observable#")
48-
NS_UCO_TYPES = rdflib.Namespace("https://unifiedcyberontology.org/ontology/uco/types#")
49-
NS_UCO_VOCABULARY = rdflib.Namespace("https://unifiedcyberontology.org/ontology/uco/vocabulary#")
50-
NS_XSD = rdflib.namespace.XSD
5144

5245
argument_parser = argparse.ArgumentParser(epilog=__doc__)
5346
argument_parser.add_argument("--base-prefix", default="http://example.org/kb/")
@@ -57,8 +50,8 @@
5750
argument_parser.add_argument("--raw-xml", help="A file recording the output of ExifTool run against some file. Expects exiftool was run with -binary, -duplicates, and -xmlFormat.", required=True)
5851
argument_parser.add_argument("out_graph", help="A self-contained RDF graph file, in the format either requested by --output-format or guessed based on extension.")
5952

60-
def controlled_dictionary_object_to_node(graph, controlled_dict):
61-
n_controlled_dictionary = rdflib.BNode()
53+
def controlled_dictionary_object_to_node(graph, ns_base, controlled_dict):
54+
n_controlled_dictionary = ns_base["controlled-dictionary-" + case_utils.local_uuid.local_uuid()]
6255
graph.add((
6356
n_controlled_dictionary,
6457
NS_RDF.type,
@@ -71,7 +64,7 @@ def controlled_dictionary_object_to_node(graph, controlled_dict):
7164
except:
7265
_logger.info("v_value = %r." % v_value)
7366
raise
74-
n_entry = rdflib.BNode()
67+
n_entry = ns_base["controlled-dictionary-entry-" + case_utils.local_uuid.local_uuid()]
7568
graph.add((
7669
n_controlled_dictionary,
7770
NS_UCO_TYPES.entry,
@@ -94,6 +87,15 @@ def controlled_dictionary_object_to_node(graph, controlled_dict):
9487
))
9588
return n_controlled_dictionary
9689

90+
def manufacturer_name_to_node(graph: rdflib.Graph, ns_base: rdflib.Namespace, name: str) -> rdflib.URIRef:
91+
"""
92+
This method is provided to be overwritten in case a mapping function exists within the user's knowledge base.
93+
"""
94+
n_manufacturer = ns_base["identity-" + case_utils.local_uuid.local_uuid()]
95+
graph.add((n_manufacturer, NS_RDF.type, NS_UCO_IDENTITY.Identity))
96+
graph.add((n_manufacturer, NS_UCO_CORE.name, rdflib.Literal(name)))
97+
return n_manufacturer
98+
9799
class ExifToolRDFMapper(object):
98100
"""
99101
This class maps ExifTool RDF predicates into UCO objects and Facets.
@@ -207,10 +209,11 @@ def map_raw_and_printconv_iri(self, exiftool_iri):
207209
self.exif_dictionary_dict[dict_key] = v_raw
208210
elif exiftool_iri == "http://ns.exiftool.ca/EXIF/IFD0/1.0/Make":
209211
(v_raw, v_printconv) = self.pop_iri(exiftool_iri)
212+
n_manufacturer = manufacturer_name_to_node(self.graph, self.ns_base, v_printconv)
210213
self.graph.add((
211214
self.n_camera_object_device_facet,
212215
NS_UCO_OBSERVABLE.manufacturer,
213-
v_printconv
216+
n_manufacturer
214217
))
215218
elif exiftool_iri == "http://ns.exiftool.ca/EXIF/IFD0/1.0/Model":
216219
(v_raw, v_printconv) = self.pop_iri(exiftool_iri)
@@ -275,7 +278,7 @@ def map_raw_and_printconv_iri(self, exiftool_iri):
275278
self.graph.add((
276279
self.n_content_data_facet,
277280
NS_UCO_OBSERVABLE.sizeInBytes,
278-
rdflib.Literal(v_raw.toPython(), datatype=NS_XSD.long)
281+
rdflib.Literal(v_raw.toPython(), datatype=NS_XSD.integer)
279282
))
280283
else:
281284
# Somewhat in the name of information preservation, somewhat as a progress marker on converting data: Attach all remaining unconverted properties directly to the ObservableObject. Provide both values to assist with mapping decisions.
@@ -438,7 +441,7 @@ def n_camera_object_device_facet(self):
438441
Initialized on first access.
439442
"""
440443
if self._n_camera_object_device_facet is None:
441-
self._n_camera_object_device_facet = rdflib.BNode()
444+
self._n_camera_object_device_facet = self.ns_base["device-facet-" + case_utils.local_uuid.local_uuid()]
442445
self.graph.add((
443446
self._n_camera_object_device_facet,
444447
NS_RDF.type,
@@ -457,7 +460,7 @@ def n_content_data_facet(self):
457460
Initialized on first access.
458461
"""
459462
if self._n_content_data_facet is None:
460-
self._n_content_data_facet = rdflib.BNode()
463+
self._n_content_data_facet = self.ns_base["content-data-facet-" + case_utils.local_uuid.local_uuid()]
461464
self.graph.add((
462465
self._n_content_data_facet,
463466
NS_RDF.type,
@@ -476,7 +479,7 @@ def n_exif_dictionary_object(self):
476479
Initialized on first access.
477480
"""
478481
if self._n_exif_dictionary_object is None:
479-
self._n_exif_dictionary_object = controlled_dictionary_object_to_node(self.graph, self.exif_dictionary_dict)
482+
self._n_exif_dictionary_object = controlled_dictionary_object_to_node(self.graph, self.ns_base, self.exif_dictionary_dict)
480483
self.graph.add((
481484
self.n_exif_facet,
482485
NS_UCO_OBSERVABLE.exifData,
@@ -490,7 +493,7 @@ def n_exif_facet(self):
490493
Initialized on first access.
491494
"""
492495
if self._n_exif_facet is None:
493-
self._n_exif_facet = rdflib.BNode()
496+
self._n_exif_facet = self.ns_base["exif-facet-" + case_utils.local_uuid.local_uuid()]
494497
self.graph.add((
495498
self._n_exif_facet,
496499
NS_RDF.type,
@@ -509,7 +512,7 @@ def n_file_facet(self):
509512
Initialized on first access.
510513
"""
511514
if self._n_file_facet is None:
512-
self._n_file_facet = rdflib.BNode()
515+
self._n_file_facet = self.ns_base["file-facet-" + case_utils.local_uuid.local_uuid()]
513516
self.graph.add((
514517
self._n_file_facet,
515518
NS_RDF.type,
@@ -542,11 +545,11 @@ def n_location_object_latlong_facet(self):
542545
Initialized on first access.
543546
"""
544547
if self._n_location_object_latlong_facet is None:
545-
self._n_location_object_latlong_facet = rdflib.BNode()
548+
self._n_location_object_latlong_facet = self.ns_base["lat-long-coordinates-facet-" + case_utils.local_uuid.local_uuid()]
546549
self.graph.add((
547550
self._n_location_object_latlong_facet,
548551
NS_RDF.type,
549-
NS_UCO_LOCATION.LatLongCoordinates
552+
NS_UCO_LOCATION.LatLongCoordinatesFacet
550553
))
551554
self.graph.add((
552555
self.n_location_object,
@@ -576,7 +579,7 @@ def n_raster_picture_facet(self):
576579
Initialized on first access.
577580
"""
578581
if self._n_raster_picture_facet is None:
579-
self._n_raster_picture_facet = rdflib.BNode()
582+
self._n_raster_picture_facet = self.ns_base["raster-picture-facet-" + case_utils.local_uuid.local_uuid()]
580583
self.graph.add((
581584
self._n_raster_picture_facet,
582585
NS_RDF.type,
@@ -611,10 +614,15 @@ def n_relationship_object_location(self):
611614
NS_UCO_CORE.target,
612615
self.n_observable_object
613616
))
617+
self.graph.add((
618+
self._n_relationship_object_location,
619+
NS_UCO_CORE.isDirectional,
620+
rdflib.Literal(True)
621+
))
614622
self.graph.add((
615623
self._n_relationship_object_location,
616624
NS_UCO_CORE.kindOfRelationship,
617-
rdflib.Literal("Extracted_From", datatype=NS_UCO_VOCABULARY.ObservableObjectRelationshipVocab)
625+
rdflib.Literal("Extracted_From")
618626
))
619627
return self._n_relationship_object_location
620628

@@ -624,7 +632,7 @@ def n_unix_file_permissions_facet(self):
624632
Initialized on first access.
625633
"""
626634
if self._n_unix_file_permissions_facet is None:
627-
self._n_unix_file_permissions_facet = rdflib.BNode()
635+
self._n_unix_file_permissions_facet = self.ns_base["unix-file-permissions-facet-" + case_utils.local_uuid.local_uuid()]
628636
self.graph.add((
629637
self._n_unix_file_permissions_facet,
630638
NS_RDF.type,
@@ -680,10 +688,10 @@ def main():
680688
out_graph.namespace_manager.bind("exiftool-IFD1", NS_EXIFTOOL_IFD1)
681689
out_graph.namespace_manager.bind("kb", NS_BASE)
682690
out_graph.namespace_manager.bind("uco-core", NS_UCO_CORE)
691+
out_graph.namespace_manager.bind("uco-identity", NS_UCO_IDENTITY)
683692
out_graph.namespace_manager.bind("uco-location", NS_UCO_LOCATION)
684693
out_graph.namespace_manager.bind("uco-observable", NS_UCO_OBSERVABLE)
685694
out_graph.namespace_manager.bind("uco-types", NS_UCO_TYPES)
686-
out_graph.namespace_manager.bind("uco-vocabulary", NS_UCO_VOCABULARY)
687695

688696
exiftool_rdf_mapper = ExifToolRDFMapper(out_graph, NS_BASE)
689697
exiftool_rdf_mapper.map_raw_and_printconv_rdf(args.raw_xml, args.print_conv_xml)

dependencies/CASE-Examples-QC

Submodule CASE-Examples-QC updated 222 files

dependencies/CASE-Utilities-Python

setup.cfg

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,10 @@ classifiers =
1717

1818
[options]
1919
install_requires =
20-
case_utils
21-
# TODO - This constraint on pyparsing can be removed when rdflib Issue #1190 is resolved.
22-
# https://github.com/RDFLib/rdflib/issues/1190
23-
pyparsing < 3.0.0
20+
case_utils >= 0.9.0, < 0.10.0
2421
python-dateutil
25-
rdflib
26-
requests
2722
packages = find:
28-
python_requires = >=3.6
23+
python_requires = >=3.8
2924

3025
[options.entry_points]
3126
console_scripts =

tests/Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ all: \
3434
$(top_srcdir)/setup.cfg \
3535
$(top_srcdir)/setup.py
3636
rm -rf venv
37-
$(PYTHON3) -m virtualenv \
38-
--python=$(PYTHON3) \
37+
$(PYTHON3) -m venv \
3938
venv
4039
source venv/bin/activate \
4140
&& pip install \
4241
--upgrade \
4342
pip \
44-
setuptools
43+
setuptools \
44+
wheel
4545
source venv/bin/activate \
4646
&& cd $(top_srcdir)/dependencies/CASE-Utilities-Python \
4747
&& pip install \

tests/govdocs1/files/799/987/Makefile

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@ top_srcdir := $(shell cd ../../../../.. ; pwd)
1717

1818
qc_srcdir := $(top_srcdir)/dependencies/CASE-Examples-QC
1919

20-
case_srcdir := $(top_srcdir)/dependencies/CASE-Examples-QC/dependencies/CASE-Examples/dependencies/CASE
21-
2220
tests_srcdir := $(top_srcdir)/tests
2321

24-
RDF_TOOLKIT_JAR := $(case_srcdir)/lib/rdf-toolkit.jar
22+
RDF_TOOLKIT_JAR := $(qc_srcdir)/dependencies/CASE-Examples/dependencies/UCO-develop/lib/rdf-toolkit.jar
2523

2624
COMM ?= $(shell which gcomm 2>/dev/null || which comm)
2725
ifeq ($(COMM),)
@@ -34,7 +32,7 @@ $(error sort not found)
3432
endif
3533

3634
all: \
37-
undefined_vocabulary.txt
35+
analysis.json
3836

3937
analysis.json: \
4038
analysis.ttl \
@@ -51,6 +49,9 @@ analysis.json: \
5149
&& python3 $(tests_srcdir)/src/compact.py \
5250
__$@ \
5351
_$@
52+
source $(top_srcdir)/tests/venv/bin/activate \
53+
&& case_validate \
54+
_$@
5455
rm __$@
5556
mv _$@ $@
5657

@@ -60,13 +61,16 @@ analysis.ttl: \
6061
$(top_srcdir)/case_exiftool/__init__.py \
6162
799987_printConv.xml \
6263
799987_raw.xml
63-
export DEMO_UUID_REQUESTING_NONRANDOM=NONRANDOM_REQUESTED \
64+
export CASE_DEMO_NONRANDOM_UUID_BASE=$(top_srcdir) \
6465
&& source $(top_srcdir)/tests/venv/bin/activate \
6566
&& case_exiftool \
6667
--output-format turtle \
6768
--print-conv-xml 799987_printConv.xml \
6869
--raw-xml 799987_raw.xml \
6970
__$@
71+
source $(top_srcdir)/tests/venv/bin/activate \
72+
&& case_validate \
73+
__$@
7074
java -jar $(RDF_TOOLKIT_JAR) \
7175
--infer-base-iri \
7276
--inline-blank-nodes \
@@ -106,36 +110,11 @@ analysis.ttl: \
106110
mv _$@ $@
107111

108112
check: \
109-
undefined_vocabulary.txt
110-
test ! -s undefined_vocabulary.txt \
111-
|| (echo "ERROR:tests/govdocs1/files/799/987/Makefile:The output in analysis.json has undefined CASE or UCO terms. The first few are:" >&2 && head undefined_vocabulary.txt >&2 && exit 1)
113+
analysis.json
112114
source $(top_srcdir)/tests/venv/bin/activate \
113115
&& pytest
114116

115117
clean:
116118
@rm -f \
117119
analysis.json \
118-
analysis.ttl \
119-
undefined_vocabulary.txt
120-
121-
undefined_vocabulary.txt: \
122-
analysis.json \
123-
$(qc_srcdir)/src/vocabulary_used.py \
124-
$(qc_srcdir)/tests/ontology_vocabulary.txt
125-
source $(qc_srcdir)/venv/bin/activate \
126-
&& python3 $(qc_srcdir)/src/vocabulary_used.py \
127-
$< \
128-
> $@___
129-
grep ':' $@___ \
130-
| egrep -v '(ns.exiftool.ca|w3.org)' \
131-
| grep -v '/kb/' \
132-
> $@__
133-
rm $@___
134-
LC_ALL=C \
135-
$(COMM) \
136-
-13 \
137-
$(qc_srcdir)/tests/ontology_vocabulary.txt \
138-
<(LC_ALL=C $(SORT) $@__) \
139-
> $@_
140-
rm $@__
141-
mv $@_ $@
120+
analysis.ttl

tests/govdocs1/files/799/987/undefined_vocabulary.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)