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.
Files changed (163) hide show
  1. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/PKG-INFO +38 -9
  2. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/README.md +32 -7
  3. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/__init__.py +23 -1
  4. h5rdmtoolbox-2.8.1/h5rdmtoolbox/cli.py +4 -0
  5. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/__init__.py +4 -1
  6. h5rdmtoolbox-2.8.1/h5rdmtoolbox/ld/metrics.py +288 -0
  7. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/shacl.py +63 -20
  8. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/__init__.py +15 -0
  9. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/chat.py +340 -0
  10. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/errors.py +22 -0
  11. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/executor.py +56 -0
  12. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/interfaces.py +17 -0
  13. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/models.py +85 -0
  14. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/ontology.py +171 -0
  15. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/prompting.py +157 -0
  16. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/providers/__init__.py +6 -0
  17. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/providers/openai_adapter.py +61 -0
  18. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/sparql_guard.py +124 -0
  19. h5rdmtoolbox-2.8.1/h5rdmtoolbox/llm/summary.py +77 -0
  20. h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/static/graph.css +554 -0
  21. h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/static/graph.js +800 -0
  22. h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/static/style.css +11 -0
  23. h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/templates/graph.html +145 -0
  24. h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/templates/index.html +0 -0
  25. h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/templates/resource.html +0 -0
  26. h5rdmtoolbox-2.8.1/h5rdmtoolbox/server.py +3334 -0
  27. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/user.py +18 -11
  28. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/download.py +180 -26
  29. h5rdmtoolbox-2.8.1/h5rdmtoolbox/wrapper/accessors/__init__.py +0 -0
  30. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/core.py +27 -0
  31. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/jsonld.py +67 -0
  32. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox.egg-info/PKG-INFO +38 -9
  33. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox.egg-info/SOURCES.txt +23 -0
  34. h5rdmtoolbox-2.8.1/h5rdmtoolbox.egg-info/entry_points.txt +2 -0
  35. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox.egg-info/requires.txt +6 -1
  36. h5rdmtoolbox-2.8.1/h5rdmtoolbox.egg-info/top_level.txt +2 -0
  37. h5rdmtoolbox-2.8.1/h5rdmtoolbox_cli.py +324 -0
  38. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/pyproject.toml +15 -4
  39. h5rdmtoolbox-2.8.1/tests/test_cli.py +282 -0
  40. h5rdmtoolbox-2.8.1/tests/test_server.py +2046 -0
  41. h5rdmtoolbox-2.7.4/h5rdmtoolbox/cli.py +0 -61
  42. h5rdmtoolbox-2.7.4/h5rdmtoolbox.egg-info/entry_points.txt +0 -2
  43. h5rdmtoolbox-2.7.4/h5rdmtoolbox.egg-info/top_level.txt +0 -1
  44. h5rdmtoolbox-2.7.4/tests/test_cli.py +0 -69
  45. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/LICENSE +0 -0
  46. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/_cfg.py +0 -0
  47. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/_repr.py +0 -0
  48. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/_version.py +0 -0
  49. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/__init__.py +0 -0
  50. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/_initialization.py +0 -0
  51. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/abstracts.py +0 -0
  52. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/core.py +0 -0
  53. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/profiles.py +0 -0
  54. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/query_templates.py +0 -0
  55. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/stores/__init__.py +0 -0
  56. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/stores/hdf5.py +0 -0
  57. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/catalog/utils.py +0 -0
  58. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/consts.py +0 -0
  59. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/__init__.py +0 -0
  60. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/cfg.py +0 -0
  61. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/consts.py +0 -0
  62. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/core.py +0 -0
  63. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/errors.py +0 -0
  64. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/generate.py +0 -0
  65. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/ontology/__init__.py +0 -0
  66. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/ontology/h5ontocls.py +0 -0
  67. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/ontology/hdf_datatypes.py +0 -0
  68. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/references.py +0 -0
  69. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/standard_attributes.py +0 -0
  70. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/toolbox_validators.py +0 -0
  71. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/convention/utils.py +0 -0
  72. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/def_icon.png +0 -0
  73. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/fluid-v1.yml +0 -0
  74. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/h5tbx.yaml +0 -0
  75. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/id_icon.png +0 -0
  76. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/iri_icon.png +0 -0
  77. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/ld_icon.png +0 -0
  78. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/piv-v1.yml +0 -0
  79. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/style.css +0 -0
  80. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/tutorial_convention.yaml +0 -0
  81. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/tutorial_convention_vfuncs.py +0 -0
  82. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/tutorial_standard_name_table.yaml +0 -0
  83. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/data/type_icon.png +0 -0
  84. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/__init__.py +0 -0
  85. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/__init__.py +0 -0
  86. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/filedb.py +0 -0
  87. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/objdb.py +0 -0
  88. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/query.py +0 -0
  89. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/hdfdb/utils.py +0 -0
  90. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/interface.py +0 -0
  91. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/lazy.py +0 -0
  92. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/database/mongo.py +0 -0
  93. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/errors.py +0 -0
  94. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/__init__.py +0 -0
  95. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/magnitude.py +0 -0
  96. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/normalize.py +0 -0
  97. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/onto.py +0 -0
  98. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/units.py +0 -0
  99. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/extensions/vector.py +0 -0
  100. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/gui/__init__.py +0 -0
  101. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/gui/fairify.py +0 -0
  102. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/gui/src/__init__.py +0 -0
  103. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/gui/src/main.py +0 -0
  104. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/identifiers.py +0 -0
  105. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/layout/__init__.py +0 -0
  106. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/layout/core.py +0 -0
  107. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/_types.py +0 -0
  108. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/globals.py +0 -0
  109. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/__init__.py +0 -0
  110. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/attributes.py +0 -0
  111. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/datasets.py +0 -0
  112. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/file.py +0 -0
  113. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/hdf/groups.py +0 -0
  114. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/rdf.py +0 -0
  115. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/sparql.py +0 -0
  116. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/__init__.py +0 -0
  117. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/attributes.py +0 -0
  118. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/datasets.py +0 -0
  119. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/file.py +0 -0
  120. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/groups.py +0 -0
  121. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/user/utils.py +0 -0
  122. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/ld/utils.py +0 -0
  123. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/plotting.py +0 -0
  124. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/protected_attributes.py +0 -0
  125. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/protocols.py +0 -0
  126. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/__init__.py +0 -0
  127. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/default_metamapper.py +0 -0
  128. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/interface.py +0 -0
  129. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/utils.py +0 -0
  130. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/__init__.py +0 -0
  131. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/core.py +0 -0
  132. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/metadata.py +0 -0
  133. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/tokens.py +0 -0
  134. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/repository/zenodo/utils.py +0 -0
  135. /h5rdmtoolbox-2.7.4/h5rdmtoolbox/wrapper/accessors/__init__.py → /h5rdmtoolbox-2.8.1/h5rdmtoolbox/server/templates/file.html +0 -0
  136. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/tutorial.py +0 -0
  137. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/__init__.py +0 -0
  138. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/docstring.py +0 -0
  139. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/file_io.py +0 -0
  140. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/hdf5.py +0 -0
  141. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils/string.py +0 -0
  142. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/utils.py +0 -0
  143. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/__init__.py +0 -0
  144. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/accessor.py +0 -0
  145. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/accessors/provenance.py +0 -0
  146. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/ds_decoder.py +0 -0
  147. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/h5attr.py +0 -0
  148. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/h5ext.py +0 -0
  149. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/h5utils.py +0 -0
  150. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/h5yaml.py +0 -0
  151. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox/wrapper/xr2hdf.py +0 -0
  152. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/h5rdmtoolbox.egg-info/dependency_links.txt +0 -0
  153. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/setup.cfg +0 -0
  154. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_config.py +0 -0
  155. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_extensions.py +0 -0
  156. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_identifiers.py +0 -0
  157. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_plotting.py +0 -0
  158. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_readme.py +0 -0
  159. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_repr.py +0 -0
  160. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_user.py +0 -0
  161. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_utils.py +0 -0
  162. {h5rdmtoolbox-2.7.4 → h5rdmtoolbox-2.8.1}/tests/test_version.py +0 -0
  163. {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.7.4
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: click>=8.1.7
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
- `ttl_ctx`:
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
- [//]: # (The "HDF5 Research Data Management Toolbox" &#40;h5RDMtoolbox&#41; is a Python package supporting everybody who is working with)
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
- [//]: # (HDF5 to achieve a sustainable data lifecycle which follows)
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
- [//]: # (the [FAIR &#40;Findable, Accessible, Interoperable, Reusable&#41;]&#40;https://www.nature.com/articles/sdata201618&#41;)
252
+ To inspect files in a browser, run:
241
253
 
242
- [//]: # (principles. It specifically supports the five main steps of *planning*, *collecting*, *analyzing*, *sharing* and)
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
- [//]: # (*reusing* data. Please visit the [documentation]&#40;https://h5rdmtoolbox.readthedocs.io/en/latest/&#41; for detailed)
260
+ You may before SPARQL queries or view the File in a graph as shown below:
245
261
 
246
- [//]: # (information of try the [quickstart using colab]&#40;#quickstart&#41;.)
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
- `ttl_ctx`:
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
- [//]: # (The "HDF5 Research Data Management Toolbox" &#40;h5RDMtoolbox&#41; is a Python package supporting everybody who is working with)
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
- [//]: # (HDF5 to achieve a sustainable data lifecycle which follows)
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
- [//]: # (the [FAIR &#40;Findable, Accessible, Interoperable, Reusable&#41;]&#40;https://www.nature.com/articles/sdata201618&#41;)
166
+ To inspect files in a browser, run:
159
167
 
160
- [//]: # (principles. It specifically supports the five main steps of *planning*, *collecting*, *analyzing*, *sharing* and)
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
- [//]: # (*reusing* data. Please visit the [documentation]&#40;https://h5rdmtoolbox.readthedocs.io/en/latest/&#41; for detailed)
174
+ You may before SPARQL queries or view the File in a graph as shown below:
163
175
 
164
- [//]: # (information of try the [quickstart using colab]&#40;#quickstart&#41;.)
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 {})
@@ -0,0 +1,4 @@
1
+ from h5rdmtoolbox_cli import h5tbx
2
+
3
+ if __name__ == '__main__':
4
+ h5tbx(obj={})
@@ -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
- hdf_data: Union[str, rdflib.Graph] = None,
47
- hdf_source: Union[h5py.File, pathlib.Path] = None,
48
- shacl_data: Union[str, rdflib.Graph] = None,
49
- shacl_source: Union[str, pathlib.Path] = None,
50
- hdf_file_uri="https://example.org/hdf5file#",
51
- shacl_format: str = "turtle",
52
- hdf_data_format: str = "turtle",
53
- **pyshacl_kwargs,
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="turtle")
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, shacl_graph, **pyshacl_kwargs
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
- data_graph,
160
- shacl_graph,
161
- inference="rdfs",
162
- abort_on_first=False,
163
- meta_shacl=False,
164
- advanced=False,
165
- debug=False,
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
- (None, rdflib.namespace.SH.resultMessage, None)
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
+