h5rdmtoolbox 2.6.0__tar.gz → 2.6.2__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.6.0 → h5rdmtoolbox-2.6.2}/LICENSE +1 -1
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/PKG-INFO +102 -27
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/README.md +101 -26
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/__init__.py +5 -4
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/_initialization.py +2 -1
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/core.py +36 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/utils.py +19 -41
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/references.py +7 -1
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/protocols.py +2 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/utils.py +9 -8
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/zenodo/core.py +39 -18
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox.egg-info/PKG-INFO +102 -27
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox.egg-info/SOURCES.txt +1 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/pyproject.toml +1 -1
- h5rdmtoolbox-2.6.2/tests/test_readme.py +22 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/_cfg.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/_repr.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/_version.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/abstracts.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/profiles.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/query_templates.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/stores/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/catalog/stores/hdf5.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/cli.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/consts.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/cfg.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/consts.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/core.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/errors.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/generate.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/ontology/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/ontology/h5ontocls.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/ontology/hdf_datatypes.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/standard_attributes.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/toolbox_validators.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/convention/utils.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/def_icon.png +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/fluid-v1.yml +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/h5tbx.yaml +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/id_icon.png +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/iri_icon.png +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/ld_icon.png +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/piv-v1.yml +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/style.css +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/tutorial_convention.yaml +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/tutorial_convention_vfuncs.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/tutorial_standard_name_table.yaml +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/data/type_icon.png +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/hdfdb/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/hdfdb/filedb.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/hdfdb/objdb.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/hdfdb/query.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/hdfdb/utils.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/interface.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/lazy.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/database/mongo.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/errors.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/extensions/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/extensions/magnitude.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/extensions/normalize.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/extensions/onto.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/extensions/units.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/extensions/vector.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/gui/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/gui/fairify.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/gui/src/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/gui/src/main.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/identifiers.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/layout/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/layout/core.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/globals.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/hdf/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/hdf/attributes.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/hdf/datasets.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/hdf/file.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/hdf/groups.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/rdf.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/shacl.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/user/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/user/attributes.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/user/datasets.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/user/file.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/user/groups.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/user/utils.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/ld/utils.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/plotting.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/protected_attributes.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/default_metamapper.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/interface.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/zenodo/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/zenodo/metadata.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/zenodo/tokens.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/repository/zenodo/utils.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/tutorial.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/user.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/utils.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/accessor.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/accessors/__init__.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/accessors/provenance.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/core.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/ds_decoder.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/h5attr.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/h5ext.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/h5utils.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/h5yaml.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/jsonld.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox/wrapper/xr2hdf.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox.egg-info/dependency_links.txt +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox.egg-info/entry_points.txt +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox.egg-info/requires.txt +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/h5rdmtoolbox.egg-info/top_level.txt +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/setup.cfg +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_cli.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_config.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_extensions.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_identifiers.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_plotting.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_repr.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_user.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_utils.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/tests/test_version.py +0 -0
- {h5rdmtoolbox-2.6.0 → h5rdmtoolbox-2.6.2}/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.
|
|
3
|
+
Version: 2.6.2
|
|
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
|
|
@@ -85,34 +85,109 @@ Dynamic: license-file
|
|
|
85
85
|

