h5rdmtoolbox 2.7.4__tar.gz → 2.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/PKG-INFO +38 -9
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/README.md +32 -7
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/__init__.py +23 -1
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/cli.py +4 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/__init__.py +4 -1
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/ld/metrics.py +288 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/shacl.py +63 -20
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/__init__.py +15 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/chat.py +340 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/errors.py +22 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/executor.py +56 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/interfaces.py +17 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/models.py +85 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/ontology.py +171 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/prompting.py +157 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/providers/__init__.py +6 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/providers/openai_adapter.py +61 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/sparql_guard.py +124 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/summary.py +77 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/static/graph.css +554 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/static/graph.js +800 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/static/style.css +11 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/templates/graph.html +145 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/templates/index.html +0 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/templates/resource.html +0 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/server.py +3334 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/user.py +18 -11
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/download.py +180 -26
- h5rdmtoolbox-2.8.1/h5rdmtoolbox/wrapper/accessors/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/core.py +27 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/jsonld.py +67 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox.egg-info/PKG-INFO +38 -9
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox.egg-info/SOURCES.txt +23 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox.egg-info/entry_points.txt +2 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox.egg-info/requires.txt +6 -1
- h5rdmtoolbox-2.8.1/h5rdmtoolbox.egg-info/top_level.txt +2 -0
- h5rdmtoolbox-2.8.1/h5rdmtoolbox_cli.py +324 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/pyproject.toml +15 -4
- h5rdmtoolbox-2.8.1/tests/test_cli.py +282 -0
- h5rdmtoolbox-2.8.1/tests/test_server.py +2046 -0
- h5rdmtoolbox-2.7.4/h5rdmtoolbox/cli.py +0 -61
- h5rdmtoolbox-2.7.4/h5rdmtoolbox.egg-info/entry_points.txt +0 -2
- h5rdmtoolbox-2.7.4/h5rdmtoolbox.egg-info/top_level.txt +0 -1
- h5rdmtoolbox-2.7.4/tests/test_cli.py +0 -69
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/LICENSE +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/_cfg.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/_repr.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/_version.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/_initialization.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/abstracts.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/core.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/profiles.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/query_templates.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/stores/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/stores/hdf5.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/consts.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/cfg.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/consts.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/core.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/errors.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/generate.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/ontology/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/ontology/h5ontocls.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/ontology/hdf_datatypes.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/references.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/standard_attributes.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/toolbox_validators.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/def_icon.png +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/fluid-v1.yml +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/h5tbx.yaml +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/id_icon.png +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/iri_icon.png +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/ld_icon.png +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/piv-v1.yml +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/style.css +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/tutorial_convention.yaml +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/tutorial_convention_vfuncs.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/tutorial_standard_name_table.yaml +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/type_icon.png +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/filedb.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/objdb.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/query.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/interface.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/lazy.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/mongo.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/errors.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/magnitude.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/normalize.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/onto.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/units.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/vector.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/gui/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/gui/fairify.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/gui/src/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/gui/src/main.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/identifiers.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/layout/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/layout/core.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/_types.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/globals.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/attributes.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/datasets.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/file.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/groups.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/rdf.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/sparql.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/attributes.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/datasets.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/file.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/groups.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/plotting.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/protected_attributes.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/protocols.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/default_metamapper.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/interface.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/core.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/metadata.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/tokens.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/utils.py +0 -0
- /h5rdmtoolbox-2.7.4/h5rdmtoolbox/wrapper/accessors/__init__.py → /h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/templates/file.html +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/tutorial.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/docstring.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/file_io.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/hdf5.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/string.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/__init__.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/accessor.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/accessors/provenance.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/ds_decoder.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/h5attr.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/h5ext.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/h5utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/h5yaml.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/xr2hdf.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox.egg-info/dependency_links.txt +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/setup.cfg +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_config.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_extensions.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_identifiers.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_plotting.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_readme.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_repr.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_user.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_utils.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_version.py +0 -0
- {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_xarray_export.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: h5rdmtoolbox
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.1
|
|
4
4
|
Summary: Supporting a FAIR Research Data lifecycle using Python and HDF5.
|
|
5
5
|
Author-email: Matthias Probst <matth.probst@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -37,7 +37,7 @@ Requires-Dist: python-forge==18.6.0
|
|
|
37
37
|
Requires-Dist: requests>=2.32.4
|
|
38
38
|
Requires-Dist: pydantic~=2.8
|
|
39
39
|
Requires-Dist: rdflib~=7.1
|
|
40
|
-
Requires-Dist:
|
|
40
|
+
Requires-Dist: typer>=0.12
|
|
41
41
|
Requires-Dist: cftime>=1.6.4
|
|
42
42
|
Requires-Dist: pyshacl<1.0.0,>=0.30.1
|
|
43
43
|
Provides-Extra: database
|
|
@@ -78,6 +78,10 @@ Provides-Extra: complete
|
|
|
78
78
|
Requires-Dist: h5rdmtoolbox[gui,test]; extra == "complete"
|
|
79
79
|
Provides-Extra: complete-with-docs
|
|
80
80
|
Requires-Dist: h5rdmtoolbox[complete,docs]; extra == "complete-with-docs"
|
|
81
|
+
Provides-Extra: server
|
|
82
|
+
Requires-Dist: fastapi>=0.95; extra == "server"
|
|
83
|
+
Requires-Dist: uvicorn[standard]>=0.22; extra == "server"
|
|
84
|
+
Requires-Dist: jinja2>=3.1; extra == "server"
|
|
81
85
|
Dynamic: license-file
|
|
82
86
|
|
|
83
87
|
# HDF5 Research Data Management Toolbox
|
|
@@ -165,7 +169,7 @@ Below we show the first two serializations.
|
|
|
165
169
|
schema:description "Room temperature measurements" .
|
|
166
170
|
```
|
|
167
171
|
|
|
168
|
-
`
|
|
172
|
+
`ttl_struc`:
|
|
169
173
|
```ttl
|
|
170
174
|
@prefix hdf: <http://purl.allotrope.org/ontologies/hdf5/1.8#> .
|
|
171
175
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
@@ -233,19 +237,44 @@ hdf:H5T_INTEL_I64 a hdf:Datatype .
|
|
|
233
237
|
hdf:size 4 .
|
|
234
238
|
```
|
|
235
239
|
|
|
236
|
-
|
|
240
|
+
## Command Line Interface
|
|
241
|
+
|
|
242
|
+
The `h5tbx` command provides quick access to linked-data serialization and local inspection from the shell.
|
|
243
|
+
|
|
244
|
+
Use `h5tbx ld dump example.h5` to print Turtle RDF, or write a specific format with:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
h5tbx ld dump example.h5 -o example.jsonld
|
|
248
|
+
```
|
|
237
249
|
|
|
238
|
-
|
|
250
|
+
The dump command accepts `--format`, `--file-uri`, `--prefix`, `--structural=false`, and `--contextual=false`, so you can choose RDF output, assign stable RDF subject URIs, bind a compact prefix, and include only structural or user-provided contextual metadata when needed.
|
|
239
251
|
|
|
240
|
-
|
|
252
|
+
To inspect files in a browser, run:
|
|
241
253
|
|
|
242
|
-
|
|
254
|
+
```bash
|
|
255
|
+
h5tbx serve example.h5
|
|
256
|
+
h5tbx serve data/ --h5ext=.h5 --h5ext=.hdf5
|
|
257
|
+
```
|
|
258
|
+
This allows you to serve a single HDF5 file or a directory of HDF5 files, and view their metadata and structure in a web interface.
|
|
243
259
|
|
|
244
|
-
|
|
260
|
+
You may before SPARQL queries or view the File in a graph as shown below:
|
|
245
261
|
|
|
246
|
-
|
|
262
|
+
<p align="center">
|
|
263
|
+
<img src="docs/_static/screenshot-served-hdf5-as-graph.png" alt="Serve UI screenshot" width="640">
|
|
264
|
+
</p>
|
|
247
265
|
|
|
248
266
|
|
|
267
|
+
The same graph metrics are available from Python:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
import h5rdmtoolbox as h5tbx
|
|
271
|
+
|
|
272
|
+
metrics = h5tbx.compute_metrics("example.h5")
|
|
273
|
+
|
|
274
|
+
with h5tbx.File("example.h5") as h5:
|
|
275
|
+
metrics = h5.metrics()
|
|
276
|
+
```
|
|
277
|
+
|
|
249
278
|
## 🚀 Key Features
|
|
250
279
|
|
|
251
280
|
- **🔗 HDF5 + [Xarray](https://docs.xarray.dev/en/stable/) Integration**: Seamless access to metadata during data analysis with native xarray support
|
|
@@ -83,7 +83,7 @@ Below we show the first two serializations.
|
|
|
83
83
|
schema:description "Room temperature measurements" .
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
`
|
|
86
|
+
`ttl_struc`:
|
|
87
87
|
```ttl
|
|
88
88
|
@prefix hdf: <http://purl.allotrope.org/ontologies/hdf5/1.8#> .
|
|
89
89
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
@@ -151,19 +151,44 @@ hdf:H5T_INTEL_I64 a hdf:Datatype .
|
|
|
151
151
|
hdf:size 4 .
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
## Command Line Interface
|
|
155
|
+
|
|
156
|
+
The `h5tbx` command provides quick access to linked-data serialization and local inspection from the shell.
|
|
157
|
+
|
|
158
|
+
Use `h5tbx ld dump example.h5` to print Turtle RDF, or write a specific format with:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
h5tbx ld dump example.h5 -o example.jsonld
|
|
162
|
+
```
|
|
155
163
|
|
|
156
|
-
|
|
164
|
+
The dump command accepts `--format`, `--file-uri`, `--prefix`, `--structural=false`, and `--contextual=false`, so you can choose RDF output, assign stable RDF subject URIs, bind a compact prefix, and include only structural or user-provided contextual metadata when needed.
|
|
157
165
|
|
|
158
|
-
|
|
166
|
+
To inspect files in a browser, run:
|
|
159
167
|
|
|
160
|
-
|
|
168
|
+
```bash
|
|
169
|
+
h5tbx serve example.h5
|
|
170
|
+
h5tbx serve data/ --h5ext=.h5 --h5ext=.hdf5
|
|
171
|
+
```
|
|
172
|
+
This allows you to serve a single HDF5 file or a directory of HDF5 files, and view their metadata and structure in a web interface.
|
|
161
173
|
|
|
162
|
-
|
|
174
|
+
You may before SPARQL queries or view the File in a graph as shown below:
|
|
163
175
|
|
|
164
|
-
|
|
176
|
+
<p align="center">
|
|
177
|
+
<img src="docs/_static/screenshot-served-hdf5-as-graph.png" alt="Serve UI screenshot" width="640">
|
|
178
|
+
</p>
|
|
165
179
|
|
|
166
180
|
|
|
181
|
+
The same graph metrics are available from Python:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
import h5rdmtoolbox as h5tbx
|
|
185
|
+
|
|
186
|
+
metrics = h5tbx.compute_metrics("example.h5")
|
|
187
|
+
|
|
188
|
+
with h5tbx.File("example.h5") as h5:
|
|
189
|
+
metrics = h5.metrics()
|
|
190
|
+
```
|
|
191
|
+
|
|
167
192
|
## 🚀 Key Features
|
|
168
193
|
|
|
169
194
|
- **🔗 HDF5 + [Xarray](https://docs.xarray.dev/en/stable/) Integration**: Seamless access to metadata during data analysis with native xarray support
|
|
@@ -19,7 +19,7 @@ _stream_handler = logging.StreamHandler()
|
|
|
19
19
|
_stream_handler.setLevel(DEFAULT_LOGGING_LEVEL)
|
|
20
20
|
_stream_handler.setFormatter(_formatter)
|
|
21
21
|
|
|
22
|
-
_file_handler = RotatingFileHandler(USER_LOG_DIR / 'h5rdmtoolbox.log')
|
|
22
|
+
_file_handler = RotatingFileHandler(USER_LOG_DIR / 'h5rdmtoolbox.log', delay=True)
|
|
23
23
|
_file_handler.setLevel(logging.DEBUG) # log everything to file!
|
|
24
24
|
_file_handler.setFormatter(_formatter)
|
|
25
25
|
|
|
@@ -146,6 +146,28 @@ def get_ld(
|
|
|
146
146
|
)
|
|
147
147
|
|
|
148
148
|
|
|
149
|
+
def compute_metrics(
|
|
150
|
+
hdf_filename: Union[str, pathlib.Path],
|
|
151
|
+
structural: bool = True,
|
|
152
|
+
contextual: bool = True,
|
|
153
|
+
skipND: int = 1,
|
|
154
|
+
file_uri: Optional[str] = None,
|
|
155
|
+
context: Optional[Dict] = None,
|
|
156
|
+
rdf_mappings: Dict[str, RDFMappingEntry] = None,
|
|
157
|
+
) -> Dict[str, object]:
|
|
158
|
+
"""Compute RDF knowledge graph metrics for an HDF5 file."""
|
|
159
|
+
from .ld.metrics import compute_metrics as _compute_metrics
|
|
160
|
+
return _compute_metrics(
|
|
161
|
+
hdf_filename,
|
|
162
|
+
structural=structural,
|
|
163
|
+
contextual=contextual,
|
|
164
|
+
skipND=skipND,
|
|
165
|
+
file_uri=file_uri,
|
|
166
|
+
context=context,
|
|
167
|
+
rdf_mappings=rdf_mappings,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
149
171
|
def _optimize_ld_context(graph: rdflib.Graph, context: Optional[Dict]) -> Dict:
|
|
150
172
|
from .ld import optimize_context
|
|
151
173
|
return optimize_context(graph, context or {})
|
|
@@ -12,7 +12,7 @@ from .utils import optimize_context
|
|
|
12
12
|
|
|
13
13
|
BINARY_AS_STRING = True
|
|
14
14
|
|
|
15
|
-
__all__ = ["get_ld", "hdf2jsonld", "hdf2ttl", "sparql"]
|
|
15
|
+
__all__ = ["get_ld", "hdf2jsonld", "hdf2ttl", "sparql", "compute_metrics", "compute_graph_metrics"]
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def _validate_file_uri(file_uri: Optional[str]) -> None:
|
|
@@ -280,3 +280,6 @@ def _hdf2ld(
|
|
|
280
280
|
f.write(_serialize_graph(graph, fmt=fmt, indent=indent, context=context))
|
|
281
281
|
|
|
282
282
|
return metadata_filename
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
from .metrics import compute_graph_metrics, compute_metrics # noqa: E402
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Knowledge graph metrics for RDF extracted from HDF5 files."""
|
|
2
|
+
|
|
3
|
+
import pathlib
|
|
4
|
+
import re
|
|
5
|
+
from typing import Dict, Optional, Union
|
|
6
|
+
|
|
7
|
+
import rdflib
|
|
8
|
+
|
|
9
|
+
from . import get_ld
|
|
10
|
+
|
|
11
|
+
STANDARD_PREFIXES = {
|
|
12
|
+
"dcat": "http://www.w3.org/ns/dcat#",
|
|
13
|
+
"dcterms": "http://purl.org/dc/terms/",
|
|
14
|
+
"doi": "https://doi.org/",
|
|
15
|
+
"foaf": "http://xmlns.com/foaf/0.1/",
|
|
16
|
+
"prof": "http://www.w3.org/ns/dx/prof/",
|
|
17
|
+
"prov": "http://www.w3.org/ns/prov#",
|
|
18
|
+
"qudt": "http://qudt.org/schema/qudt/",
|
|
19
|
+
"quantitykind": "http://qudt.org/vocab/quantitykind/",
|
|
20
|
+
"schema": "https://schema.org/",
|
|
21
|
+
"skos": "http://www.w3.org/2004/02/skos/core#",
|
|
22
|
+
"ssno": "https://matthiasprobst.github.io/ssno#",
|
|
23
|
+
"unit": "http://qudt.org/vocab/unit/",
|
|
24
|
+
"zenodo": "https://zenodo.org/records/",
|
|
25
|
+
"zenodo_record": "https://zenodo.org/record/",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
ZENODO_COMPACT_PREFIXES = (
|
|
29
|
+
("https://doi.org/10.5281/zenodo.", "zen"),
|
|
30
|
+
("https://zenodo.org/records/", "rzen"),
|
|
31
|
+
("https://zenodo.org/record/", "rzen"),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def bind_standard_prefixes(graph: rdflib.Graph) -> None:
|
|
36
|
+
"""Bind common namespaces without replacing prefixes already defined by the graph."""
|
|
37
|
+
for prefix, namespace in STANDARD_PREFIXES.items():
|
|
38
|
+
graph.bind(prefix, rdflib.Namespace(namespace), override=False, replace=False)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def graph_label(value, rdf_graph: Optional[rdflib.Graph] = None) -> str:
|
|
42
|
+
"""Return a compact display label for an RDF term."""
|
|
43
|
+
if isinstance(value, rdflib.URIRef):
|
|
44
|
+
namespace_manager = rdf_graph.namespace_manager if rdf_graph is not None else rdflib.Graph().namespace_manager
|
|
45
|
+
text = str(value)
|
|
46
|
+
for namespace, prefix in ZENODO_COMPACT_PREFIXES:
|
|
47
|
+
if text.startswith(namespace) and text != namespace:
|
|
48
|
+
return f"{prefix}:{text[len(namespace):]}"
|
|
49
|
+
try:
|
|
50
|
+
normalized = str(namespace_manager.normalizeUri(value))
|
|
51
|
+
if not normalized.startswith("<") and not re.match(r"^ns\d+:", normalized):
|
|
52
|
+
return normalized
|
|
53
|
+
except Exception:
|
|
54
|
+
pass
|
|
55
|
+
for prefix, namespace in STANDARD_PREFIXES.items():
|
|
56
|
+
if text.startswith(namespace) and text != namespace:
|
|
57
|
+
return f"{prefix}:{text[len(namespace):]}"
|
|
58
|
+
try:
|
|
59
|
+
return namespace_manager.qname(value)
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
if rdf_graph is not None:
|
|
63
|
+
namespaces = sorted(
|
|
64
|
+
((prefix, str(namespace)) for prefix, namespace in rdf_graph.namespaces() if prefix),
|
|
65
|
+
key=lambda item: len(item[1]),
|
|
66
|
+
reverse=True,
|
|
67
|
+
)
|
|
68
|
+
for prefix, namespace in namespaces:
|
|
69
|
+
if text.startswith(namespace) and text != namespace:
|
|
70
|
+
return f"{prefix}:{text[len(namespace):]}"
|
|
71
|
+
return text.rsplit("#", 1)[-1].rsplit("/", 1)[-1] or text
|
|
72
|
+
text = str(value)
|
|
73
|
+
return text if len(text) <= 48 else f"{text[:45]}..."
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _percent(numerator: int, denominator: int) -> float:
|
|
77
|
+
return (numerator / denominator * 100.0) if denominator else 0.0
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _external_namespace(value) -> str:
|
|
81
|
+
text = str(value)
|
|
82
|
+
if "#" in text:
|
|
83
|
+
return text.rsplit("#", 1)[0] + "#"
|
|
84
|
+
if "/" in text:
|
|
85
|
+
return text.rsplit("/", 1)[0] + "/"
|
|
86
|
+
return text
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _resource_terms(subject, predicate, obj) -> list:
|
|
90
|
+
return [
|
|
91
|
+
term
|
|
92
|
+
for term in (subject, predicate, obj)
|
|
93
|
+
if isinstance(term, (rdflib.URIRef, rdflib.BNode))
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def compute_graph_metrics(
|
|
98
|
+
rdf_graph: rdflib.Graph,
|
|
99
|
+
base_namespace: Optional[str] = None,
|
|
100
|
+
compute_distances: bool = True,
|
|
101
|
+
distance_node_limit: int = 2000,
|
|
102
|
+
) -> Dict[str, object]:
|
|
103
|
+
"""Compute RDF knowledge graph metrics for an existing graph."""
|
|
104
|
+
bind_standard_prefixes(rdf_graph)
|
|
105
|
+
triples = list(rdf_graph)
|
|
106
|
+
subjects = set()
|
|
107
|
+
objects = set()
|
|
108
|
+
predicates = set()
|
|
109
|
+
iris = set()
|
|
110
|
+
blank_nodes = set()
|
|
111
|
+
resource_nodes = set()
|
|
112
|
+
literal_count = 0
|
|
113
|
+
untyped_literal_count = 0
|
|
114
|
+
empty_literal_count = 0
|
|
115
|
+
predicate_counts = {}
|
|
116
|
+
subject_triple_counts = {}
|
|
117
|
+
class_instances = {}
|
|
118
|
+
datatype_counts = {}
|
|
119
|
+
language_counts = {}
|
|
120
|
+
labels = set()
|
|
121
|
+
typed_subjects = set()
|
|
122
|
+
same_as_count = 0
|
|
123
|
+
external_iri_counts = {}
|
|
124
|
+
out_degree = {}
|
|
125
|
+
in_degree = {}
|
|
126
|
+
adjacency = {}
|
|
127
|
+
|
|
128
|
+
for subject, predicate, obj in triples:
|
|
129
|
+
subjects.add(subject)
|
|
130
|
+
predicates.add(predicate)
|
|
131
|
+
objects.add(obj)
|
|
132
|
+
subject_triple_counts[subject] = subject_triple_counts.get(subject, 0) + 1
|
|
133
|
+
|
|
134
|
+
for term in _resource_terms(subject, predicate, obj):
|
|
135
|
+
if isinstance(term, rdflib.URIRef):
|
|
136
|
+
iris.add(term)
|
|
137
|
+
namespace = _external_namespace(term)
|
|
138
|
+
if not base_namespace or not str(term).startswith(base_namespace):
|
|
139
|
+
external_iri_counts[namespace] = external_iri_counts.get(namespace, 0) + 1
|
|
140
|
+
elif isinstance(term, rdflib.BNode):
|
|
141
|
+
blank_nodes.add(term)
|
|
142
|
+
|
|
143
|
+
predicate_label = graph_label(predicate, rdf_graph)
|
|
144
|
+
predicate_counts[predicate_label] = predicate_counts.get(predicate_label, 0) + 1
|
|
145
|
+
|
|
146
|
+
if predicate == rdflib.RDF.type:
|
|
147
|
+
typed_subjects.add(subject)
|
|
148
|
+
class_label = graph_label(obj, rdf_graph)
|
|
149
|
+
class_instances.setdefault(class_label, set()).add(subject)
|
|
150
|
+
|
|
151
|
+
if predicate == rdflib.RDFS.label:
|
|
152
|
+
labels.add(subject)
|
|
153
|
+
|
|
154
|
+
if predicate == rdflib.OWL.sameAs:
|
|
155
|
+
same_as_count += 1
|
|
156
|
+
|
|
157
|
+
resource_nodes.add(subject)
|
|
158
|
+
|
|
159
|
+
if isinstance(obj, rdflib.Literal):
|
|
160
|
+
literal_count += 1
|
|
161
|
+
datatype_label = graph_label(obj.datatype, rdf_graph) if obj.datatype else "None"
|
|
162
|
+
datatype_counts[datatype_label] = datatype_counts.get(datatype_label, 0) + 1
|
|
163
|
+
language_label = obj.language or "None"
|
|
164
|
+
language_counts[language_label] = language_counts.get(language_label, 0) + 1
|
|
165
|
+
if obj.datatype is None and obj.language is None:
|
|
166
|
+
untyped_literal_count += 1
|
|
167
|
+
if str(obj) == "":
|
|
168
|
+
empty_literal_count += 1
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
resource_nodes.add(obj)
|
|
172
|
+
out_degree[subject] = out_degree.get(subject, 0) + 1
|
|
173
|
+
in_degree[obj] = in_degree.get(obj, 0) + 1
|
|
174
|
+
adjacency.setdefault(subject, set()).add(obj)
|
|
175
|
+
adjacency.setdefault(obj, set()).add(subject)
|
|
176
|
+
|
|
177
|
+
seen = set()
|
|
178
|
+
component_sizes = []
|
|
179
|
+
for node in resource_nodes:
|
|
180
|
+
if node in seen:
|
|
181
|
+
continue
|
|
182
|
+
stack = [node]
|
|
183
|
+
seen.add(node)
|
|
184
|
+
size = 0
|
|
185
|
+
while stack:
|
|
186
|
+
current = stack.pop()
|
|
187
|
+
size += 1
|
|
188
|
+
for neighbor in adjacency.get(current, set()):
|
|
189
|
+
if neighbor not in seen:
|
|
190
|
+
seen.add(neighbor)
|
|
191
|
+
stack.append(neighbor)
|
|
192
|
+
component_sizes.append(size)
|
|
193
|
+
|
|
194
|
+
resource_count = len(resource_nodes)
|
|
195
|
+
largest_distance = None
|
|
196
|
+
largest_distance_computed = compute_distances and resource_count <= distance_node_limit
|
|
197
|
+
if largest_distance_computed:
|
|
198
|
+
largest_distance = 0
|
|
199
|
+
for start in resource_nodes:
|
|
200
|
+
distances = {start: 0}
|
|
201
|
+
queue = [start]
|
|
202
|
+
for current in queue:
|
|
203
|
+
for neighbor in adjacency.get(current, set()):
|
|
204
|
+
if neighbor not in distances:
|
|
205
|
+
distances[neighbor] = distances[current] + 1
|
|
206
|
+
queue.append(neighbor)
|
|
207
|
+
if distances:
|
|
208
|
+
largest_distance = max(largest_distance, max(distances.values()))
|
|
209
|
+
|
|
210
|
+
subjects_without_type = len(subjects - typed_subjects)
|
|
211
|
+
subjects_with_labels = len(labels)
|
|
212
|
+
subjects_missing_labels = len(subjects - labels)
|
|
213
|
+
duplicate_triples = len(triples) - len(set(triples))
|
|
214
|
+
subjects_with_one_triple = sum(1 for count in subject_triple_counts.values() if count == 1)
|
|
215
|
+
weakly_connected_nodes = sum(
|
|
216
|
+
1 for node in resource_nodes
|
|
217
|
+
if in_degree.get(node, 0) + out_degree.get(node, 0) <= 1
|
|
218
|
+
)
|
|
219
|
+
class_counts = {label: len(instances) for label, instances in class_instances.items()}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
"triples": len(triples),
|
|
223
|
+
"subjects": len(subjects),
|
|
224
|
+
"predicates": len(predicates),
|
|
225
|
+
"objects": len(objects),
|
|
226
|
+
"iri_count": len(iris),
|
|
227
|
+
"blank_nodes": len(blank_nodes),
|
|
228
|
+
"literals": literal_count,
|
|
229
|
+
"avg_triples_per_subject": len(triples) / len(subjects) if subjects else 0.0,
|
|
230
|
+
"classes": len(class_counts),
|
|
231
|
+
"subjects_without_type": subjects_without_type,
|
|
232
|
+
"resource_nodes": resource_count,
|
|
233
|
+
"avg_out_degree": sum(out_degree.values()) / resource_count if resource_count else 0.0,
|
|
234
|
+
"avg_in_degree": sum(in_degree.values()) / resource_count if resource_count else 0.0,
|
|
235
|
+
"weakly_connected_nodes": weakly_connected_nodes,
|
|
236
|
+
"components": len(component_sizes),
|
|
237
|
+
"largest_component": max(component_sizes, default=0),
|
|
238
|
+
"largest_distance": largest_distance,
|
|
239
|
+
"largest_distance_computed": largest_distance_computed,
|
|
240
|
+
"largest_distance_node_limit": distance_node_limit,
|
|
241
|
+
"datatype_distribution": sorted(datatype_counts.items(), key=lambda item: (-item[1], item[0])),
|
|
242
|
+
"language_distribution": sorted(language_counts.items(), key=lambda item: (-item[1], item[0])),
|
|
243
|
+
"untyped_literals": untyped_literal_count,
|
|
244
|
+
"empty_literals": empty_literal_count,
|
|
245
|
+
"subjects_with_labels": subjects_with_labels,
|
|
246
|
+
"subjects_missing_labels": subjects_missing_labels,
|
|
247
|
+
"label_coverage": _percent(subjects_with_labels, len(subjects)),
|
|
248
|
+
"duplicate_triples": duplicate_triples,
|
|
249
|
+
"subjects_with_one_triple": subjects_with_one_triple,
|
|
250
|
+
"blank_node_percentage": _percent(len(blank_nodes), resource_count),
|
|
251
|
+
"same_as_count": same_as_count,
|
|
252
|
+
"external_iri_count": sum(external_iri_counts.values()),
|
|
253
|
+
"top_external_namespaces": sorted(external_iri_counts.items(), key=lambda item: (-item[1], item[0]))[:10],
|
|
254
|
+
"top_predicates": sorted(predicate_counts.items(), key=lambda item: (-item[1], item[0]))[:10],
|
|
255
|
+
"rare_predicates": sorted(predicate_counts.items(), key=lambda item: (item[1], item[0]))[:10],
|
|
256
|
+
"predicate_distribution": sorted(predicate_counts.items(), key=lambda item: (-item[1], item[0])),
|
|
257
|
+
"top_classes": sorted(class_counts.items(), key=lambda item: (-item[1], item[0]))[:10],
|
|
258
|
+
"top_out_degree": [
|
|
259
|
+
(graph_label(node, rdf_graph), count)
|
|
260
|
+
for node, count in sorted(out_degree.items(), key=lambda item: (-item[1], graph_label(item[0], rdf_graph)))[:10]
|
|
261
|
+
],
|
|
262
|
+
"top_in_degree": [
|
|
263
|
+
(graph_label(node, rdf_graph), count)
|
|
264
|
+
for node, count in sorted(in_degree.items(), key=lambda item: (-item[1], graph_label(item[0], rdf_graph)))[:10]
|
|
265
|
+
],
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def compute_metrics(
|
|
270
|
+
hdf_filename: Union[str, pathlib.Path],
|
|
271
|
+
structural: bool = True,
|
|
272
|
+
contextual: bool = True,
|
|
273
|
+
file_uri: Optional[str] = None,
|
|
274
|
+
skipND: Optional[int] = 1,
|
|
275
|
+
context: Optional[Dict] = None,
|
|
276
|
+
rdf_mappings: Optional[Dict] = None,
|
|
277
|
+
) -> Dict[str, object]:
|
|
278
|
+
"""Compute RDF knowledge graph metrics for an HDF5 file."""
|
|
279
|
+
graph = get_ld(
|
|
280
|
+
hdf_filename,
|
|
281
|
+
structural=structural,
|
|
282
|
+
contextual=contextual,
|
|
283
|
+
file_uri=file_uri,
|
|
284
|
+
skipND=skipND,
|
|
285
|
+
context=context,
|
|
286
|
+
rdf_mappings=rdf_mappings,
|
|
287
|
+
)
|
|
288
|
+
return compute_graph_metrics(graph, base_namespace=file_uri)
|
|
@@ -41,16 +41,34 @@ def _parse_shacl(shacl: Union[str, pathlib.Path, rdflib.Graph], format) -> rdfli
|
|
|
41
41
|
return shacl_graph
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
def _parse_ont_graph(
|
|
45
|
+
ont_graph: Union[str, pathlib.Path, rdflib.Graph],
|
|
46
|
+
ont_graph_format: str,
|
|
47
|
+
) -> rdflib.Graph:
|
|
48
|
+
if isinstance(ont_graph, rdflib.Graph):
|
|
49
|
+
return ont_graph
|
|
50
|
+
if isinstance(ont_graph, pathlib.Path):
|
|
51
|
+
return rdflib.Graph().parse(ont_graph, format=ont_graph_format)
|
|
52
|
+
if isinstance(ont_graph, str):
|
|
53
|
+
return rdflib.Graph().parse(data=ont_graph, format=ont_graph_format)
|
|
54
|
+
raise TypeError(
|
|
55
|
+
"ont_graph must be an rdflib.Graph, pathlib.Path, or RDF string."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
44
59
|
def validate_hdf(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
*,
|
|
61
|
+
hdf_data: Union[str, rdflib.Graph] = None,
|
|
62
|
+
hdf_source: Union[h5py.File, pathlib.Path] = None,
|
|
63
|
+
shacl_data: Union[str, rdflib.Graph] = None,
|
|
64
|
+
shacl_source: Union[str, pathlib.Path] = None,
|
|
65
|
+
hdf_file_uri="https://example.org/hdf5file#",
|
|
66
|
+
shacl_format: str = "turtle",
|
|
67
|
+
hdf_data_format: str = "turtle",
|
|
68
|
+
ont_graph: Union[str, rdflib.Graph, pathlib.Path] = None,
|
|
69
|
+
ont_graph_format: str = "turtle",
|
|
70
|
+
merge_ont_graph_into_data: bool = True,
|
|
71
|
+
**pyshacl_kwargs,
|
|
54
72
|
) -> ValidationResult:
|
|
55
73
|
"""Validate an HDF5 file against SHACL shapes.
|
|
56
74
|
|
|
@@ -61,6 +79,8 @@ def validate_hdf(
|
|
|
61
79
|
it is assumed to be in Turtle format (you may overwrite this by passing hdf_data_format).
|
|
62
80
|
hdf_source : Union[h5py.File, pathlib.Path], optional
|
|
63
81
|
The path to the HDF5 file.
|
|
82
|
+
hdf_file_uri: str, optional
|
|
83
|
+
The base URI to use for the HDF5 file when generating RDF. Default is "
|
|
64
84
|
shacl_data : Union[str, rdflib.Graph], optional
|
|
65
85
|
The SHACL shapes as a string or rdflib.Graph. If is string is provided
|
|
66
86
|
it is assumed to be in Turtle format.
|
|
@@ -70,6 +90,16 @@ def validate_hdf(
|
|
|
70
90
|
The format of the SHACL shapes string. Default is 'turtle'.
|
|
71
91
|
hdf_data_format : str, optional
|
|
72
92
|
The format of the HDF5 data string. Default is 'turtle'.
|
|
93
|
+
ont_graph : Union[str, rdflib.Graph, pathlib.Path], optional
|
|
94
|
+
Ontology graph to use during validation. If `merge_ont_graph_into_data`
|
|
95
|
+
is True, it is merged into the generated HDF RDF data graph before
|
|
96
|
+
validation. Otherwise it is passed to pySHACL as `ont_graph`.
|
|
97
|
+
ont_graph_format : str, optional
|
|
98
|
+
The format of the ontology graph string. Default is 'turtle'.
|
|
99
|
+
merge_ont_graph_into_data : bool, optional
|
|
100
|
+
If True, the ontology graph is merged into the generated HDF RDF data graph
|
|
101
|
+
before validation. This is useful for making ontology triples visible to
|
|
102
|
+
SHACL constraints.
|
|
73
103
|
**pyshacl_kwargs
|
|
74
104
|
Additional keyword arguments to pass to pyshacl.validate().
|
|
75
105
|
|
|
@@ -98,7 +128,7 @@ def validate_hdf(
|
|
|
98
128
|
if hdf_data is not None:
|
|
99
129
|
if isinstance(hdf_data, str):
|
|
100
130
|
h5_graph = rdflib.Graph()
|
|
101
|
-
h5_graph.parse(hdf_data, format=
|
|
131
|
+
h5_graph.parse(data=hdf_data, format=hdf_data_format)
|
|
102
132
|
elif isinstance(hdf_data, rdflib.Graph):
|
|
103
133
|
h5_graph = hdf_data
|
|
104
134
|
else:
|
|
@@ -117,6 +147,9 @@ def validate_hdf(
|
|
|
117
147
|
shacl_source=shacl_source,
|
|
118
148
|
hdf_file_uri=hdf_file_uri,
|
|
119
149
|
hdf_data_format=hdf_data_format,
|
|
150
|
+
ont_graph=ont_graph,
|
|
151
|
+
ont_graph_format=ont_graph_format,
|
|
152
|
+
merge_ont_graph_into_data=merge_ont_graph_into_data,
|
|
120
153
|
**pyshacl_kwargs,
|
|
121
154
|
)
|
|
122
155
|
if not isinstance(hdf_source, h5py.File):
|
|
@@ -127,6 +160,12 @@ def validate_hdf(
|
|
|
127
160
|
h5_graph2 = get_contextual_ld(hdf_source, file_uri=hdf_file_uri)
|
|
128
161
|
h5_graph = h5_graph1 + h5_graph2
|
|
129
162
|
|
|
163
|
+
parsed_ont_graph = None
|
|
164
|
+
if ont_graph is not None:
|
|
165
|
+
parsed_ont_graph = _parse_ont_graph(ont_graph, ont_graph_format)
|
|
166
|
+
if merge_ont_graph_into_data:
|
|
167
|
+
h5_graph += parsed_ont_graph
|
|
168
|
+
|
|
130
169
|
shacl_graph = None
|
|
131
170
|
if shacl_data is not None:
|
|
132
171
|
if isinstance(shacl_data, str):
|
|
@@ -144,7 +183,10 @@ def validate_hdf(
|
|
|
144
183
|
shacl_graph.parse(source=shacl_source, format=shacl_format)
|
|
145
184
|
|
|
146
185
|
conforms, results_graph, results_text = _validate_graphs(
|
|
147
|
-
h5_graph,
|
|
186
|
+
h5_graph,
|
|
187
|
+
shacl_graph,
|
|
188
|
+
ont_graph=None if merge_ont_graph_into_data else parsed_ont_graph,
|
|
189
|
+
**pyshacl_kwargs
|
|
148
190
|
)
|
|
149
191
|
return ValidationResult(
|
|
150
192
|
conforms=conforms,
|
|
@@ -156,13 +198,14 @@ def validate_hdf(
|
|
|
156
198
|
|
|
157
199
|
|
|
158
200
|
def _validate_graphs(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
201
|
+
data_graph,
|
|
202
|
+
shacl_graph,
|
|
203
|
+
inference="rdfs",
|
|
204
|
+
abort_on_first=False,
|
|
205
|
+
meta_shacl=False,
|
|
206
|
+
advanced=False,
|
|
207
|
+
debug=False,
|
|
208
|
+
**pyshacl_kwargs
|
|
166
209
|
):
|
|
167
210
|
"""
|
|
168
211
|
Validate a data graph against a SHACL shapes graph.
|
|
@@ -182,7 +225,6 @@ def _validate_graphs(
|
|
|
182
225
|
- results_graph: An RDF graph with validation results.
|
|
183
226
|
- results_text: A textual summary of the validation results.
|
|
184
227
|
"""
|
|
185
|
-
|
|
186
228
|
conforms, results_graph, results_text = pyshacl_validate(
|
|
187
229
|
data_graph,
|
|
188
230
|
shacl_graph=shacl_graph,
|
|
@@ -191,6 +233,7 @@ def _validate_graphs(
|
|
|
191
233
|
meta_shacl=meta_shacl,
|
|
192
234
|
advanced=advanced,
|
|
193
235
|
debug=debug,
|
|
236
|
+
**pyshacl_kwargs
|
|
194
237
|
)
|
|
195
238
|
|
|
196
239
|
return conforms, results_graph, results_text
|
|
@@ -208,7 +251,7 @@ def _get_messages(results_graph):
|
|
|
208
251
|
"""
|
|
209
252
|
messages = []
|
|
210
253
|
for s, p, o in results_graph.triples(
|
|
211
|
-
|
|
254
|
+
(None, rdflib.namespace.SH.resultMessage, None)
|
|
212
255
|
):
|
|
213
256
|
messages.append(str(o))
|
|
214
257
|
return messages
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""LLM-based chat support for HDF5 metadata graphs."""
|
|
2
|
+
|
|
3
|
+
from .chat import chat
|
|
4
|
+
from .models import ChatResponse, ChatSession, ChatSessionState, QueryAttempt
|
|
5
|
+
from .providers.openai_adapter import OpenAIChatClient
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"chat",
|
|
9
|
+
"ChatResponse",
|
|
10
|
+
"ChatSession",
|
|
11
|
+
"ChatSessionState",
|
|
12
|
+
"QueryAttempt",
|
|
13
|
+
"OpenAIChatClient",
|
|
14
|
+
]
|
|
15
|
+
|