h5rdmtoolbox 2.6.2__tar.gz → 2.7.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.
Files changed (131) hide show
  1. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/PKG-INFO +1 -1
  2. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/__init__.py +4 -1
  3. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/abstracts.py +2 -1
  4. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/core.py +7 -1
  5. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/__init__.py +7 -3
  6. h5rdmtoolbox-2.7.1/h5rdmtoolbox/ld/_types.py +11 -0
  7. h5rdmtoolbox-2.7.1/h5rdmtoolbox/ld/hdf/attributes.py +57 -0
  8. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/user/attributes.py +6 -4
  9. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/user/datasets.py +3 -1
  10. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/user/file.py +11 -4
  11. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/user/groups.py +9 -4
  12. h5rdmtoolbox-2.7.1/h5rdmtoolbox/ld/user/utils.py +33 -0
  13. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/utils.py +25 -4
  14. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/utils.py +4 -5
  15. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/core.py +5 -2
  16. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox.egg-info/PKG-INFO +1 -1
  17. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox.egg-info/SOURCES.txt +1 -0
  18. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/pyproject.toml +1 -1
  19. h5rdmtoolbox-2.6.2/h5rdmtoolbox/ld/hdf/attributes.py +0 -33
  20. h5rdmtoolbox-2.6.2/h5rdmtoolbox/ld/user/utils.py +0 -8
  21. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/LICENSE +0 -0
  22. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/README.md +0 -0
  23. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/_cfg.py +0 -0
  24. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/_repr.py +0 -0
  25. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/_version.py +0 -0
  26. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/__init__.py +0 -0
  27. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/_initialization.py +0 -0
  28. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/profiles.py +0 -0
  29. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/query_templates.py +0 -0
  30. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/stores/__init__.py +0 -0
  31. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/stores/hdf5.py +0 -0
  32. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/catalog/utils.py +0 -0
  33. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/cli.py +0 -0
  34. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/consts.py +0 -0
  35. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/__init__.py +0 -0
  36. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/cfg.py +0 -0
  37. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/consts.py +0 -0
  38. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/core.py +0 -0
  39. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/errors.py +0 -0
  40. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/generate.py +0 -0
  41. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/ontology/__init__.py +0 -0
  42. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/ontology/h5ontocls.py +0 -0
  43. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/ontology/hdf_datatypes.py +0 -0
  44. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/references.py +0 -0
  45. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/standard_attributes.py +0 -0
  46. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/toolbox_validators.py +0 -0
  47. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/convention/utils.py +0 -0
  48. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/def_icon.png +0 -0
  49. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/fluid-v1.yml +0 -0
  50. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/h5tbx.yaml +0 -0
  51. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/id_icon.png +0 -0
  52. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/iri_icon.png +0 -0
  53. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/ld_icon.png +0 -0
  54. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/piv-v1.yml +0 -0
  55. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/style.css +0 -0
  56. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/tutorial_convention.yaml +0 -0
  57. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/tutorial_convention_vfuncs.py +0 -0
  58. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/tutorial_standard_name_table.yaml +0 -0
  59. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/data/type_icon.png +0 -0
  60. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/__init__.py +0 -0
  61. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/hdfdb/__init__.py +0 -0
  62. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/hdfdb/filedb.py +0 -0
  63. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/hdfdb/objdb.py +0 -0
  64. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/hdfdb/query.py +0 -0
  65. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/hdfdb/utils.py +0 -0
  66. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/interface.py +0 -0
  67. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/lazy.py +0 -0
  68. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/database/mongo.py +0 -0
  69. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/errors.py +0 -0
  70. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/extensions/__init__.py +0 -0
  71. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/extensions/magnitude.py +0 -0
  72. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/extensions/normalize.py +0 -0
  73. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/extensions/onto.py +0 -0
  74. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/extensions/units.py +0 -0
  75. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/extensions/vector.py +0 -0
  76. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/gui/__init__.py +0 -0
  77. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/gui/fairify.py +0 -0
  78. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/gui/src/__init__.py +0 -0
  79. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/gui/src/main.py +0 -0
  80. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/identifiers.py +0 -0
  81. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/layout/__init__.py +0 -0
  82. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/layout/core.py +0 -0
  83. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/globals.py +0 -0
  84. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/hdf/__init__.py +0 -0
  85. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/hdf/datasets.py +0 -0
  86. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/hdf/file.py +0 -0
  87. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/hdf/groups.py +0 -0
  88. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/rdf.py +0 -0
  89. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/shacl.py +0 -0
  90. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/ld/user/__init__.py +0 -0
  91. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/plotting.py +0 -0
  92. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/protected_attributes.py +0 -0
  93. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/protocols.py +0 -0
  94. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/__init__.py +0 -0
  95. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/default_metamapper.py +0 -0
  96. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/interface.py +0 -0
  97. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/utils.py +0 -0
  98. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/zenodo/__init__.py +0 -0
  99. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/zenodo/core.py +0 -0
  100. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/zenodo/metadata.py +0 -0
  101. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/zenodo/tokens.py +0 -0
  102. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/repository/zenodo/utils.py +0 -0
  103. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/tutorial.py +0 -0
  104. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/user.py +0 -0
  105. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/__init__.py +0 -0
  106. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/accessor.py +0 -0
  107. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/accessors/__init__.py +0 -0
  108. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/accessors/provenance.py +0 -0
  109. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/ds_decoder.py +0 -0
  110. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/h5attr.py +0 -0
  111. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/h5ext.py +0 -0
  112. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/h5utils.py +0 -0
  113. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/h5yaml.py +0 -0
  114. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/jsonld.py +0 -0
  115. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox/wrapper/xr2hdf.py +0 -0
  116. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox.egg-info/dependency_links.txt +0 -0
  117. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox.egg-info/entry_points.txt +0 -0
  118. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox.egg-info/requires.txt +0 -0
  119. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/h5rdmtoolbox.egg-info/top_level.txt +0 -0
  120. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/setup.cfg +0 -0
  121. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_cli.py +0 -0
  122. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_config.py +0 -0
  123. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_extensions.py +0 -0
  124. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_identifiers.py +0 -0
  125. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_plotting.py +0 -0
  126. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_readme.py +0 -0
  127. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_repr.py +0 -0
  128. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_user.py +0 -0
  129. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_utils.py +0 -0
  130. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.1}/tests/test_version.py +0 -0
  131. {h5rdmtoolbox-2.6.2 → h5rdmtoolbox-2.7.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.6.2
3
+ Version: 2.7.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
@@ -55,6 +55,7 @@ import json
55
55
  from .wrapper.accessor import register_accessor
56
56
 
57
57
  from .ld.shacl import validate_hdf, ValidationResult
58
+ from .ld._types import RDFMappingEntry
58
59
  # noinspection PyUnresolvedReferences
59
60
  from .utils import DownloadFileManager
60
61
 
@@ -282,6 +283,7 @@ def serialize(hdf_filename,
282
283
  structural: bool = True,
283
284
  contextual: bool = True,
284
285
  file_uri: Optional[str] = None,
286
+ rdf_mappings: Dict[str, RDFMappingEntry] = None,
285
287
  **kwargs):
286
288
  """Alternative to json-ld but allows multiple serialization options"""
287
289
  fmt = kwargs.pop("format", fmt)
@@ -290,7 +292,8 @@ def serialize(hdf_filename,
290
292
  skipND=skipND,
291
293
  structural=structural,
292
294
  contextual=contextual,
293
- file_uri=file_uri)
295
+ file_uri=file_uri,
296
+ rdf_mappings=rdf_mappings)
294
297
 
