h5rdmtoolbox 2.5.3__py3-none-any.whl
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/__init__.py +424 -0
- h5rdmtoolbox/_cfg.py +104 -0
- h5rdmtoolbox/_repr.py +747 -0
- h5rdmtoolbox/_version.py +6 -0
- h5rdmtoolbox/cli.py +61 -0
- h5rdmtoolbox/consts.py +3 -0
- h5rdmtoolbox/convention/__init__.py +41 -0
- h5rdmtoolbox/convention/cfg.py +2 -0
- h5rdmtoolbox/convention/consts.py +28 -0
- h5rdmtoolbox/convention/core.py +817 -0
- h5rdmtoolbox/convention/errors.py +34 -0
- h5rdmtoolbox/convention/generate.py +385 -0
- h5rdmtoolbox/convention/ontology/__init__.py +3 -0
- h5rdmtoolbox/convention/ontology/h5ontocls.py +133 -0
- h5rdmtoolbox/convention/ontology/hdf_datatypes.py +64 -0
- h5rdmtoolbox/convention/references.py +23 -0
- h5rdmtoolbox/convention/standard_attributes.py +383 -0
- h5rdmtoolbox/convention/toolbox_validators.py +175 -0
- h5rdmtoolbox/convention/utils.py +216 -0
- h5rdmtoolbox/data/def_icon.png +0 -0
- h5rdmtoolbox/data/fluid-v1.yml +36 -0
- h5rdmtoolbox/data/h5tbx.yaml +28 -0
- h5rdmtoolbox/data/id_icon.png +0 -0
- h5rdmtoolbox/data/iri_icon.png +0 -0
- h5rdmtoolbox/data/ld_icon.png +0 -0
- h5rdmtoolbox/data/piv-v1.yml +35 -0
- h5rdmtoolbox/data/style.css +216 -0
- h5rdmtoolbox/data/tutorial_convention.yaml +91 -0
- h5rdmtoolbox/data/tutorial_standard_name_table.yaml +390 -0
- h5rdmtoolbox/data/type_icon.png +0 -0
- h5rdmtoolbox/database/__init__.py +75 -0
- h5rdmtoolbox/database/hdfdb/__init__.py +4 -0
- h5rdmtoolbox/database/hdfdb/filedb.py +129 -0
- h5rdmtoolbox/database/hdfdb/objdb.py +520 -0
- h5rdmtoolbox/database/hdfdb/query.py +133 -0
- h5rdmtoolbox/database/hdfdb/utils.py +51 -0
- h5rdmtoolbox/database/interface.py +59 -0
- h5rdmtoolbox/database/lazy.py +183 -0
- h5rdmtoolbox/database/mongo.py +374 -0
- h5rdmtoolbox/errors.py +4 -0
- h5rdmtoolbox/extensions/__init__.py +0 -0
- h5rdmtoolbox/extensions/magnitude.py +67 -0
- h5rdmtoolbox/extensions/normalize.py +103 -0
- h5rdmtoolbox/extensions/onto.py +52 -0
- h5rdmtoolbox/extensions/units.py +48 -0
- h5rdmtoolbox/extensions/vector.py +138 -0
- h5rdmtoolbox/gui/__init__.py +0 -0
- h5rdmtoolbox/gui/fairify.py +446 -0
- h5rdmtoolbox/gui/src/__init__.py +0 -0
- h5rdmtoolbox/gui/src/main.py +131 -0
- h5rdmtoolbox/identifiers.py +257 -0
- h5rdmtoolbox/layout/__init__.py +4 -0
- h5rdmtoolbox/layout/core.py +624 -0
- h5rdmtoolbox/ld/__init__.py +129 -0
- h5rdmtoolbox/ld/globals.py +3 -0
- h5rdmtoolbox/ld/hdf/__init__.py +0 -0
- h5rdmtoolbox/ld/hdf/attributes.py +36 -0
- h5rdmtoolbox/ld/hdf/datasets.py +160 -0
- h5rdmtoolbox/ld/hdf/file.py +54 -0
- h5rdmtoolbox/ld/hdf/groups.py +47 -0
- h5rdmtoolbox/ld/rdf.py +883 -0
- h5rdmtoolbox/ld/shacl.py +221 -0
- h5rdmtoolbox/ld/user/__init__.py +0 -0
- h5rdmtoolbox/ld/user/attributes.py +241 -0
- h5rdmtoolbox/ld/user/datasets.py +22 -0
- h5rdmtoolbox/ld/user/file.py +58 -0
- h5rdmtoolbox/ld/user/groups.py +58 -0
- h5rdmtoolbox/ld/user/utils.py +8 -0
- h5rdmtoolbox/ld/utils.py +133 -0
- h5rdmtoolbox/plotting.py +168 -0
- h5rdmtoolbox/protected_attributes.py +9 -0
- h5rdmtoolbox/protocols.py +268 -0
- h5rdmtoolbox/repository/__init__.py +11 -0
- h5rdmtoolbox/repository/default_metamapper.py +55 -0
- h5rdmtoolbox/repository/interface.py +214 -0
- h5rdmtoolbox/repository/utils.py +131 -0
- h5rdmtoolbox/repository/zenodo/__init__.py +5 -0
- h5rdmtoolbox/repository/zenodo/core.py +616 -0
- h5rdmtoolbox/repository/zenodo/metadata.py +276 -0
- h5rdmtoolbox/repository/zenodo/tokens.py +118 -0
- h5rdmtoolbox/repository/zenodo/utils.py +16 -0
- h5rdmtoolbox/tutorial.py +374 -0
- h5rdmtoolbox/user.py +158 -0
- h5rdmtoolbox/utils.py +743 -0
- h5rdmtoolbox/wrapper/__init__.py +8 -0
- h5rdmtoolbox/wrapper/accessor.py +97 -0
- h5rdmtoolbox/wrapper/accessors/__init__.py +0 -0
- h5rdmtoolbox/wrapper/accessors/provenance.py +36 -0
- h5rdmtoolbox/wrapper/core.py +2528 -0
- h5rdmtoolbox/wrapper/ds_decoder.py +113 -0
- h5rdmtoolbox/wrapper/h5attr.py +519 -0
- h5rdmtoolbox/wrapper/h5ext.py +20 -0
- h5rdmtoolbox/wrapper/h5utils.py +32 -0
- h5rdmtoolbox/wrapper/h5yaml.py +116 -0
- h5rdmtoolbox/wrapper/jsonld.py +1189 -0
- h5rdmtoolbox/wrapper/xr2hdf.py +106 -0
- h5rdmtoolbox-2.5.3.dist-info/METADATA +379 -0
- h5rdmtoolbox-2.5.3.dist-info/RECORD +144 -0
- h5rdmtoolbox-2.5.3.dist-info/WHEEL +5 -0
- h5rdmtoolbox-2.5.3.dist-info/entry_points.txt +2 -0
- h5rdmtoolbox-2.5.3.dist-info/licenses/LICENSE +20 -0
- h5rdmtoolbox-2.5.3.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/clean_zenodo_sandbox.py +23 -0
- tests/conventions/__init__.py +0 -0
- tests/conventions/test_convention_vfuncs.py +98 -0
- tests/conventions/test_conventions.py +697 -0
- tests/conventions/test_engmeta.py +55 -0
- tests/conventions/test_references.py +22 -0
- tests/conventions/test_toolbox_validators.py +138 -0
- tests/conventions/test_utils.py +94 -0
- tests/database/__init__.py +0 -0
- tests/database/test_find_rdf.py +36 -0
- tests/database/test_hdfDB.py +619 -0
- tests/database/test_mongo.py +229 -0
- tests/layouts/__init__.py +0 -0
- tests/layouts/test_core.py +362 -0
- tests/layouts/test_docs.py +66 -0
- tests/ld/__init__.py +0 -0
- tests/ld/test_ld.py +904 -0
- tests/ld/test_rdf.py +688 -0
- tests/ld/test_shacl.py +223 -0
- tests/ld/test_user_graph.py +278 -0
- tests/ld/test_utils.py +13 -0
- tests/repository/__init__.py +0 -0
- tests/repository/test_zenodo.py +728 -0
- tests/test_cli.py +69 -0
- tests/test_config.py +80 -0
- tests/test_extensions.py +148 -0
- tests/test_identifiers.py +85 -0
- tests/test_plotting.py +155 -0
- tests/test_repr.py +114 -0
- tests/test_user.py +100 -0
- tests/test_utils.py +143 -0
- tests/test_version.py +72 -0
- tests/test_xarray_export.py +47 -0
- tests/wrapper/__init__.py +0 -0
- tests/wrapper/test_core.py +994 -0
- tests/wrapper/test_dump.py +77 -0
- tests/wrapper/test_file.py +411 -0
- tests/wrapper/test_h5ext.py +22 -0
- tests/wrapper/test_lazy.py +62 -0
- tests/wrapper/test_namespaces.py +46 -0
- tests/wrapper/test_xr2df.py +41 -0
h5rdmtoolbox/__init__.py
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
"""h5rdmtoolbox repository"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import pathlib
|
|
5
|
+
import warnings
|
|
6
|
+
from logging.handlers import RotatingFileHandler
|
|
7
|
+
from typing import Optional, Dict
|
|
8
|
+
|
|
9
|
+
import rdflib
|
|
10
|
+
|
|
11
|
+
from ._version import __version__
|
|
12
|
+
from .user import USER_LOG_DIR, USER_DATA_DIR
|
|
13
|
+
|
|
14
|
+
DEFAULT_LOGGING_LEVEL = logging.WARNING
|
|
15
|
+
_formatter = logging.Formatter(
|
|
16
|
+
'%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
|
|
17
|
+
datefmt='%Y-%m-%d_%H:%M:%S')
|
|
18
|
+
|
|
19
|
+
_stream_handler = logging.StreamHandler()
|
|
20
|
+
_stream_handler.setLevel(DEFAULT_LOGGING_LEVEL)
|
|
21
|
+
_stream_handler.setFormatter(_formatter)
|
|
22
|
+
|
|
23
|
+
_file_handler = RotatingFileHandler(USER_LOG_DIR / 'h5rdmtoolbox.log')
|
|
24
|
+
_file_handler.setLevel(logging.DEBUG) # log everything to file!
|
|
25
|
+
_file_handler.setFormatter(_formatter)
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__package__)
|
|
28
|
+
logger.addHandler(_stream_handler)
|
|
29
|
+
logger.addHandler(_file_handler)
|
|
30
|
+
|
|
31
|
+
import atexit
|
|
32
|
+
# noinspection PyUnresolvedReferences
|
|
33
|
+
import pint_xarray
|
|
34
|
+
import shutil
|
|
35
|
+
import xarray as xr
|
|
36
|
+
from typing import Union, Callable
|
|
37
|
+
from pydantic import HttpUrl
|
|
38
|
+
|
|
39
|
+
from h5rdmtoolbox._cfg import set_config, get_config, get_ureg
|
|
40
|
+
|
|
41
|
+
pint_xarray.unit_registry = get_ureg()
|
|
42
|
+
|
|
43
|
+
from . import convention
|
|
44
|
+
from .convention.core import Convention
|
|
45
|
+
from . import wrapper
|
|
46
|
+
from .user import UserDir
|
|
47
|
+
from ._version import __version__
|
|
48
|
+
from . import utils
|
|
49
|
+
from .wrapper.core import lower, Lower, File, Group, Dataset
|
|
50
|
+
from . import errors
|
|
51
|
+
from .wrapper import jsonld
|
|
52
|
+
from .database.lazy import lazy
|
|
53
|
+
from .wrapper.h5attr import Attribute
|
|
54
|
+
import json
|
|
55
|
+
from .wrapper.accessor import register_accessor
|
|
56
|
+
|
|
57
|
+
from .ld.shacl import validate_hdf, ValidationResult
|
|
58
|
+
# noinspection PyUnresolvedReferences
|
|
59
|
+
from .utils import DownloadFileManager
|
|
60
|
+
|
|
61
|
+
name = 'h5rdmtoolbox'
|
|
62
|
+
__this_dir__ = pathlib.Path(__file__).parent
|
|
63
|
+
__author__ = 'Matthias Probst'
|
|
64
|
+
__author_orcid__ = 'https://orcid.org/0000-0001-8729-0482'
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_package_meta() -> Dict:
|
|
68
|
+
"""Reads codemeta.json and returns it as dict"""
|
|
69
|
+
with open(__this_dir__ / '../codemeta.json', 'r') as f:
|
|
70
|
+
codemeta = json.loads(f.read())
|
|
71
|
+
return codemeta
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
cv_h5py = convention.Convention(name='h5py',
|
|
75
|
+
contact=__author_orcid__)
|
|
76
|
+
cv_h5py.register()
|
|
77
|
+
|
|
78
|
+
use = convention.core.use
|
|
79
|
+
use(None)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def dump(src: Union[str, File, pathlib.Path],
|
|
83
|
+
*args, **kwargs) -> None:
|
|
84
|
+
"""Call h5.dump() on the provided HDF5 file
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
src : str, File, pathlib.Path
|
|
89
|
+
the HDF5 file or filename to dump. An object which has a hdf_filename attribute can also be provided.
|
|
90
|
+
kwargs
|
|
91
|
+
kwargs passed to h5.dump(). See h5.dump() for more information.
|
|
92
|
+
"""
|
|
93
|
+
if isinstance(src, File):
|
|
94
|
+
with File(src.hdf_filename) as h5:
|
|
95
|
+
return h5.dump(*args, **kwargs)
|
|
96
|
+
|
|
97
|
+
if isinstance(src, (str, pathlib.Path)):
|
|
98
|
+
pass
|
|
99
|
+
else:
|
|
100
|
+
if hasattr(src, 'hdf_filename'):
|
|
101
|
+
src = src.hdf_filename
|
|
102
|
+
|
|
103
|
+
with File(src) as h5:
|
|
104
|
+
return h5.dump(**kwargs)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def dumps(src: Union[str, File, pathlib.Path]):
|
|
108
|
+
"""Call h5.dumps() on the provided HDF5 file
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
src : str, File, pathlib.Path
|
|
113
|
+
the HDF5 file or filename to dump. An object which has a hdf_filename attribute can also be provided.
|
|
114
|
+
"""
|
|
115
|
+
if isinstance(src, File):
|
|
116
|
+
with File(src.hdf_filename) as h5:
|
|
117
|
+
return h5.dumps()
|
|
118
|
+
|
|
119
|
+
if isinstance(src, (str, pathlib.Path)):
|
|
120
|
+
pass
|
|
121
|
+
else:
|
|
122
|
+
if hasattr(src, 'hdf_filename'):
|
|
123
|
+
src = src.hdf_filename
|
|
124
|
+
|
|
125
|
+
with File(src) as h5:
|
|
126
|
+
return h5.dumps()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_ld(
|
|
130
|
+
hdf_filename: Union[str, pathlib.Path],
|
|
131
|
+
structural: bool = True,
|
|
132
|
+
contextual: bool = True,
|
|
133
|
+
skipND: int = 1,
|
|
134
|
+
file_uri: Optional[str] = None,
|
|
135
|
+
context: Optional[Dict] = None
|
|
136
|
+
) -> rdflib.Graph:
|
|
137
|
+
"""Return the HDF file content as a rdflib.Graph object."""
|
|
138
|
+
from h5rdmtoolbox.ld import get_ld
|
|
139
|
+
return get_ld(hdf_filename,
|
|
140
|
+
structural=structural,
|
|
141
|
+
contextual=contextual,
|
|
142
|
+
skipND=skipND,
|
|
143
|
+
file_uri=file_uri,
|
|
144
|
+
context=context)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def dump_jsonld(
|
|
148
|
+
hdf_filename: Union[str, pathlib.Path],
|
|
149
|
+
skipND: int = 1,
|
|
150
|
+
indent: int = 2,
|
|
151
|
+
structural: bool = True,
|
|
152
|
+
contextual: bool = True,
|
|
153
|
+
context: Optional[Dict] = None,
|
|
154
|
+
file_uri: Optional[str] = None
|
|
155
|
+
):
|
|
156
|
+
"""Return the file content as a JSON-LD string."""
|
|
157
|
+
from .ld import optimize_context
|
|
158
|
+
context = context or {}
|
|
159
|
+
graph = get_ld(hdf_filename,
|
|
160
|
+
structural=structural,
|
|
161
|
+
contextual=contextual,
|
|
162
|
+
file_uri=file_uri,
|
|
163
|
+
skipND=skipND)
|
|
164
|
+
context = optimize_context(graph, context)
|
|
165
|
+
return graph.serialize(
|
|
166
|
+
format="json-ld",
|
|
167
|
+
indent=indent,
|
|
168
|
+
auto_compact=True,
|
|
169
|
+
context=context
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# def shacl_validate(
|
|
174
|
+
# *,
|
|
175
|
+
# hdf_data: Union[str, rdflib.Graph] = None,
|
|
176
|
+
# hdf_source: Union[h5py.File, pathlib.Path] = None,
|
|
177
|
+
# shacl_data: Union[str, rdflib.Graph] = None,
|
|
178
|
+
# shacl_source: Union[str, pathlib.Path] = None,
|
|
179
|
+
# hdf_file_uri="https://example.org/hdf5file#",
|
|
180
|
+
# shacl_format: str = 'turtle',
|
|
181
|
+
# hdf_data_format: str = 'turtle',
|
|
182
|
+
# **pyshacl_kwargs
|
|
183
|
+
# ) -> ValidationResult:
|
|
184
|
+
# """Validate HDF5 file content against SHACL shapes.
|
|
185
|
+
#
|
|
186
|
+
# Parameters
|
|
187
|
+
# ----------
|
|
188
|
+
# hdf_data : Union[str, rdflib.Graph], optional
|
|
189
|
+
# RDF data of the HDF5 file as string or rdflib.Graph. If not
|
|
190
|
+
# provided, `hdf_source` must be provided.
|
|
191
|
+
# hdf_source : Union[h5py.File, pathlib.Path], optional
|
|
192
|
+
# HDF5 file or h5py.File object to extract RDF data from. If not
|
|
193
|
+
# provided, `hdf_data` must be provided.
|
|
194
|
+
# shacl_data : Union[str, rdflib.Graph], optional
|
|
195
|
+
# SHACL shapes as string or rdflib.Graph. If not provided,
|
|
196
|
+
# `shacl_source` must be provided.
|
|
197
|
+
# shacl_source : Union[str, pathlib.Path], optional
|
|
198
|
+
# File path to SHACL shapes. If not provided, `shacl_data`
|
|
199
|
+
# must be provided.
|
|
200
|
+
# hdf_file_uri : str, optional
|
|
201
|
+
# The file URI to use for the HDF5 file when extracting RDF data.
|
|
202
|
+
# Default is "https://example.org/hdf5file#".
|
|
203
|
+
# shacl_format : str, optional
|
|
204
|
+
# The format of the SHACL shapes if `shacl_data` is provided as
|
|
205
|
+
# string. Default is 'turtle'.
|
|
206
|
+
# hdf_data_format : str, optional
|
|
207
|
+
# The format of the HDF5 RDF data if `hdf_data` is provided as
|
|
208
|
+
# string. Default is 'turtle'.
|
|
209
|
+
# **pyshacl_kwargs
|
|
210
|
+
# Additional keyword arguments passed to pyshacl.validate().
|
|
211
|
+
#
|
|
212
|
+
# Returns
|
|
213
|
+
# -------
|
|
214
|
+
# ValidationResult
|
|
215
|
+
# The result of the SHACL validation.
|
|
216
|
+
# """
|
|
217
|
+
# return validate_hdf(
|
|
218
|
+
# hdf_data=hdf_data,
|
|
219
|
+
# hdf_source=hdf_source,
|
|
220
|
+
# shacl_data=shacl_data,
|
|
221
|
+
# shacl_source=shacl_source,
|
|
222
|
+
# hdf_file_uri=hdf_file_uri,
|
|
223
|
+
# shacl_format=shacl_format,
|
|
224
|
+
# hdf_data_format=hdf_data_format,
|
|
225
|
+
# **pyshacl_kwargs
|
|
226
|
+
# )
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def dump_jsonld_depr(hdf_filename: Union[str, pathlib.Path],
|
|
230
|
+
skipND: int = 1,
|
|
231
|
+
structural: bool = True,
|
|
232
|
+
contextual: bool = True,
|
|
233
|
+
context: Optional[Dict] = None,
|
|
234
|
+
blank_node_iri_base: Optional[str] = None
|
|
235
|
+
) -> str:
|
|
236
|
+
"""Dump the JSON-LD representation of the file. With semantic=True and structural=False, the JSON-LD
|
|
237
|
+
represents the semantic content only. To get a pure structural representation, set semantic=False, which
|
|
238
|
+
will ignore any RDF content. If both are set to True, the JSON-LD will contain both structural and semantic.
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
hdf_filename : str, pathlib.Path
|
|
243
|
+
the HDF5 file to dump.
|
|
244
|
+
skipND : int=1
|
|
245
|
+
Skip writing data of datasets with more than `skipND` dimensions. Only
|
|
246
|
+
considered if structural=True.
|
|
247
|
+
structural : bool=True
|
|
248
|
+
Include structural information in the JSON-LD output.
|
|
249
|
+
contextual : bool=True
|
|
250
|
+
Include contextual information in the JSON-LD output.
|
|
251
|
+
context: Optional[Dict]
|
|
252
|
+
context in form of {prefix: IRI}, e.g. "ssno": "https://matthiasprobst.github.io/ssno#"
|
|
253
|
+
blank_node_iri_base: Optional[str]
|
|
254
|
+
IRI base used for blank nodes
|
|
255
|
+
"""
|
|
256
|
+
if blank_node_iri_base is not None:
|
|
257
|
+
if not isinstance(blank_node_iri_base, (str, HttpUrl)):
|
|
258
|
+
raise ValueError('blank_node_iri_base must be a valid URL')
|
|
259
|
+
from .wrapper import jsonld
|
|
260
|
+
if not structural and not contextual:
|
|
261
|
+
raise ValueError('At least one of structural or contextual must be True.')
|
|
262
|
+
|
|
263
|
+
from h5rdmtoolbox.ld.hdf.file import get_serialized_ld
|
|
264
|
+
if structural and not contextual:
|
|
265
|
+
return get_serialized_ld(
|
|
266
|
+
hdf_filename,
|
|
267
|
+
blank_node_iri_base,
|
|
268
|
+
format="json-ld",
|
|
269
|
+
context=context,
|
|
270
|
+
skipND=skipND)
|
|
271
|
+
return get_serialized_ld(
|
|
272
|
+
hdf_filename,
|
|
273
|
+
blank_node_iri_base,
|
|
274
|
+
format="json-ld",
|
|
275
|
+
context=context,
|
|
276
|
+
skipND=skipND)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def serialize(hdf_filename,
|
|
280
|
+
fmt: str = "ttl",
|
|
281
|
+
skipND: int = 1,
|
|
282
|
+
structural: bool = True,
|
|
283
|
+
contextual: bool = True,
|
|
284
|
+
file_uri: Optional[str] = None,
|
|
285
|
+
**kwargs):
|
|
286
|
+
"""Alternative to json-ld but allows multiple serialization options"""
|
|
287
|
+
fmt = kwargs.pop("format", fmt)
|
|
288
|
+
with File(hdf_filename) as h5:
|
|
289
|
+
return h5.serialize(fmt=fmt,
|
|
290
|
+
skipND=skipND,
|
|
291
|
+
structural=structural,
|
|
292
|
+
contextual=contextual,
|
|
293
|
+
file_uri=file_uri)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def build_pyvis_graph(hdf_filename, output_filename="kg-graph.html", notebook=False,
|
|
297
|
+
structural: bool = True,
|
|
298
|
+
contextual: bool = True,
|
|
299
|
+
style: Dict = None):
|
|
300
|
+
"""Calls `build_pyvis_graph` of kglab library. Requires kglab and pyvis"""
|
|
301
|
+
try:
|
|
302
|
+
import kglab
|
|
303
|
+
except ImportError:
|
|
304
|
+
raise ImportError('kglab is required for this function. Install it using: pip install kglab')
|
|
305
|
+
kg = kglab.KnowledgeGraph().load_rdf_text(
|
|
306
|
+
serialize(hdf_filename, fmt="ttl", structural=structural, contextual=contextual)
|
|
307
|
+
)
|
|
308
|
+
vis_style = style or {
|
|
309
|
+
"hdf": {
|
|
310
|
+
"color": "orange",
|
|
311
|
+
"size": 40,
|
|
312
|
+
},
|
|
313
|
+
"ind": {
|
|
314
|
+
"color": "blue",
|
|
315
|
+
"size": 30,
|
|
316
|
+
},
|
|
317
|
+
}
|
|
318
|
+
subgraph = kglab.SubgraphTensor(kg)
|
|
319
|
+
pyvis_graph = subgraph.build_pyvis_graph(notebook=notebook, style=vis_style)
|
|
320
|
+
pyvis_graph.force_atlas_2based()
|
|
321
|
+
pyvis_graph.show(output_filename)
|
|
322
|
+
return pyvis_graph
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def get_filesize(hdf_filename: Union[str, pathlib.Path]) -> int:
|
|
326
|
+
"""Get the size of the HDF5 file in bytes"""
|
|
327
|
+
return utils.get_filesize(hdf_filename)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def get_checksum(hdf_filename: Union[str, pathlib.Path]) -> str:
|
|
331
|
+
"""Get the checksum of the HDF5 file"""
|
|
332
|
+
return utils.get_checksum(hdf_filename)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def register_dataset_decoder(decoder: Callable, decoder_name: str = None, overwrite: bool = False):
|
|
336
|
+
"""A decoder function takes a xarray.DataArray and a dataset as input and returns a xarray.DataArray
|
|
337
|
+
It is called after the dataset is loaded into memory and before being returned to the user. Be careful:
|
|
338
|
+
Multiple decoders can be registered, and they are called in the order of registration. Hence, your decoder
|
|
339
|
+
may behave unexpectedly!
|
|
340
|
+
"""
|
|
341
|
+
from .wrapper import ds_decoder
|
|
342
|
+
if decoder_name is None:
|
|
343
|
+
decoder_name = decoder.__name__
|
|
344
|
+
registered_decorators = ds_decoder.registered_dataset_decoders
|
|
345
|
+
if decoder_name in registered_decorators or decoder in registered_decorators.values():
|
|
346
|
+
if not overwrite:
|
|
347
|
+
raise ValueError(f'decoder "{decoder_name}" already registered. Name and function must be unique.')
|
|
348
|
+
ds_decoder.registered_dataset_decoders[decoder_name] = decoder
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
_ATEXIT_VERBOSE = False
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def set_loglevel(level: Union[int, str]):
|
|
355
|
+
"""Set the logging level of the h5rdmtoolbox logger"""
|
|
356
|
+
import logging
|
|
357
|
+
_logger = logging.getLogger('h5rdmtoolbox')
|
|
358
|
+
_logger.setLevel(level)
|
|
359
|
+
for h in _logger.handlers:
|
|
360
|
+
h.setLevel(level)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@atexit.register
|
|
364
|
+
def clean_temp_data(full: bool = False):
|
|
365
|
+
"""cleaning up the tmp directory"""
|
|
366
|
+
|
|
367
|
+
failed_dirs = []
|
|
368
|
+
failed_dirs_file = UserDir['tmp'] / 'failed.txt'
|
|
369
|
+
if full:
|
|
370
|
+
root_tmp = USER_DATA_DIR / 'tmp'
|
|
371
|
+
if root_tmp.exists():
|
|
372
|
+
try:
|
|
373
|
+
shutil.rmtree(root_tmp)
|
|
374
|
+
root_tmp.mkdir(exist_ok=True, parents=True)
|
|
375
|
+
except PermissionError as e:
|
|
376
|
+
print(f'removing tmp folder "{root_tmp}" failed due to "{e}".')
|
|
377
|
+
return
|
|
378
|
+
|
|
379
|
+
for _tmp_session_dir in [UserDir['tmp'], ]:
|
|
380
|
+
if _ATEXIT_VERBOSE:
|
|
381
|
+
print(f'cleaning up tmp directory "{_tmp_session_dir}"')
|
|
382
|
+
if _tmp_session_dir.exists():
|
|
383
|
+
try:
|
|
384
|
+
if _ATEXIT_VERBOSE:
|
|
385
|
+
print(f'try deleting tmp in session dir: {_tmp_session_dir}')
|
|
386
|
+
shutil.rmtree(_tmp_session_dir)
|
|
387
|
+
except PermissionError as e:
|
|
388
|
+
if _ATEXIT_VERBOSE:
|
|
389
|
+
print(f'[!] failed deleting tmp session dir: {_tmp_session_dir}')
|
|
390
|
+
failed_dirs.append(UserDir['tmp'])
|
|
391
|
+
if _ATEXIT_VERBOSE:
|
|
392
|
+
print(f'removing tmp folder "{_tmp_session_dir}" failed due to "{e}". Best is you '
|
|
393
|
+
f'manually delete the directory.')
|
|
394
|
+
finally:
|
|
395
|
+
lines = []
|
|
396
|
+
if failed_dirs_file.exists():
|
|
397
|
+
with open(failed_dirs_file, 'r') as f:
|
|
398
|
+
lines = f.readlines()
|
|
399
|
+
for line in lines:
|
|
400
|
+
try:
|
|
401
|
+
shutil.rmtree(line.strip())
|
|
402
|
+
except Exception:
|
|
403
|
+
if pathlib.Path(line).exists():
|
|
404
|
+
failed_dirs.append(line)
|
|
405
|
+
|
|
406
|
+
if lines or failed_dirs:
|
|
407
|
+
with open(failed_dirs_file, 'w') as f:
|
|
408
|
+
for fd in failed_dirs:
|
|
409
|
+
f.writelines(f'{fd}\n')
|
|
410
|
+
else:
|
|
411
|
+
failed_dirs_file.unlink(missing_ok=True)
|
|
412
|
+
else:
|
|
413
|
+
logger.debug(f'No user tmp dir not found: {_tmp_session_dir}')
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
xr.set_options(display_expand_data=False)
|
|
417
|
+
|
|
418
|
+
__all__ = ('__version__', '__author__', '__author_orcid__',
|
|
419
|
+
'UserDir', 'use',
|
|
420
|
+
'File', 'Group', 'Dataset', 'Attribute',
|
|
421
|
+
'dump', 'dumps', 'cv_h5py', 'lower', 'Lower',
|
|
422
|
+
'set_config', 'get_config', 'get_ureg',
|
|
423
|
+
'Convention', 'jsonld', 'lazy', 'DownloadFileManager',
|
|
424
|
+
'clean_temp_data')
|
h5rdmtoolbox/_cfg.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""package configuration. largely based on xarray's configuration concept. no credits taken!"""
|
|
2
|
+
from pint import UnitRegistry
|
|
3
|
+
from typing import Dict, Union
|
|
4
|
+
|
|
5
|
+
ureg = UnitRegistry(force_ndarray_like=True)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def is_valid_logger_level(level: Union[str, int]):
|
|
9
|
+
"""Check if the logger level is valid."""
|
|
10
|
+
if not isinstance(level, (str, int)):
|
|
11
|
+
# raise TypeError(f'Invalid type for the logger: {type(logger_value)}')
|
|
12
|
+
return False
|
|
13
|
+
if isinstance(level, str):
|
|
14
|
+
return level.lower() in ('error', 'debug', 'critical', 'warning', 'info', 'fatal', 'warning', 'warn')
|
|
15
|
+
# 0: NOTSET, 10: DEBUG, 20: INFO, 30: WARNING, 40: ERROR, 50: CRITICAL
|
|
16
|
+
return level in (0, 10, 20, 30, 40, 50)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
CONFIG = {
|
|
20
|
+
'return_xarray': True,
|
|
21
|
+
'advanced_shape_repr': True,
|
|
22
|
+
'natural_naming': True,
|
|
23
|
+
'hdf_compression': None, # 'gzip',
|
|
24
|
+
'hdf_compression_opts': None, # 5,
|
|
25
|
+
'adjusting_plotting_labels': True,
|
|
26
|
+
'xarray_unit_repr_in_plots': 'in',
|
|
27
|
+
'plotting_name_order': ('plot_name', 'long_name', 'standard_name'),
|
|
28
|
+
'require_unit': True, # datasets require units
|
|
29
|
+
'ureg_format': 'C~',
|
|
30
|
+
'init_logger_level': 'ERROR',
|
|
31
|
+
'dtime_fmt': '%Y-%m-%dT%H:%M:%S%f',
|
|
32
|
+
'expose_user_prop_to_attrs': True,
|
|
33
|
+
'add_provenance': False,
|
|
34
|
+
'ignore_set_std_attr_err': False,
|
|
35
|
+
'auto_create_h5tbx_version': False, # automatically creates the group h5rdmtoolbox with the version attribute
|
|
36
|
+
'uuid_name': 'uuid', # attribute name used for UUIDs
|
|
37
|
+
# if a standard attribute is defined and cannot be retrieved because the value is invalid, ignore it:
|
|
38
|
+
'ignore_get_std_attr_err': False,
|
|
39
|
+
'allow_deleting_standard_attributes': False,
|
|
40
|
+
'ignore_none': False
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_VALIDATORS = {
|
|
44
|
+
'return_xarray': lambda x: isinstance(x, bool),
|
|
45
|
+
'advanced_shape_repr': lambda x: isinstance(x, bool),
|
|
46
|
+
'natural_naming': lambda x: isinstance(x, bool),
|
|
47
|
+
'hdf_compression': lambda x: isinstance(x, str),
|
|
48
|
+
'hdf_compression_opts': lambda x: isinstance(x, int),
|
|
49
|
+
'xarray_unit_repr_in_plots': lambda x: x in ('/', '()', '(', '[]', '[', '//', 'in'),
|
|
50
|
+
'plotting_name_order': lambda x: isinstance(x, (tuple, list)) and [xx in ('plot_name', 'long_name', 'standard_name')
|
|
51
|
+
for xx in x],
|
|
52
|
+
'require_unit': lambda x: isinstance(x, bool),
|
|
53
|
+
'ureg_format': lambda x: isinstance(x, str),
|
|
54
|
+
'init_logger_level': lambda x: is_valid_logger_level(x),
|
|
55
|
+
'dtime_fmt': lambda x: isinstance(x, str),
|
|
56
|
+
'expose_user_prop_to_attrs': lambda x: isinstance(x, bool),
|
|
57
|
+
'add_provenance': lambda x: isinstance(x, bool),
|
|
58
|
+
'ignore_set_std_attr_err': lambda x: isinstance(x, bool),
|
|
59
|
+
'ignore_get_std_attr_err': lambda x: isinstance(x, bool),
|
|
60
|
+
'ignore_none': lambda x: isinstance(x, bool)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class set_config:
|
|
65
|
+
"""Set the configuration parameters."""
|
|
66
|
+
|
|
67
|
+
def __init__(self, **kwargs):
|
|
68
|
+
self.old = {}
|
|
69
|
+
for k, v in kwargs.items():
|
|
70
|
+
if k in _VALIDATORS and not _VALIDATORS[k](v):
|
|
71
|
+
raise ValueError(f'Config parameter "{k}" has invalid value: "{v}"')
|
|
72
|
+
if k not in CONFIG:
|
|
73
|
+
raise KeyError(f'Not a configuration key: "{k}"')
|
|
74
|
+
self.old[k] = CONFIG[k]
|
|
75
|
+
if k == 'ureg_format':
|
|
76
|
+
get_ureg().formatter.default_format = str(v)
|
|
77
|
+
self._update(kwargs)
|
|
78
|
+
|
|
79
|
+
def __enter__(self):
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
def __exit__(self, *args, **kwargs):
|
|
83
|
+
self._update(self.old)
|
|
84
|
+
|
|
85
|
+
def _update(self, options_dict: Dict):
|
|
86
|
+
CONFIG.update(options_dict)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# set_config = ConfigSetter()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_config(key=None):
|
|
93
|
+
"""Return the configuration parameters."""
|
|
94
|
+
if key is None:
|
|
95
|
+
return CONFIG
|
|
96
|
+
return CONFIG[key]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_ureg() -> UnitRegistry:
|
|
100
|
+
"""get unit registry"""
|
|
101
|
+
return ureg
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
set_config(ureg_format=CONFIG['ureg_format'])
|