|
|
86
86
|
[](https://codecov.io/gh/matthiasprobst/h5RDMtoolbox)
|
|
87
87
|
[](https://h5rdmtoolbox.readthedocs.io/en/latest/?badge=latest)
|
|
88
|
+
[](https://badge.fury.io/py/h5RDMtoolbox)
|
|
88
89
|

|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
91
|
+
A Python package that helps researchers achieve sustainable
|
|
92
|
+
[**FAIR (Findable, Accessible, Interoperable, Reusable)**](https://www.nature.com/articles/sdata201618) data management with HDF5 files. The toolbox provides a comprehensive
|
|
93
|
+
approach to the research data lifecycle through five main stages: **planning**, **collecting**, **analyzing**,
|
|
94
|
+
**sharing**, and **reusing** data.
|
|
95
|
+
|
|
96
|
+
> **Note**: This project is actively under development.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
## ✨ Quick Start
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
One of the core features of the toolbox and success factors for achieving FAIR data, is using semantic metadata.
|
|
105
|
+
|
|
106
|
+
Let's see how we can use RDF (semantic metadata) together with HDF5 files using the toolbox:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Install the package
|
|
110
|
+
pip install h5RDMtoolbox
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import h5rdmtoolbox as h5tbx
|
|
115
|
+
import rdflib
|
|
116
|
+
import numpy as np
|
|
117
|
+
|
|
118
|
+
# Create a new HDF5 file with semantic metadata
|
|
119
|
+
M4I = rdflib.Namespace("http://w3id.org/nfdi4ing/metadata4ing#")
|
|
120
|
+
|
|
121
|
+
# Create a new HDF5 file with FAIR metadata
|
|
122
|
+
with h5tbx.File("example.h5", "w") as h5:
|
|
123
|
+
h5.create_dataset("temperature", data=np.array([20, 21, 19, 22]))
|
|
124
|
+
h5.attrs["units", M4I.hasUnit] = "degree_Celsius"
|
|
125
|
+
h5.rdf["units"].object = "http://qudt.org/vocab/unit/DEG_C"
|
|
126
|
+
h5.attrs["description", "https://schema.org/description"] = "Room temperature measurements"
|
|
127
|
+
|
|
128
|
+
ttl = h5.serialize("ttl")
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
The serialization in Turtle (ttl) is teh RDF serialization of the HDF5 data (without arrays):
|
|
132
|
+
```ttl
|
|
133
|
+
@prefix hdf: <http://purl.allotrope.org/ontologies/hdf5/1.8#> .
|
|
134
|
+
@prefix m4i: <http://w3id.org/nfdi4ing/metadata4ing#> .
|
|
135
|
+
@prefix schema: <https://schema.org/> .
|
|
136
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
137
|
+
|
|
138
|
+
hdf:H5T_INTEL_I64 a hdf:Datatype .
|
|
139
|
+
|
|
140
|
+
[] a hdf:File ;
|
|
141
|
+
hdf:rootGroup [ a hdf:Group ;
|
|
142
|
+
hdf:attribute [ a hdf:StringAttribute ;
|
|
143
|
+
hdf:data "Room temperature measurements" ;
|
|
144
|
+
hdf:name "description" ],
|
|
145
|
+
[ a hdf:StringAttribute ;
|
|
146
|
+
hdf:data "degree_Celsius" ;
|
|
147
|
+
hdf:name "units" ] ;
|
|
148
|
+
hdf:member [ a hdf:Dataset ;
|
|
149
|
+
hdf:dataspace [ a hdf:SimpleDataspace ;
|
|
150
|
+
hdf:dimension [ a hdf:DataspaceDimension ;
|
|
151
|
+
hdf:dimensionIndex 0 ;
|
|
152
|
+
hdf:size 4 ] ] ;
|
|
153
|
+
hdf:datatype hdf:H5T_INTEGER,
|
|
154
|
+
hdf:H5T_INTEL_I64 ;
|
|
155
|
+
hdf:layout hdf:H5D_CONTIGUOUS ;
|
|
156
|
+
hdf:maximumSize 4 ;
|
|
157
|
+
hdf:name "/temperature" ;
|
|
158
|
+
hdf:rank 1 ;
|
|
159
|
+
hdf:size 4 ] ;
|
|
160
|
+
hdf:name "/" ;
|
|
161
|
+
m4i:hasUnit <http://qudt.org/vocab/unit/DEG_C> ;
|
|
162
|
+
schema:description "Room temperature measurements" ] .
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
[//]: # (The "HDF5 Research Data Management Toolbox" (h5RDMtoolbox) is a Python package supporting everybody who is working with)
|
|
166
|
+
|
|
167
|
+
[//]: # (HDF5 to achieve a sustainable data lifecycle which follows)
|
|
168
|
+
|
|
169
|
+
[//]: # (the [FAIR (Findable, Accessible, Interoperable, Reusable)](https://www.nature.com/articles/sdata201618))
|
|
170
|
+
|
|
171
|
+
[//]: # (principles. It specifically supports the five main steps of *planning*, *collecting*, *analyzing*, *sharing* and)
|
|
172
|
+
|
|
173
|
+
[//]: # (*reusing* data. Please visit the [documentation](https://h5rdmtoolbox.readthedocs.io/en/latest/) for detailed)
|
|
174
|
+
|
|
175
|
+
[//]: # (information of try the [quickstart using colab](#quickstart).)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
## 🚀 Key Features
|
|
179
|
+
|
|
180
|
+
- **🔗 HDF5 + [Xarray](https://docs.xarray.dev/en/stable/) Integration**: Seamless access to metadata during data analysis with native xarray support
|
|
181
|
+
- **🏷️ Persistent Identifiers**: Assign globally unique identifiers using RDF triples for FAIR compliance
|
|
182
|
+
- **📋 Standardized Conventions**: Define and enforce community-specific metadata standards
|
|
183
|
+
- **☁️ Repository Integration**: Direct upload to Zenodo and other research repositories
|
|
184
|
+
- **🗄️ Database Support**: Use HDF5 files with MongoDB or native HDF5 databases
|
|
185
|
+
- **🔒 Semantic Enrichment**: Add RDF-based semantic meaning to your data
|
|
186
|
+
- **🌐 Catalog Integration**: Search and discover datasets through SPARQL-based catalogs
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
Find an example code in [](https://colab.research.google.com/github/matthiasprobst/h5RDMtoolbox/blob/main/docs/colab/quickstart.ipynb) or check out the
|
|
190
|
+
[📚 full Documentation](https://h5rdmtoolbox.readthedocs.io/en/latest/)
|
|
116
191
|
|
|
117
192
|
## Who is the package for?
|
|
118
193
|
|
|
@@ -3,34 +3,109 @@
|
|
|
3
3
|

|
|
4
4
|
[](https://codecov.io/gh/matthiasprobst/h5RDMtoolbox)
|
|
5
5
|
[](https://h5rdmtoolbox.readthedocs.io/en/latest/?badge=latest)
|
|
6
|
+
[](https://badge.fury.io/py/h5RDMtoolbox)
|
|
6
7
|

|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
9
|
+
A Python package that helps researchers achieve sustainable
|
|
10
|
+
[**FAIR (Findable, Accessible, Interoperable, Reusable)**](https://www.nature.com/articles/sdata201618) data management with HDF5 files. The toolbox provides a comprehensive
|
|
11
|
+
approach to the research data lifecycle through five main stages: **planning**, **collecting**, **analyzing**,
|
|
12
|
+
**sharing**, and **reusing** data.
|
|
13
|
+
|
|
14
|
+
> **Note**: This project is actively under development.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## ✨ Quick Start
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
One of the core features of the toolbox and success factors for achieving FAIR data, is using semantic metadata.
|
|
23
|
+
|
|
24
|
+
Let's see how we can use RDF (semantic metadata) together with HDF5 files using the toolbox:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Install the package
|
|
28
|
+
pip install h5RDMtoolbox
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import h5rdmtoolbox as h5tbx
|
|
33
|
+
import rdflib
|
|
34
|
+
import numpy as np
|
|
35
|
+
|
|
36
|
+
# Create a new HDF5 file with semantic metadata
|
|
37
|
+
M4I = rdflib.Namespace("http://w3id.org/nfdi4ing/metadata4ing#")
|
|
38
|
+
|
|
39
|
+
# Create a new HDF5 file with FAIR metadata
|
|
40
|
+
with h5tbx.File("example.h5", "w") as h5:
|
|
41
|
+
h5.create_dataset("temperature", data=np.array([20, 21, 19, 22]))
|
|
42
|
+
h5.attrs["units", M4I.hasUnit] = "degree_Celsius"
|
|
43
|
+
h5.rdf["units"].object = "http://qudt.org/vocab/unit/DEG_C"
|
|
44
|
+
h5.attrs["description", "https://schema.org/description"] = "Room temperature measurements"
|
|
45
|
+
|
|
46
|
+
ttl = h5.serialize("ttl")
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
The serialization in Turtle (ttl) is teh RDF serialization of the HDF5 data (without arrays):
|
|
50
|
+
```ttl
|
|
51
|
+
@prefix hdf: <http://purl.allotrope.org/ontologies/hdf5/1.8#> .
|
|
52
|
+
@prefix m4i: <http://w3id.org/nfdi4ing/metadata4ing#> .
|
|
53
|
+
@prefix schema: <https://schema.org/> .
|
|
54
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
55
|
+
|
|
56
|
+
hdf:H5T_INTEL_I64 a hdf:Datatype .
|
|
57
|
+
|
|
58
|
+
[] a hdf:File ;
|
|
59
|
+
hdf:rootGroup [ a hdf:Group ;
|
|
60
|
+
hdf:attribute [ a hdf:StringAttribute ;
|
|
61
|
+
hdf:data "Room temperature measurements" ;
|
|
62
|
+
hdf:name "description" ],
|
|
63
|
+
[ a hdf:StringAttribute ;
|
|
64
|
+
hdf:data "degree_Celsius" ;
|
|
65
|
+
hdf:name "units" ] ;
|
|
66
|
+
hdf:member [ a hdf:Dataset ;
|
|
67
|
+
hdf:dataspace [ a hdf:SimpleDataspace ;
|
|
68
|
+
hdf:dimension [ a hdf:DataspaceDimension ;
|
|
69
|
+
hdf:dimensionIndex 0 ;
|
|
70
|
+
hdf:size 4 ] ] ;
|
|
71
|
+
hdf:datatype hdf:H5T_INTEGER,
|
|
72
|
+
hdf:H5T_INTEL_I64 ;
|
|
73
|
+
hdf:layout hdf:H5D_CONTIGUOUS ;
|
|
74
|
+
hdf:maximumSize 4 ;
|
|
75
|
+
hdf:name "/temperature" ;
|
|
76
|
+
hdf:rank 1 ;
|
|
77
|
+
hdf:size 4 ] ;
|
|
78
|
+
hdf:name "/" ;
|
|
79
|
+
m4i:hasUnit <http://qudt.org/vocab/unit/DEG_C> ;
|
|
80
|
+
schema:description "Room temperature measurements" ] .
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
[//]: # (The "HDF5 Research Data Management Toolbox" (h5RDMtoolbox) is a Python package supporting everybody who is working with)
|
|
84
|
+
|
|
85
|
+
[//]: # (HDF5 to achieve a sustainable data lifecycle which follows)
|
|
86
|
+
|
|
87
|
+
[//]: # (the [FAIR (Findable, Accessible, Interoperable, Reusable)](https://www.nature.com/articles/sdata201618))
|
|
88
|
+
|
|
89
|
+
[//]: # (principles. It specifically supports the five main steps of *planning*, *collecting*, *analyzing*, *sharing* and)
|
|
90
|
+
|
|
91
|
+
[//]: # (*reusing* data. Please visit the [documentation](https://h5rdmtoolbox.readthedocs.io/en/latest/) for detailed)
|
|
92
|
+
|
|
93
|
+
[//]: # (information of try the [quickstart using colab](#quickstart).)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
## 🚀 Key Features
|
|
97
|
+
|
|
98
|
+
- **🔗 HDF5 + [Xarray](https://docs.xarray.dev/en/stable/) Integration**: Seamless access to metadata during data analysis with native xarray support
|
|
99
|
+
- **🏷️ Persistent Identifiers**: Assign globally unique identifiers using RDF triples for FAIR compliance
|
|
100
|
+
- **📋 Standardized Conventions**: Define and enforce community-specific metadata standards
|
|
101
|
+
- **☁️ Repository Integration**: Direct upload to Zenodo and other research repositories
|
|
102
|
+
- **🗄️ Database Support**: Use HDF5 files with MongoDB or native HDF5 databases
|
|
103
|
+
- **🔒 Semantic Enrichment**: Add RDF-based semantic meaning to your data
|
|
104
|
+
- **🌐 Catalog Integration**: Search and discover datasets through SPARQL-based catalogs
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
Find an example code in [](https://colab.research.google.com/github/matthiasprobst/h5RDMtoolbox/blob/main/docs/colab/quickstart.ipynb) or check out the
|
|
108
|
+
[📚 full Documentation](https://h5rdmtoolbox.readthedocs.io/en/latest/)
|
|
34
109
|
|
|
35
110
|
## Who is the package for?
|
|
36
111
|
|
|
@@ -100,7 +100,7 @@ def _download_catalog_datasets(
|
|
|
100
100
|
|
|
101
101
|
results = g.query(GET_ALL_METADATA_CATALOG_DATASETS.query)
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
logger.debug(f"found datasets: {len(results)}")
|
|
104
104
|
list_of_ttl_web_resources = [
|
|
105
105
|
WebResource(
|
|
106
106
|
download_url=row.download,
|
|
@@ -113,11 +113,13 @@ def _download_catalog_datasets(
|
|
|
113
113
|
]
|
|
114
114
|
|
|
115
115
|
n_ttl_resources = len(list_of_ttl_web_resources)
|
|
116
|
-
|
|
116
|
+
logger.debug(
|
|
117
117
|
f"Found {n_ttl_resources} TTL web resources to download. Downloading to {download_directory}..."
|
|
118
118
|
)
|
|
119
119
|
return download(
|
|
120
|
-
download_directory=download_directory,
|
|
120
|
+
download_directory=download_directory,
|
|
121
|
+
web_resources=list_of_ttl_web_resources,
|
|
122
|
+
use_agent_header=True
|
|
121
123
|
)
|
|
122
124
|
|
|
123
125
|
|
|
@@ -421,7 +423,6 @@ class CatalogManager:
|
|
|
421
423
|
logger.debug("Adding Wikidata knowledge to the main RDF store...")
|
|
422
424
|
|
|
423
425
|
if res.data.empty:
|
|
424
|
-
print("empty")
|
|
425
426
|
logger.info(
|
|
426
427
|
"No Wikidata entities found in the main RDF store. Skipping Wikidata knowledge addition."
|
|
427
428
|
)
|
|
@@ -84,5 +84,6 @@ def database_initialization(
|
|
|
84
84
|
f"Found {len(list_of_ttl_web_resources)} TTL web resources to download. Downloading to {download_directory}...")
|
|
85
85
|
return download(
|
|
86
86
|
download_directory=download_directory,
|
|
87
|
-
web_resources=list_of_ttl_web_resources
|
|
87
|
+
web_resources=list_of_ttl_web_resources,
|
|
88
|
+
use_agent_header=True
|
|
88
89
|
)
|
|
@@ -383,6 +383,21 @@ class GraphDB(RemoteSparqlStore):
|
|
|
383
383
|
)
|
|
384
384
|
resp.raise_for_status()
|
|
385
385
|
|
|
386
|
+
def _post_update(self, update):
|
|
387
|
+
url = f"{self.endpoint}/repositories/{self.repository}/statements"
|
|
388
|
+
headers = {"Content-Type": "application/sparql-update"}
|
|
389
|
+
auth = (self.username, self.password) if self.username and self.password else None
|
|
390
|
+
r = requests.post(
|
|
391
|
+
url,
|
|
392
|
+
data=update.encode("utf-8"),
|
|
393
|
+
headers=headers,
|
|
394
|
+
auth=auth,
|
|
395
|
+
)
|
|
396
|
+
if not r.ok:
|
|
397
|
+
raise RuntimeError(
|
|
398
|
+
f"GraphDB SPARQL update failed "
|
|
399
|
+
f"(status {r.status_code}):\n{r.text}"
|
|
400
|
+
)
|
|
386
401
|
|
|
387
402
|
class InMemoryRDFStore(RDFStore):
|
|
388
403
|
"""In-memory RDF database that can upload files and return a combined graph."""
|
|
@@ -565,6 +580,27 @@ class InMemoryRDFStore(RDFStore):
|
|
|
565
580
|
self._combined_graph = rdflib.Graph()
|
|
566
581
|
return self
|
|
567
582
|
|
|
583
|
+
def save(self, filename: Union[str, pathlib.Path]):
|
|
584
|
+
"""Saves the combined graph to a file.
|
|
585
|
+
|
|
586
|
+
Parameters
|
|
587
|
+
----------
|
|
588
|
+
filename : Union[str, pathlib.Path]
|
|
589
|
+
The path to the file where the graph will be saved.
|
|
590
|
+
"""
|
|
591
|
+
filename = pathlib.Path(filename).resolve().absolute()
|
|
592
|
+
filename_suffix = filename.suffix.lower()
|
|
593
|
+
if filename_suffix == ".ttl":
|
|
594
|
+
_format = "turtle"
|
|
595
|
+
elif filename_suffix == ".rdf" or filename_suffix == ".xml":
|
|
596
|
+
_format = "xml"
|
|
597
|
+
elif filename_suffix == ".jsonld":
|
|
598
|
+
_format = "json-ld"
|
|
599
|
+
elif filename_suffix == ".nt":
|
|
600
|
+
_format = "nt"
|
|
601
|
+
else:
|
|
602
|
+
raise ValueError(f"File format '{filename_suffix}' not supported for saving.")
|
|
603
|
+
self.graph.serialize(destination=str(filename), format=_format)
|
|
568
604
|
|
|
569
605
|
class AbstractQuery(ABC):
|
|
570
606
|
|
|
@@ -17,6 +17,7 @@ from pydantic.v1 import HttpUrl
|
|
|
17
17
|
from rdflib import Graph
|
|
18
18
|
|
|
19
19
|
from .. import logger
|
|
20
|
+
from .._version import __version__
|
|
20
21
|
|
|
21
22
|
# --- minimal caster for common XSD datatypes ---
|
|
22
23
|
_XSD = "http://www.w3.org/2001/XMLSchema#"
|
|
@@ -28,6 +29,10 @@ _NUM_DT = {
|
|
|
28
29
|
_FLOAT_DT = {_XSD + "float", _XSD + "double"}
|
|
29
30
|
_DEC_DT = {_XSD + "decimal"}
|
|
30
31
|
|
|
32
|
+
USER_AGENT_HEADER = {
|
|
33
|
+
"User-Agent": f"h5rdmtoolbox/{__version__} (https://github.com/matthiasprobst/h5rdmtoolbox)",
|
|
34
|
+
}
|
|
35
|
+
|
|
31
36
|
|
|
32
37
|
def file_uri_or_path_to_path(value) -> pathlib.Path:
|
|
33
38
|
# Keep behavior close to your code
|
|
@@ -190,40 +195,8 @@ def extract_record_id(value: str) -> str:
|
|
|
190
195
|
raise ValueError(f"Could not extract a numeric record ID from '{value}'")
|
|
191
196
|
|
|
192
197
|
|
|
193
|
-
def get_file_metadata(record: str, sandbox: bool = False) -> Iterator[Tuple[str, Dict]]:
|
|
194
|
-
"""
|
|
195
|
-
Fetch the Zenodo record JSON and yield (filename, file-metadata-dict) tuples.
|
|
196
|
-
"""
|
|
197
|
-
record_id = extract_record_id(record)
|
|
198
|
-
base_url = "https://sandbox.zenodo.org/api/records" if sandbox else "https://zenodo.org/api/records"
|
|
199
|
-
url = f"{base_url}/{record_id}"
|
|
200
|
-
|
|
201
|
-
resp = requests.get(url)
|
|
202
|
-
if resp.status_code != 200:
|
|
203
|
-
raise RuntimeError(
|
|
204
|
-
f"Failed to fetch record {record_id}: HTTP {resp.status_code} - {resp.text}"
|
|
205
|
-
)
|
|
206
198
|
|
|
207
|
-
data = resp.json()
|
|
208
199
|
|
|
209
|
-
# Zenodo records normally have 'files' at the top level
|
|
210
|
-
files = data.get("files", [])
|
|
211
|
-
|
|
212
|
-
if not files:
|
|
213
|
-
# Some records may store file info differently; handle gracefully
|
|
214
|
-
raise RuntimeError(f"No files found in record {record_id} (or unexpected schema).")
|
|
215
|
-
|
|
216
|
-
for f in files:
|
|
217
|
-
# Common fields in Zenodo API
|
|
218
|
-
name = f.get("key") or f.get("filename") or "<unknown>"
|
|
219
|
-
yield name, f
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def _is_sandbox_doi(doi: str) -> bool:
|
|
223
|
-
"""Return True if the provided doi string looks like a sandbox URL/DOI."""
|
|
224
|
-
if "10.5281/zenodo" in doi:
|
|
225
|
-
return True
|
|
226
|
-
return False
|
|
227
200
|
|
|
228
201
|
|
|
229
202
|
def download_and_verify_file(file_meta: Dict, dest_dir: pathlib.Path) -> Tuple[pathlib.Path, bool, str, str]:
|
|
@@ -312,7 +285,8 @@ def parse_checksum(raw: str) -> Tuple[str, str]:
|
|
|
312
285
|
return algo.lower(), val.lower()
|
|
313
286
|
|
|
314
287
|
|
|
315
|
-
def download(download_directory: pathlib.Path, web_resources: List[WebResource]) -> List[
|
|
288
|
+
def download(download_directory: pathlib.Path, web_resources: List[WebResource], use_agent_header=True) -> List[
|
|
289
|
+
DownloadStatus]:
|
|
316
290
|
"""Download and verify a list of WebResource items.
|
|
317
291
|
|
|
318
292
|
For each resource:
|
|
@@ -403,7 +377,11 @@ def download(download_directory: pathlib.Path, web_resources: List[WebResource])
|
|
|
403
377
|
# stream download to tmp file while computing md5
|
|
404
378
|
tmp_path = record_dir / (filename + ".tmp")
|
|
405
379
|
try:
|
|
406
|
-
|
|
380
|
+
if use_agent_header:
|
|
381
|
+
kwargs_request = {"headers": USER_AGENT_HEADER}
|
|
382
|
+
else:
|
|
383
|
+
kwargs_request = {}
|
|
384
|
+
resp = requests.get(url, stream=True, timeout=60, **kwargs_request)
|
|
407
385
|
resp.raise_for_status()
|
|
408
386
|
|
|
409
387
|
md5 = hashlib.md5()
|
|
@@ -426,27 +404,27 @@ def download(download_directory: pathlib.Path, web_resources: List[WebResource])
|
|
|
426
404
|
# move tmp to final
|
|
427
405
|
tmp_path.replace(dest_path)
|
|
428
406
|
message = f"saved to {dest_path}"
|
|
429
|
-
|
|
407
|
+
logger.debug(f" > OK: {filename} -> {dest_path} (md5 matches)")
|
|
430
408
|
else:
|
|
431
409
|
# conflict: keep both, rename new file with record id + short hash
|
|
432
410
|
new_name = f"{pathlib.Path(filename).stem}__{rec_id}__{computed[:8]}{pathlib.Path(filename).suffix}"
|
|
433
411
|
new_path = record_dir / new_name
|
|
434
412
|
tmp_path.replace(new_path)
|
|
435
413
|
message = f"checksum mismatch: expected={expected_val}, computed={computed}; saved as {new_path}"
|
|
436
|
-
|
|
414
|
+
logger.error(
|
|
437
415
|
f" > MISMATCH: {filename} -> {new_path} (expected md5: {expected_val}, computed md5: {computed})")
|
|
438
416
|
else:
|
|
439
417
|
# no expected checksum provided — accept and move
|
|
440
418
|
tmp_path.replace(dest_path)
|
|
441
419
|
ok = True
|
|
442
420
|
message = f"saved to {dest_path} (no expected checksum)"
|
|
443
|
-
|
|
421
|
+
logger.debug(f" > SAVED (no expected checksum): {filename} -> {dest_path}")
|
|
444
422
|
else:
|
|
445
423
|
# unsupported algorithm: save file but mark as not-verified
|
|
446
424
|
tmp_path.replace(dest_path)
|
|
447
425
|
ok = False
|
|
448
426
|
message = f"saved to {dest_path} (unsupported checksum algorithm: {expected_algo})"
|
|
449
|
-
|
|
427
|
+
logger.debug(f" > SAVED (unsupported checksum alg '{expected_algo}'): {filename} -> {dest_path}")
|
|
450
428
|
|
|
451
429
|
overall.append(DownloadStatus(rec_id, dest_path, ok, message))
|
|
452
430
|
|
|
@@ -457,13 +435,13 @@ def download(download_directory: pathlib.Path, web_resources: List[WebResource])
|
|
|
457
435
|
tmp_path.unlink()
|
|
458
436
|
except Exception:
|
|
459
437
|
pass
|
|
460
|
-
|
|
438
|
+
logger.error(f" > ERROR downloading {filename}: {exc}")
|
|
461
439
|
overall.append(DownloadStatus(rec_id, dest_path, False, str(exc)))
|
|
462
440
|
|
|
463
441
|
# summary
|
|
464
442
|
total = len(overall)
|
|
465
443
|
ok_count = sum(1 for o in overall if o.ok)
|
|
466
|
-
|
|
444
|
+
logger.debug(f"Summary: {ok_count}/{total} files verified successfully")
|
|
467
445
|
|
|
468
446
|
return overall
|
|
469
447
|
|
|
@@ -542,7 +520,7 @@ def database_initialization(
|
|
|
542
520
|
mediaType="text/turtle"
|
|
543
521
|
) for row in results]
|
|
544
522
|
|
|
545
|
-
|
|
523
|
+
logger.debug(
|
|
546
524
|
f"Found {len(list_of_ttl_web_resources)} TTL web resources to download. Downloading to {download_directory}...")
|
|
547
525
|
return download(
|
|
548
526
|
download_directory=download_directory,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
|
|
3
|
+
from h5rdmtoolbox import __version__
|
|
4
|
+
|
|
5
|
+
USER_AGENT_HEADER = {
|
|
6
|
+
"User-Agent": f"h5rdmtoolbox/{__version__} (https://github.com/matthiasprobst/h5rdmtoolbox)",
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
|
|
4
10
|
def validate_url(url: str) -> bool:
|
|
5
11
|
"""Validate URL
|
|
@@ -15,7 +21,7 @@ def validate_url(url: str) -> bool:
|
|
|
15
21
|
True if URL
|
|
16
22
|
"""
|
|
17
23
|
try:
|
|
18
|
-
response = requests.get(url)
|
|
24
|
+
response = requests.get(url, headers=USER_AGENT_HEADER)
|
|
19
25
|
except requests.exceptions.MissingSchema:
|
|
20
26
|
return False
|
|
21
27
|
if response.status_code == 200:
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
import pathlib
|
|
4
4
|
import uuid
|
|
5
5
|
import warnings
|
|
6
|
-
from typing import Optional, Union
|
|
6
|
+
from typing import Optional, Union, Dict
|
|
7
7
|
|
|
8
8
|
import requests
|
|
9
9
|
|
|
@@ -15,7 +15,8 @@ def download_file(file_url,
|
|
|
15
15
|
target_folder: Union[str, pathlib.Path] = None,
|
|
16
16
|
access_token: Optional[str] = None,
|
|
17
17
|
checksum: Optional[str] = None,
|
|
18
|
-
checksum_algorithm: Optional[str] = None
|
|
18
|
+
checksum_algorithm: Optional[str] = None,
|
|
19
|
+
headers: Optional[Dict]=None) -> pathlib.Path:
|
|
19
20
|
logger.debug(f'Attempting to provide file from URL (download or return from cache): {file_url}')
|
|
20
21
|
from ..utils import DownloadFileManager
|
|
21
22
|
dfm = DownloadFileManager()
|
|
@@ -54,11 +55,11 @@ def download_file(file_url,
|
|
|
54
55
|
raise ValueError(f"Unsupported checksum algorithm: {checksum_algorithm}")
|
|
55
56
|
|
|
56
57
|
try:
|
|
57
|
-
response = requests.get(file_url, stream=True)
|
|
58
|
+
response = requests.get(file_url, stream=True, headers=headers)
|
|
58
59
|
if response.status_code == 403:
|
|
59
|
-
response = requests.get(file_url, stream=True, params={'access_token': access_token})
|
|
60
|
+
response = requests.get(file_url, stream=True, params={'access_token': access_token}, headers=headers)
|
|
60
61
|
except requests.RequestException as e:
|
|
61
|
-
response = requests.get(file_url, stream=True, params={'access_token': access_token})
|
|
62
|
+
response = requests.get(file_url, stream=True, params={'access_token': access_token}, headers=headers)
|
|
62
63
|
response.raise_for_status()
|
|
63
64
|
|
|
64
65
|
chunk_size = 1024 # Define chunk size for download
|
|
@@ -107,7 +108,7 @@ def download_file(file_url,
|
|
|
107
108
|
# f'but got: {file_checksum}')
|
|
108
109
|
else:
|
|
109
110
|
try:
|
|
110
|
-
r = requests.get(file_url)
|
|
111
|
+
r = requests.get(file_url, headers=headers)
|
|
111
112
|
except requests.RequestException as e:
|
|
112
113
|
r = requests.get(file_url, params={'access_token': access_token})
|
|
113
114
|
|
|
@@ -126,9 +127,9 @@ def download_file(file_url,
|
|
|
126
127
|
file.write(r.content)
|
|
127
128
|
if links_content:
|
|
128
129
|
try:
|
|
129
|
-
_content_response = requests.get(links_content)
|
|
130
|
+
_content_response = requests.get(links_content, headers=headers)
|
|
130
131
|
except requests.RequestException:
|
|
131
|
-
_content_response = requests.get(links_content, params={'access_token': access_token})
|
|
132
|
+
_content_response = requests.get(links_content, params={'access_token': access_token}, headers=headers)
|
|
132
133
|
if _content_response.ok:
|
|
133
134
|
with open(target_filename, 'wb') as file:
|
|
134
135
|
file.write(_content_response.content)
|