295
298
 
296
299
  def build_pyvis_graph(hdf_filename, output_filename="kg-graph.html", notebook=False,
@@ -80,7 +80,8 @@ class DataStore(Store, ABC):
80
80
  class MetadataStore(Store, ABC):
81
81
  """Metadata database interface using."""
82
82
 
83
- __populate_on_init__ = False
83
+ __supports_named_graphs__: bool
84
+ __populate_on_init__: bool = False
84
85
  __shacl_shapes__: Dict[str, rdflib.Graph] = {}
85
86
 
86
87
  @abstractmethod
@@ -121,6 +121,8 @@ class RemoteSparqlStore(MetadataStore):
121
121
 
122
122
  class GraphDB(RemoteSparqlStore):
123
123
  """GraphDB RDF database store."""
124
+
125
+ __supports_named_graphs__: bool=True
124
126
  __supported_file_extensions__ = {".ttl", ".rdf", ".jsonld"}
125
127
 
126
128
  def __init__(self,
@@ -401,7 +403,7 @@ class GraphDB(RemoteSparqlStore):
401
403
 
402
404
  class InMemoryRDFStore(RDFStore):
403
405
  """In-memory RDF database that can upload files and return a combined graph."""
404
-
406
+ __supports_named_graphs__=False
405
407
  __default_expected_file_extensions__ = {".ttl", ".rdf", ".jsonld", ".nt", ".xml", ".n3"}
406
408
  __populate_on_init__ = True
407
409
 
@@ -786,6 +788,10 @@ class HDF5SqlDB(DataStore):
786
788
  class MetadataStoreQuery(Query, ABC):
787
789
  """RDF Store Query interface."""
788
790
 
791
+ @abstractmethod
792
+ def execute(self, store: "Store", named_graph: Optional[str]=None) -> "QueryResult":
793
+ """Executes the query against the given metadata store with optional named graph."""
794
+
789
795
 
790
796
  class SparqlQuery(MetadataStoreQuery):
791
797
  """A SPARQL query interface for RDF stores."""
@@ -4,18 +4,21 @@ from typing import Union, Optional, Dict
4
4
  import h5py
5
5
  import rdflib
6
6
 
7
+ from ._types import RDFMappingEntry
7
8
  from .hdf.file import get_ld as get_hdf_ld
8
9
  from .user.file import get_ld as get_contextual_ld
9
10
  from .utils import optimize_context
10
11
 
11
-
12
+ BINARY_AS_STRING=True
12
13
  def get_ld(
13
14
  hdf_filename: Union[str, pathlib.Path],
14
15
  structural: bool = True,
15
16
  contextual: bool = True,
16
17
  file_uri: Optional[str] = None,
17
18
  skipND: Optional[int] = 1,
18
- context: Optional[Dict] = None) -> rdflib.Graph:
19
+ context: Optional[Dict] = None,
20
+ rdf_mappings: Dict[str, RDFMappingEntry] = None
21
+ ) -> rdflib.Graph:
19
22
  """Return the HDF file content as a rdflib.Graph object."""
20
23
  if file_uri and not (str(file_uri).endswith("#") or str(file_uri).endswith("/")):
21
24
  raise ValueError("file_uri must end with '#' or '/'")
@@ -23,7 +26,8 @@ def get_ld(
23
26
  with h5py.File(hdf_filename) as h5:
24
27
  if contextual and structural:
25
28
  graph1 = get_hdf_ld(h5, file_uri=file_uri, skipND=skipND)
26
- graph2 = get_contextual_ld(h5, file_uri=file_uri)
29
+ graph2 = get_contextual_ld(h5, file_uri=file_uri,
30
+ rdf_mappings=rdf_mappings)
27
31
  graph = graph1 + graph2
28
32
  else:
29
33
  if structural:
@@ -0,0 +1,11 @@
1
+ from typing import Callable, Optional, Any
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class RDFMappingEntry(BaseModel):
7
+ predicate: Optional[Any] = Field(None, description="Predicate IRI or rdflib term")
8
+ object: Optional[Callable[[Any], Any]] = Field(None, description="Function to generate object value")
9
+
10
+ class Config:
11
+ arbitrary_types_allowed = True
@@ -0,0 +1,57 @@
1
+ import warnings
2
+
3
+ import numpy as np
4
+ from ontolutils.namespacelib.hdf5 import HDF5
5
+ from rdflib import Namespace, Literal, XSD, RDF
6
+
7
+ from ..utils import get_attr_node, to_literal
8
+
9
+ HDF = Namespace(str(HDF5))
10
+
11
+
12
+ def process_attribute(*, name, value, graph, parent, parent_uri, blank_node_iri_base):
13
+ """Process an HDF5 attribute, adding it to the RDF graph."""
14
+ if name.isupper() or name.startswith('@'):
15
+ return
16
+ attr_uri = get_attr_node(
17
+ parent,
18
+ name,
19
+ blank_node_iri_base
20
+ )
21
+
22
+ if isinstance(value, str):
23
+ graph.add((attr_uri, RDF.type, HDF5.StringAttribute))
24
+ if isinstance(value, bytes):
25
+ value = value.decode('utf-8', errors='ignore')
26
+ graph.add((attr_uri, HDF.data, to_literal(value)))
27
+ elif isinstance(value, int):
28
+ graph.add((attr_uri, RDF.type, HDF5.IntegerAttribute))
29
+ graph.add((attr_uri, HDF.data, Literal(value, datatype=XSD.integer)))
30
+ elif isinstance(value, float):
31
+ graph.add((attr_uri, RDF.type, HDF5.FloatAttribute))
32
+ graph.add((attr_uri, HDF.data, Literal(value, datatype=XSD.float)))
33
+ elif isinstance(value, np.ndarray):
34
+ if value.ndim == 1:
35
+ for v in value:
36
+ graph.add((attr_uri, HDF.data, to_literal(v)))
37
+ if value.dtype.kind in {'i', 'u'}:
38
+ graph.add((attr_uri, RDF.type, HDF5.IntegerAttribute))
39
+ elif value.dtype.kind == 'f':
40
+ graph.add((attr_uri, RDF.type, HDF5.FloatAttribute))
41
+ else:
42
+ graph.add((attr_uri, RDF.type, HDF5.Attribute))
43
+ else:
44
+ warnings.warn(f"Attribute data containing more than one dimension is not fully supported.")
45
+ graph.add((attr_uri, RDF.type, HDF5.Attribute))
46
+ graph.add((attr_uri, HDF.data, to_literal(value)))
47
+ else:
48
+ # attributes that are string bytes should be HDF5.StringAttribute
49
+ if isinstance(value, bytes):
50
+ value = value.decode('utf-8', errors='ignore')
51
+ graph.add((attr_uri, RDF.type, HDF5.StringAttribute))
52
+ else:
53
+ graph.add((attr_uri, RDF.type, HDF5.Attribute))
54
+ graph.add((attr_uri, HDF.data, to_literal(value)))
55
+
56
+ graph.add((attr_uri, HDF.name, Literal(name)))
57
+ graph.add((parent_uri, HDF.attribute, attr_uri))
@@ -9,7 +9,7 @@ from ontolutils import SCHEMA
9
9
  from ontolutils.classes.thing import is_url
10
10
  from rdflib import RDF, DCTERMS, XSD, URIRef, Literal, Graph, BNode
11
11
 
12
- from h5rdmtoolbox.ld.utils import get_attr_dtype_as_xsd, get_obj_bnode
12
+ from h5rdmtoolbox.ld.utils import get_attr_dtype_as_xsd, get_obj_bnode, to_literal
13
13
  from .utils import to_uriref
14
14
  from ..rdf import FileRDFManager, RDFManager
15
15
 
@@ -230,9 +230,11 @@ def process_attribute(parent_obj, name, data, graph, blank_node_iri_base):
230
230
  )
231
231
  else:
232
232
  graph.add(
233
- (parent_uri,
234
- to_uriref(rdf_user_predicate, blank_node_iri_base),
235
- Literal(data, datatype=get_attr_dtype_as_xsd(data)))
233
+ (
234
+ parent_uri,
235
+ to_uriref(rdf_user_predicate, blank_node_iri_base),
236
+ to_literal(data)
237
+ ) # Literal(data, datatype=get_attr_dtype_as_xsd(data)))
236
238
  )
237
239
  return _graph
238
240
 
@@ -4,14 +4,16 @@ from ontolutils.namespacelib import SCHEMA
4
4
  from h5rdmtoolbox.ld.rdf import PROTECTED_ATTRIBUTE_NAMES
5
5
  from h5rdmtoolbox.ld.user.attributes import process_attribute
6
6
  from h5rdmtoolbox.ld.utils import get_obj_bnode
7
+ from .utils import apply_rdf_mappings
7
8
  from ..rdf import RDFManager
8
9
 
9
10
 
10
- def process_dataset(dataset, graph, blank_node_iri_base=None):
11
+ def process_dataset(*, dataset, graph, rdf_mappings, blank_node_iri_base=None):
11
12
  dataset_uri = get_obj_bnode(dataset, blank_node_iri_base=blank_node_iri_base)
12
13
  for ak, av in dataset.attrs.items():
13
14
  if ak not in PROTECTED_ATTRIBUTE_NAMES:
14
15
  process_attribute(dataset, ak, av, graph, blank_node_iri_base=blank_node_iri_base)
16
+ apply_rdf_mappings(dataset, dataset_uri, graph, rdf_mappings)
15
17
 
16
18
  rdf_type = RDFManager(dataset.attrs).type
17
19
  if rdf_type is not None:
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from typing import Optional, Dict
2
2
  from typing import Union
3
3
 
4
4
  import h5py
@@ -12,16 +12,21 @@ from h5rdmtoolbox.ld.user.attributes import process_file_attribute
12
12
  from h5rdmtoolbox.ld.user.groups import process_group
13
13
  from h5rdmtoolbox.ld.utils import get_file_bnode
14
14
  from .utils import to_uriref
15
+ from .._types import RDFMappingEntry
15
16
  from ..rdf import FileRDFManager
17
+ from .utils import apply_rdf_mappings
16
18
 
17
19
  HDF = Namespace(str(HDF5))
18
20
 
19
21
 
20
22
  def get_ld(
21
23
  source: Union[str, h5py.File],
22
- file_uri: Optional[str] = None) -> rdflib.Graph:
24
+ file_uri: Optional[str] = None,
25
+ rdf_mappings: Dict[str, RDFMappingEntry] = None
26
+ ) -> rdflib.Graph:
23
27
  """Convert an HDF5 file into an RDF graph."""
24
-
28
+ if rdf_mappings is None:
29
+ rdf_mappings = {}
25
30
  if not isinstance(source, h5py.File):
26
31
  with h5py.File(source) as h5f:
27
32
  return get_ld(h5f, file_uri=file_uri)
@@ -53,6 +58,8 @@ def get_ld(
53
58
  for ak, av in source.attrs.items():
54
59
  process_file_attribute(source, ak, av, graph, file_node, blank_node_iri_base=file_uri)
55
60
 
56
- process_group(source, graph, blank_node_iri_base=file_uri)
61
+ apply_rdf_mappings(source, file_node, graph, rdf_mappings)
62
+
63
+ process_group(group=source, graph=graph, blank_node_iri_base=file_uri, rdf_mappings=rdf_mappings)
57
64
 
58
65
  return graph
@@ -8,15 +8,18 @@ from ontolutils.namespacelib import SCHEMA
8
8
  from h5rdmtoolbox.ld.user.attributes import process_attribute
9
9
  from h5rdmtoolbox.ld.user.datasets import process_dataset
10
10
  from h5rdmtoolbox.ld.utils import get_obj_bnode, get_file_bnode
11
+ from .utils import apply_rdf_mappings
11
12
  from .utils import to_uriref
12
13
  from ..rdf import RDFManager
13
14
 
14
15
 
15
- def process_group(group, graph, blank_node_iri_base: Optional[str] = None):
16
+ def process_group(*, group, graph, rdf_mappings, blank_node_iri_base: Optional[str] = None):
17
+ group_uri = get_obj_bnode(group, blank_node_iri_base)
18
+
16
19
  for ak, av in group.attrs.items():
17
20
  process_attribute(group, ak, av, graph, blank_node_iri_base)
21
+ apply_rdf_mappings(group, group_uri, graph, rdf_mappings)
18
22
 
19
- group_uri = get_obj_bnode(group, blank_node_iri_base)
20
23
  rdf_manager = RDFManager(group.attrs)
21
24
  parent_rdf_manager = RDFManager(group.parent.attrs) if group.parent is not None else None
22
25
  rdf_type = rdf_manager.type
@@ -57,7 +60,9 @@ def process_group(group, graph, blank_node_iri_base: Optional[str] = None):
57
60
  # graph.add((group_uri, HDF.member, sub_group_or_dataset_uri))
58
61
 
59
62
  if isinstance(sub_group_or_dataset, h5py.Group):
60
- process_group(sub_group_or_dataset, graph, blank_node_iri_base=blank_node_iri_base)
63
+ process_group(group=sub_group_or_dataset, graph=graph, rdf_mappings=rdf_mappings,
64
+ blank_node_iri_base=blank_node_iri_base)
61
65
 
62
66
  elif isinstance(sub_group_or_dataset, h5py.Dataset):
63
- process_dataset(sub_group_or_dataset, graph, blank_node_iri_base=blank_node_iri_base)
67
+ process_dataset(dataset=sub_group_or_dataset, graph=graph, rdf_mappings=rdf_mappings,
68
+ blank_node_iri_base=blank_node_iri_base)
@@ -0,0 +1,33 @@
1
+ import rdflib
2
+ from rdflib import URIRef
3
+
4
+ from h5rdmtoolbox.ld.rdf import PROTECTED_ATTRIBUTE_NAMES
5
+ from ..utils import to_literal
6
+
7
+
8
+ def to_uriref(value: str, blank_node_iri_base: str = None) -> URIRef:
9
+ """Convert a value to a URIRef, handling blank nodes if necessary."""
10
+ if value.startswith("_:") and blank_node_iri_base:
11
+ value = blank_node_iri_base + value[2:]
12
+ return URIRef(value)
13
+
14
+
15
+ def apply_rdf_mappings(obj, obj_uri, graph, rdf_mappings):
16
+ for ak, av in obj.attrs.items():
17
+ if ak not in PROTECTED_ATTRIBUTE_NAMES:
18
+ if ak in rdf_mappings:
19
+ mapping = rdf_mappings[ak]
20
+ predicate_uri = rdflib.URIRef(mapping['predicate'])
21
+ if predicate_uri:
22
+ object_uri = mapping.get('object')
23
+ if object_uri:
24
+ if callable(object_uri):
25
+ object_uri = str(object_uri(av, obj.attrs))
26
+ if object_uri is None:
27
+ return
28
+ graph.add((obj_uri, predicate_uri, rdflib.URIRef(object_uri)))
29
+ else:
30
+ graph.add((obj_uri, predicate_uri, to_literal(av)))
31
+ type_uri = mapping.get('type')
32
+ if type_uri:
33
+ graph.add((obj_uri, rdflib.RDF.type, rdflib.URIRef(type_uri)))
@@ -1,3 +1,4 @@
1
+ import base64
1
2
  import pathlib
2
3
  import urllib.parse
3
4
  from typing import Optional
@@ -5,8 +6,28 @@ from typing import Union
5
6
 
6
7
  import h5py
7
8
  import rdflib
9
+ from rdflib import Literal
8
10
  from rdflib.namespace import XSD
9
11
 
12
+ def to_literal(value, *, binary_as_string=None, encoding="utf-8") -> Literal:
13
+ """
14
+ Convert a Python value to an rdflib Literal.
15
+
16
+ - str -> xsd:string
17
+ - bytes -> xsd:base64Binary (default)
18
+ - bytes -> xsd:string if binary_as_string=True
19
+ """
20
+ if binary_as_string is None:
21
+ from . import BINARY_AS_STRING
22
+ binary_as_string = BINARY_AS_STRING
23
+ if isinstance(value, bytes):
24
+ if binary_as_string:
25
+ return Literal(value.decode(encoding))
26
+ encoded = base64.b64encode(value).decode("ascii")
27
+ return Literal(encoded, datatype=XSD.base64Binary)
28
+
29
+ return Literal(value, datatype=get_attr_dtype_as_xsd(value))
30
+
10
31
 
11
32
  def _parse_obj_name(obj_name: str):
12
33
  return urllib.parse.quote(obj_name, safe='/')
@@ -111,9 +132,9 @@ def get_property_node(
111
132
  """Get a property node for an HDF5 object."""
112
133
  _file_id = _get_file_id(obj.file)
113
134
  if obj.name == "/":
114
- _id = f"{_file_id}@{name}"
135
+ _id = f"{_file_id}@{_parse_obj_name(name)}"
115
136
  else:
116
- _id = f"{_file_id}{_parse_obj_name(obj.name)}__{name}"
137
+ _id = f"{_file_id}{_parse_obj_name(obj.name)}__{_parse_obj_name(name)}"
117
138
  if blank_node_iri_base:
118
139
  return rdflib.URIRef(f'{blank_node_iri_base}{_id}')
119
140
  return rdflib.BNode(_id)
@@ -125,9 +146,9 @@ def get_attr_node(
125
146
  blank_node_iri_base: str) -> Union[rdflib.URIRef, rdflib.BNode]:
126
147
  _file_id = _get_file_id(obj.file)
127
148
  if obj.name == "/":
128
- _id = f"{_file_id}@{name}"
149
+ _id = f"{_file_id}@{_parse_obj_name(name)}"
129
150
  else:
130
- _id = f"{_file_id}{_parse_obj_name(obj.name)}@{name}"
151
+ _id = f"{_file_id}{_parse_obj_name(obj.name)}@{_parse_obj_name(name)}"
131
152
  if blank_node_iri_base:
132
153
  return rdflib.URIRef(f'{blank_node_iri_base}{_id}')
133
154
  return rdflib.BNode(f"{_id}")
@@ -32,7 +32,6 @@ logger = logging.getLogger('h5rdmtoolbox')
32
32
  DEFAULT_LOGGING_LEVEL = logging.INFO
33
33
  RETRY_STATUS = {429, 500, 502, 503, 504}
34
34
 
35
-
36
35
  def get_filesize(filename: Union[str, pathlib.Path]) -> int:
37
36
  """Get the size of a file in bytes"""
38
37
  return os.path.getsize(filename) * get_ureg().byte
@@ -199,9 +198,9 @@ def generate_temporary_filename(prefix='tmp', suffix: str = '', touch: bool = Fa
199
198
  tmp_filename: pathlib.Path
200
199
  The generated temporary filename
201
200
  """
202
- _filename = user.UserDir['tmp'] / f"{prefix}{next(user._filecounter)}{suffix}"
201
+ _filename = user.UserDir['tmp'] / f"{prefix}{next(user._filecounter)}{suffix}"
203
202
  while _filename.exists():
204
- _filename = user.UserDir['tmp'] / f"{prefix}{next(user._filecounter)}{suffix}"
203
+ _filename = user.UserDir['tmp'] / f"{prefix}{next(user._filecounter)}{suffix}"
205
204
  if touch:
206
205
  if _filename.suffix in ('.h5', '.hdf', '.hdf5'):
207
206
  with h5py.File(_filename, 'w'):
@@ -225,9 +224,9 @@ def generate_temporary_directory(prefix='tmp') -> pathlib.Path:
225
224
  tmp_filename: pathlib.Path
226
225
  The generated temporary filename
227
226
  """
228
- _dir = user.UserDir['tmp'] / f"{prefix}{next(user._dircounter)}"
227
+ _dir = user.UserDir['tmp'] / f"{prefix}{next(user._dircounter)}"
229
228
  while _dir.exists():
230
- _dir = user.UserDir['tmp'] / f"{prefix}{next(user._dircounter)}"
229
+ _dir = user.UserDir['tmp'] / f"{prefix}{next(user._dircounter)}"
231
230
  _dir.mkdir(parents=True)
232
231
  return _dir
233
232
 
@@ -32,6 +32,7 @@ from .. import protocols
32
32
  from .._repr import H5Repr, H5PY_SPECIAL_ATTRIBUTES
33
33
  from ..convention.consts import DefaultValue
34
34
  from ..ld import rdf
35
+ from ..ld._types import RDFMappingEntry
35
36
 
36
37
  logger = logging.getLogger('h5rdmtoolbox')
37
38
 
@@ -2486,7 +2487,8 @@ class File(h5py.File, Group):
2486
2487
  contextual: bool = True,
2487
2488
  file_uri: Optional[Union[str, Dict]] = None,
2488
2489
  context: Optional[Dict] = None,
2489
- indent: int = 2
2490
+ indent: int = 2,
2491
+ rdf_mappings: Dict[str, RDFMappingEntry] = None
2490
2492
  ):
2491
2493
  """Serialize the file content to a specific format"""
2492
2494
  from h5rdmtoolbox.ld.utils import optimize_context
@@ -2510,7 +2512,8 @@ class File(h5py.File, Group):
2510
2512
  structural=structural,
2511
2513
  contextual=contextual,
2512
2514
  file_uri=file_uri,
2513
- skipND=skipND)
2515
+ skipND=skipND,
2516
+ rdf_mappings=rdf_mappings)
2514
2517
  if prefix:
2515
2518
  graph.bind(prefix, file_uri)
2516
2519
  context = context or {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: h5rdmtoolbox
3
- Version: 2.6.2
3
+ Version: 2.7.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
@@ -77,6 +77,7 @@ h5rdmtoolbox/gui/src/main.py
77
77
  h5rdmtoolbox/layout/__init__.py
78
78
  h5rdmtoolbox/layout/core.py
79
79
  h5rdmtoolbox/ld/__init__.py
80
+ h5rdmtoolbox/ld/_types.py
80
81
  h5rdmtoolbox/ld/globals.py
81
82
  h5rdmtoolbox/ld/rdf.py
82
83
  h5rdmtoolbox/ld/shacl.py
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "h5rdmtoolbox"
10
- version = "2.6.2"
10
+ version = "2.7.1"
11
11
  description = "Supporting a FAIR Research Data lifecycle using Python and HDF5."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.9, <3.14"
@@ -1,33 +0,0 @@
1
- from ontolutils.namespacelib.hdf5 import HDF5
2
- from rdflib import Namespace, Literal, XSD, RDF
3
-
4
- from ..utils import get_attr_node
5
-
6
- HDF = Namespace(str(HDF5))
7
-
8
-
9
- def process_attribute(*, name, value, graph, parent, parent_uri, blank_node_iri_base):
10
- """Process an HDF5 attribute, adding it to the RDF graph."""
11
- if name.isupper() or name.startswith('@'):
12
- return
13
- attr_uri = get_attr_node(
14
- parent,
15
- name,
16
- blank_node_iri_base
17
- )
18
-
19
- if isinstance(value, str):
20
- graph.add((attr_uri, RDF.type, HDF5.StringAttribute))
21
- graph.add((attr_uri, HDF.data, Literal(value)))
22
- elif isinstance(value, int):
23
- graph.add((attr_uri, RDF.type, HDF5.IntegerAttribute))
24
- graph.add((attr_uri, HDF.data, Literal(value, datatype=XSD.integer)))
25
- elif isinstance(value, float):
26
- graph.add((attr_uri, RDF.type, HDF5.FloatAttribute))
27
- graph.add((attr_uri, HDF.data, Literal(value, datatype=XSD.float)))
28
- else:
29
- graph.add((attr_uri, RDF.type, HDF5.Attribute))
30
- graph.add((attr_uri, HDF.data, Literal(value)))
31
-
32
- graph.add((attr_uri, HDF.name, Literal(name)))
33
- graph.add((parent_uri, HDF.attribute, attr_uri))
@@ -1,8 +0,0 @@
1
- from rdflib import URIRef
2
-
3
-
4
- def to_uriref(value: str, blank_node_iri_base: str = None) -> URIRef:
5
- """Convert a value to a URIRef, handling blank nodes if necessary."""
6
- if value.startswith("_:") and blank_node_iri_base:
7
- value = blank_node_iri_base + value[2:]
8
- return URIRef(value)
File without changes
File without changes
File without changes