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.
Files changed (144) hide show
  1. h5rdmtoolbox/__init__.py +424 -0
  2. h5rdmtoolbox/_cfg.py +104 -0
  3. h5rdmtoolbox/_repr.py +747 -0
  4. h5rdmtoolbox/_version.py +6 -0
  5. h5rdmtoolbox/cli.py +61 -0
  6. h5rdmtoolbox/consts.py +3 -0
  7. h5rdmtoolbox/convention/__init__.py +41 -0
  8. h5rdmtoolbox/convention/cfg.py +2 -0
  9. h5rdmtoolbox/convention/consts.py +28 -0
  10. h5rdmtoolbox/convention/core.py +817 -0
  11. h5rdmtoolbox/convention/errors.py +34 -0
  12. h5rdmtoolbox/convention/generate.py +385 -0
  13. h5rdmtoolbox/convention/ontology/__init__.py +3 -0
  14. h5rdmtoolbox/convention/ontology/h5ontocls.py +133 -0
  15. h5rdmtoolbox/convention/ontology/hdf_datatypes.py +64 -0
  16. h5rdmtoolbox/convention/references.py +23 -0
  17. h5rdmtoolbox/convention/standard_attributes.py +383 -0
  18. h5rdmtoolbox/convention/toolbox_validators.py +175 -0
  19. h5rdmtoolbox/convention/utils.py +216 -0
  20. h5rdmtoolbox/data/def_icon.png +0 -0
  21. h5rdmtoolbox/data/fluid-v1.yml +36 -0
  22. h5rdmtoolbox/data/h5tbx.yaml +28 -0
  23. h5rdmtoolbox/data/id_icon.png +0 -0
  24. h5rdmtoolbox/data/iri_icon.png +0 -0
  25. h5rdmtoolbox/data/ld_icon.png +0 -0
  26. h5rdmtoolbox/data/piv-v1.yml +35 -0
  27. h5rdmtoolbox/data/style.css +216 -0
  28. h5rdmtoolbox/data/tutorial_convention.yaml +91 -0
  29. h5rdmtoolbox/data/tutorial_standard_name_table.yaml +390 -0
  30. h5rdmtoolbox/data/type_icon.png +0 -0
  31. h5rdmtoolbox/database/__init__.py +75 -0
  32. h5rdmtoolbox/database/hdfdb/__init__.py +4 -0
  33. h5rdmtoolbox/database/hdfdb/filedb.py +129 -0
  34. h5rdmtoolbox/database/hdfdb/objdb.py +520 -0
  35. h5rdmtoolbox/database/hdfdb/query.py +133 -0
  36. h5rdmtoolbox/database/hdfdb/utils.py +51 -0
  37. h5rdmtoolbox/database/interface.py +59 -0
  38. h5rdmtoolbox/database/lazy.py +183 -0
  39. h5rdmtoolbox/database/mongo.py +374 -0
  40. h5rdmtoolbox/errors.py +4 -0
  41. h5rdmtoolbox/extensions/__init__.py +0 -0
  42. h5rdmtoolbox/extensions/magnitude.py +67 -0
  43. h5rdmtoolbox/extensions/normalize.py +103 -0
  44. h5rdmtoolbox/extensions/onto.py +52 -0
  45. h5rdmtoolbox/extensions/units.py +48 -0
  46. h5rdmtoolbox/extensions/vector.py +138 -0
  47. h5rdmtoolbox/gui/__init__.py +0 -0
  48. h5rdmtoolbox/gui/fairify.py +446 -0
  49. h5rdmtoolbox/gui/src/__init__.py +0 -0
  50. h5rdmtoolbox/gui/src/main.py +131 -0
  51. h5rdmtoolbox/identifiers.py +257 -0
  52. h5rdmtoolbox/layout/__init__.py +4 -0
  53. h5rdmtoolbox/layout/core.py +624 -0
  54. h5rdmtoolbox/ld/__init__.py +129 -0
  55. h5rdmtoolbox/ld/globals.py +3 -0
  56. h5rdmtoolbox/ld/hdf/__init__.py +0 -0
  57. h5rdmtoolbox/ld/hdf/attributes.py +36 -0
  58. h5rdmtoolbox/ld/hdf/datasets.py +160 -0
  59. h5rdmtoolbox/ld/hdf/file.py +54 -0
  60. h5rdmtoolbox/ld/hdf/groups.py +47 -0
  61. h5rdmtoolbox/ld/rdf.py +883 -0
  62. h5rdmtoolbox/ld/shacl.py +221 -0
  63. h5rdmtoolbox/ld/user/__init__.py +0 -0
  64. h5rdmtoolbox/ld/user/attributes.py +241 -0
  65. h5rdmtoolbox/ld/user/datasets.py +22 -0
  66. h5rdmtoolbox/ld/user/file.py +58 -0
  67. h5rdmtoolbox/ld/user/groups.py +58 -0
  68. h5rdmtoolbox/ld/user/utils.py +8 -0
  69. h5rdmtoolbox/ld/utils.py +133 -0
  70. h5rdmtoolbox/plotting.py +168 -0
  71. h5rdmtoolbox/protected_attributes.py +9 -0
  72. h5rdmtoolbox/protocols.py +268 -0
  73. h5rdmtoolbox/repository/__init__.py +11 -0
  74. h5rdmtoolbox/repository/default_metamapper.py +55 -0
  75. h5rdmtoolbox/repository/interface.py +214 -0
  76. h5rdmtoolbox/repository/utils.py +131 -0
  77. h5rdmtoolbox/repository/zenodo/__init__.py +5 -0
  78. h5rdmtoolbox/repository/zenodo/core.py +616 -0
  79. h5rdmtoolbox/repository/zenodo/metadata.py +276 -0
  80. h5rdmtoolbox/repository/zenodo/tokens.py +118 -0
  81. h5rdmtoolbox/repository/zenodo/utils.py +16 -0
  82. h5rdmtoolbox/tutorial.py +374 -0
  83. h5rdmtoolbox/user.py +158 -0
  84. h5rdmtoolbox/utils.py +743 -0
  85. h5rdmtoolbox/wrapper/__init__.py +8 -0
  86. h5rdmtoolbox/wrapper/accessor.py +97 -0
  87. h5rdmtoolbox/wrapper/accessors/__init__.py +0 -0
  88. h5rdmtoolbox/wrapper/accessors/provenance.py +36 -0
  89. h5rdmtoolbox/wrapper/core.py +2528 -0
  90. h5rdmtoolbox/wrapper/ds_decoder.py +113 -0
  91. h5rdmtoolbox/wrapper/h5attr.py +519 -0
  92. h5rdmtoolbox/wrapper/h5ext.py +20 -0
  93. h5rdmtoolbox/wrapper/h5utils.py +32 -0
  94. h5rdmtoolbox/wrapper/h5yaml.py +116 -0
  95. h5rdmtoolbox/wrapper/jsonld.py +1189 -0
  96. h5rdmtoolbox/wrapper/xr2hdf.py +106 -0
  97. h5rdmtoolbox-2.5.3.dist-info/METADATA +379 -0
  98. h5rdmtoolbox-2.5.3.dist-info/RECORD +144 -0
  99. h5rdmtoolbox-2.5.3.dist-info/WHEEL +5 -0
  100. h5rdmtoolbox-2.5.3.dist-info/entry_points.txt +2 -0
  101. h5rdmtoolbox-2.5.3.dist-info/licenses/LICENSE +20 -0
  102. h5rdmtoolbox-2.5.3.dist-info/top_level.txt +2 -0
  103. tests/__init__.py +0 -0
  104. tests/clean_zenodo_sandbox.py +23 -0
  105. tests/conventions/__init__.py +0 -0
  106. tests/conventions/test_convention_vfuncs.py +98 -0
  107. tests/conventions/test_conventions.py +697 -0
  108. tests/conventions/test_engmeta.py +55 -0
  109. tests/conventions/test_references.py +22 -0
  110. tests/conventions/test_toolbox_validators.py +138 -0
  111. tests/conventions/test_utils.py +94 -0
  112. tests/database/__init__.py +0 -0
  113. tests/database/test_find_rdf.py +36 -0
  114. tests/database/test_hdfDB.py +619 -0
  115. tests/database/test_mongo.py +229 -0
  116. tests/layouts/__init__.py +0 -0
  117. tests/layouts/test_core.py +362 -0
  118. tests/layouts/test_docs.py +66 -0
  119. tests/ld/__init__.py +0 -0
  120. tests/ld/test_ld.py +904 -0
  121. tests/ld/test_rdf.py +688 -0
  122. tests/ld/test_shacl.py +223 -0
  123. tests/ld/test_user_graph.py +278 -0
  124. tests/ld/test_utils.py +13 -0
  125. tests/repository/__init__.py +0 -0
  126. tests/repository/test_zenodo.py +728 -0
  127. tests/test_cli.py +69 -0
  128. tests/test_config.py +80 -0
  129. tests/test_extensions.py +148 -0
  130. tests/test_identifiers.py +85 -0
  131. tests/test_plotting.py +155 -0
  132. tests/test_repr.py +114 -0
  133. tests/test_user.py +100 -0
  134. tests/test_utils.py +143 -0
  135. tests/test_version.py +72 -0
  136. tests/test_xarray_export.py +47 -0
  137. tests/wrapper/__init__.py +0 -0
  138. tests/wrapper/test_core.py +994 -0
  139. tests/wrapper/test_dump.py +77 -0
  140. tests/wrapper/test_file.py +411 -0
  141. tests/wrapper/test_h5ext.py +22 -0
  142. tests/wrapper/test_lazy.py +62 -0
  143. tests/wrapper/test_namespaces.py +46 -0
  144. tests/wrapper/test_xr2df.py +41 -0
h5rdmtoolbox/_repr.py ADDED
@@ -0,0 +1,747 @@
1
+ import json
2
+ import os
3
+ import re
4
+ import typing
5
+ import warnings
6
+ from abc import abstractmethod
7
+ from time import perf_counter_ns
8
+
9
+ import h5py
10
+ import importlib_resources
11
+ import numpy as np
12
+ import xarray as xr
13
+ from IPython.display import HTML, display
14
+ from numpy import ndarray
15
+ from ontolutils import M4I
16
+
17
+ from h5rdmtoolbox.ld.rdf import (RDF_PREDICATE_ATTR_NAME,
18
+ RDF_OBJECT_ATTR_NAME,
19
+ RDF_TYPE_ATTR_NAME)
20
+ from . import get_config, identifiers, protected_attributes
21
+
22
+ H5PY_SPECIAL_ATTRIBUTES = ('DIMENSION_LIST', 'REFERENCE_LIST', 'NAME', 'CLASS', protected_attributes.COORDINATES)
23
+ try:
24
+ CSS_STR = importlib_resources.files('h5rdmtoolbox').joinpath('data/style.css').read_bytes().decode("utf8")
25
+ except FileNotFoundError:
26
+ import pathlib
27
+
28
+ with open(pathlib.Path(__file__).parent / 'data/style.css') as f:
29
+ CSS_STR = f.read().rstrip()
30
+
31
+ # IRI_ICON = importlib_resources.files('h5rdmtoolbox').joinpath('data/iri_icon.png')
32
+ # if IRI_ICON.exists():
33
+ # IRI_ICON = rf'file:///{IRI_ICON}'
34
+ # else:
35
+ DEF_ICON = "https://github.com/matthiasprobst/h5RDMtoolbox/blob/dev/h5rdmtoolbox/data/def_icon.png?raw=true"
36
+ IRI_ICON = "https://github.com/matthiasprobst/h5RDMtoolbox/blob/dev/h5rdmtoolbox/data/iri_icon.png?raw=true"
37
+ LD_ICON = "https://github.com/matthiasprobst/h5RDMtoolbox/blob/dev/h5rdmtoolbox/data/ld_icon.png?raw=true"
38
+ ID_ICON = "https://github.com/matthiasprobst/h5RDMtoolbox/blob/dev/h5rdmtoolbox/data/id_icon.png?raw=true"
39
+ TYPE_ICON = "https://github.com/matthiasprobst/h5RDMtoolbox/blob/dev/h5rdmtoolbox/data/type_icon.png?raw=true"
40
+
41
+ hasUnitIRI = str(M4I.hasUnit)
42
+ """
43
+ disclaimer:
44
+
45
+ dropdown _html representation realized with "h5file_html_repr"
46
+ is inspired and mostly taken from:
47
+ https://jsfiddle.net/tay08cn9/4/ (xarray package)
48
+
49
+ """
50
+
51
+
52
+ class BColors:
53
+ """Color class to color text"""
54
+ HEADER = '\033[95m'
55
+ OKBLUE = '\033[94m'
56
+ OKCYAN = '\033[96m'
57
+ OKGREEN = '\033[92m'
58
+ WARNING = '\033[93m'
59
+ FAIL = '\033[91m'
60
+ ENDC = '\033[0m'
61
+ BOLD = '\033[1m'
62
+ UNDERLINE = '\033[4m'
63
+ ITALIC = '\033[3m'
64
+
65
+
66
+ def make_italic(string):
67
+ """make string italic"""
68
+ return f"{BColors.ITALIC}{string}{BColors.ENDC}"
69
+
70
+
71
+ def make_bold(string):
72
+ """make string bold"""
73
+ return f"{BColors.BOLD}{string}{BColors.ENDC}"
74
+
75
+
76
+ def warningtext(string):
77
+ """make string orange"""
78
+ return f"{BColors.WARNING}{string}{BColors.ENDC}"
79
+
80
+
81
+ def failtext(string):
82
+ """make string red"""
83
+ return f"{BColors.FAIL}{string}{BColors.ENDC}"
84
+
85
+
86
+ def failprint(string):
87
+ """print string in red"""
88
+ print(failtext(string))
89
+
90
+
91
+ def oktext(string):
92
+ """make string green"""
93
+ return f"{BColors.OKGREEN}{string}{BColors.ENDC}"
94
+
95
+
96
+ def okprint(string):
97
+ """print string in red"""
98
+ print(oktext(string))
99
+
100
+
101
+ def make_href(url: str, text: str) -> str:
102
+ """Builds HTML hyperlink from url
103
+
104
+ Parameters
105
+ ----------
106
+ url: str
107
+ link destination
108
+ text: str
109
+ display text
110
+
111
+ Returns
112
+ -------
113
+ The HTML <a> tag string
114
+ """
115
+ if not url.startswith('http'):
116
+ raise ValueError(f'Invalid URL: "{url}". Must start with "http"')
117
+ return f'<a href="{url}">{text}</a>'
118
+
119
+
120
+ def process_string_for_link(string: str) -> typing.Tuple[str, bool]:
121
+ """process string to make links actually clickable in html
122
+
123
+ Parameters
124
+ ----------
125
+ string: str
126
+ string to process
127
+
128
+ Returns
129
+ -------
130
+ str
131
+ processed string
132
+ bool
133
+ True if string actually contains a link
134
+
135
+ """
136
+ if 'zenodo.' in string:
137
+ if re.match(r'10\.\d{4,9}/zenodo\.\d{4,9}', string):
138
+ zenodo_url = f'https://doi.org/{string}'
139
+ img_url = f'https://zenodo.org/badge/DOI/{string}.svg'
140
+ elif string.startswith('https://zenodo.org/record/'):
141
+ zenodo_url = string
142
+ img_url = f'https://zenodo.org/badge/DOI/10.5281/zenodo.{string.split("/")[-1]}.svg'
143
+ elif string.startswith('https://zenodo.org/records/'):
144
+ rec_id = string.split('/')[4]
145
+ zenodo_url = f'https://doi.org/10.5281/zenodo.{rec_id}'
146
+ img_url = f'https://zenodo.org/badge/DOI/10.5281/zenodo.{rec_id}.svg'
147
+ else:
148
+ zenodo_url = string
149
+ img_url = ''
150
+ return make_href(url=zenodo_url, text=f'<img src="{img_url}" alt="DOI">'), True
151
+ if string.startswith(("https://", "http://", "ftp://", "www.")):
152
+ # return make_href(url=string, text=string), True
153
+ # for p in (r"(https?://\S+)", r"(ftp://\S+)", r"(www\.\S+)"):
154
+ # urls = re.findall(p, string)
155
+ # if urls:
156
+ # print(string, urls)
157
+ url = string
158
+ identifier = identifiers.from_url(url)
159
+ if identifier:
160
+ orcid_url_repr = identifier._repr_html_()
161
+ string = string.replace(url, orcid_url_repr)
162
+ else:
163
+ string = string.replace(url, make_href(url, url))
164
+ return string, True
165
+
166
+ return string, False
167
+
168
+
169
+ def get_iri_icon_href(iri: str,
170
+ icon_url: str,
171
+ tooltiptext=None) -> str:
172
+ """get html representation of an IRI with icon. The URL is shown as a tooltip"""
173
+ # if isinstance(iri, dict):
174
+ # thing = Thing.from_jsonld(data=iri, limit=1)
175
+ # _type = iri.get(RDF_TYPE_ATTR_NAME, None)
176
+ # thing.pop_blank_node_id()
177
+ # thing_str = thing.__str__(limit=50)
178
+ # if _type:
179
+ # _thing_str = thing.__str__(limit=50).split('(', 1)[1]
180
+ # thing_str = f'{_type}({_thing_str}'
181
+ # return f'<div class="tooltip">' \
182
+ # f'<img class="size_of_img" src="{icon_url}" alt="IRI_ICON" width="16" height="16" />' \
183
+ # f'<span class="tooltiptext">{thing_str}</span></div>'
184
+ if isinstance(iri, dict):
185
+ return f'<a href="#" target="_blank" class="tooltip"> ' \
186
+ f'<img class="size_of_img" src="{LD_ICON}" alt="LD_ICON" width="16" height="16" />' \
187
+ f' <span class="tooltiptext"><p>{json.dumps(iri, indent=2)}</p></span></a>'
188
+ return f'<a href="{iri}" target="_blank" class="tooltip"> ' \
189
+ f'<img class="size_of_img" src="{icon_url}" alt="IRI_ICON" width="16" height="16" />' \
190
+ f' <span class="tooltiptext">{tooltiptext or iri}</span></a>'
191
+
192
+
193
+ def get_def_icon_href(def_text: str) -> str:
194
+ """get html representation of an attribute definition with icon"""
195
+ return f'<div class="tooltip">' \
196
+ f'<img class="size_of_img" src="{DEF_ICON}" alt="D" width="16" height="16" />' \
197
+ f'<span class="tooltiptext">{def_text}</span></div>'
198
+
199
+
200
+ class _HDF5StructureRepr:
201
+
202
+ def __init__(self, ignore_attrs=None, hide_uri: bool = False):
203
+ self.base_intent = ' '
204
+ self.max_attr_length = 100
205
+ self.collapsed = None
206
+ self.hide_uri = hide_uri
207
+
208
+ self._obj_cfg = {}
209
+ if ignore_attrs is None:
210
+ self.ignore_attrs = H5PY_SPECIAL_ATTRIBUTES
211
+ else:
212
+ self.ignore_attrs = ignore_attrs
213
+
214
+ @property
215
+ def checkbox_state(self) -> str:
216
+ """return 'checked' if the group is collapsed, else ''"""
217
+ return '' if self.collapsed else 'checked'
218
+
219
+ def __dataset__(self, name, h5obj) -> str:
220
+ """overwrite the H5Repr parent method"""
221
+ if h5obj.dtype.char == 'S':
222
+ # handel string datasets:
223
+ return self.__stringdataset__(name, h5obj)
224
+ if h5obj.ndim == 0:
225
+ return self.__0Ddataset__(name, h5obj)
226
+ return self.__NDdataset__(name, h5obj)
227
+
228
+ @abstractmethod
229
+ def __stringdataset__(self, name, h5obj):
230
+ """dataset representation"""
231
+
232
+ @abstractmethod
233
+ def __0Ddataset__(self, name, h5obj):
234
+ """dataset representation"""
235
+
236
+ @abstractmethod
237
+ def __NDdataset__(self, name, h5obj):
238
+ """dataset representation"""
239
+
240
+ @abstractmethod
241
+ def __group__(self, name, h5obj):
242
+ """dataset representation"""
243
+
244
+ @abstractmethod
245
+ def __attrs__(self, name, h5obj):
246
+ """dataset representation"""
247
+
248
+
249
+ class HDF5StructureStrRepr(_HDF5StructureRepr):
250
+
251
+ def __call__(self, group, indent=0, preamble=None, hide_uri: bool = False):
252
+ self.hide_uri = hide_uri
253
+ if preamble:
254
+ print(preamble)
255
+ spaces = self.base_intent * indent
256
+ predicate = group.rdf.predicate.get('SELF', None)
257
+ if predicate:
258
+ print(spaces + f'@predicate: {predicate}')
259
+ for attr_name in group.attrs.raw.keys():
260
+ if attr_name == RDF_TYPE_ATTR_NAME:
261
+ print(spaces + f'@type: {group.attrs[attr_name]}')
262
+ else:
263
+ if not attr_name.isupper():
264
+ print(spaces + self.__attrs__(attr_name, group))
265
+ for key, item in group.items():
266
+ if isinstance(item, h5py.Dataset):
267
+ print(spaces + self.__dataset__(key, item))
268
+ for attr_name in item.attrs.raw.keys():
269
+ if not attr_name.isupper() and attr_name not in self.ignore_attrs:
270
+ print(self.base_intent * (indent + 2) + self.__attrs__(attr_name, item))
271
+ elif isinstance(item, h5py.Group):
272
+ print(spaces + self.__group__(key, item))
273
+ self(item, indent + 1, hide_uri=hide_uri)
274
+
275
+ def __dataset__(self, name: str, h5obj: h5py.Dataset) -> str:
276
+ if h5obj.dtype.char == 'S':
277
+ # handel string datasets:
278
+ return self.__stringdataset__(name, h5obj)
279
+ if h5obj.ndim == 0:
280
+ return self.__0Ddataset__(name, h5obj)
281
+ return self.__NDdataset__(name, h5obj)
282
+
283
+ def __stringdataset__(self, name: str, h5obj: h5py.Dataset) -> str:
284
+ """string representation of a string dataset"""
285
+ if h5obj.ndim == 0:
286
+ return f"\033[1m{name}\033[0m: {h5obj.values[()]}"
287
+ return f"\033[1m{name}\033[0m: {h5obj.shape}, dtype: {h5obj.dtype}"
288
+
289
+ def __0Ddataset__(self, name: str, h5obj: h5py.Dataset) -> str:
290
+ """string representation of a 0D dataset"""
291
+ value = h5obj.values[()]
292
+ if isinstance(value, (float, np.floating)):
293
+ value = f'{value:f}'
294
+ elif isinstance(value, (int, np.integer)):
295
+ value = f'{int(value):d}'
296
+ elif isinstance(value, (bool, np.bool_)):
297
+ value = f'{value}'
298
+ else:
299
+ warnings.warn(f'Unexpected type {type(value)}', UserWarning)
300
+ value = '?type?'
301
+ return f"\033[1m{name}\033[0m {value}, dtype: {h5obj.dtype}"
302
+
303
+ def __NDdataset__(self, name, h5obj: h5py.Dataset):
304
+ """string representation of a ND dataset"""
305
+ return f"\033[1m{name}\033[0m: {h5obj.shape}, dtype: {h5obj.dtype}"
306
+
307
+ def __group__(self, name, item) -> str:
308
+ return f"/\033[1m{name}\033[0m"
309
+
310
+ def __attrs__(self, name, h5obj) -> str:
311
+ attr_value = h5obj.attrs.raw[name]
312
+
313
+ if self.hide_uri:
314
+ use_attr_name = name
315
+ else:
316
+ pred = h5obj.rdf[name].get(RDF_PREDICATE_ATTR_NAME, None)
317
+ attrsdef = h5obj.rdf[name].definition
318
+ if pred and attrsdef:
319
+ use_attr_name = f'{name} (p={pred}, def={attrsdef})'
320
+ elif pred:
321
+ use_attr_name = f'{name} (p={pred})'
322
+ elif attrsdef:
323
+ use_attr_name = f'{name} (def={attrsdef})'
324
+ else:
325
+ use_attr_name = name
326
+
327
+ obj_iri = h5obj.rdf[name].get(RDF_OBJECT_ATTR_NAME, None)
328
+ if obj_iri:
329
+ attr_value = f'{attr_value} (o={obj_iri})'
330
+
331
+ # if isinstance(attr_value, h5py.Group):
332
+ # attr_value = f'grp:{attr_value.name}'
333
+ # elif isinstance(attr_value, h5py.Dataset):
334
+ # attr_value = f'dset:{attr_value.name}'
335
+ return f'\033[3ma: {use_attr_name}\033[0m: {attr_value}'
336
+
337
+
338
+ class HDF5StructureHTMLRepr(_HDF5StructureRepr):
339
+
340
+ def __call__(self,
341
+ group,
342
+ collapsed: bool = True,
343
+ preamble: str = None,
344
+ indent: int = 0,
345
+ chunks: bool = False,
346
+ maxshape: bool = False,
347
+ hide_uri: bool = False):
348
+ self.hide_uri = hide_uri
349
+ if isinstance(group, h5py.Group):
350
+ h5group = group
351
+ else:
352
+ h5group = group['/']
353
+
354
+ self.collapsed = collapsed
355
+
356
+ self._obj_cfg.update({'chunks': chunks,
357
+ 'maxshape': maxshape})
358
+
359
+ _id = h5group.name + perf_counter_ns().__str__()
360
+
361
+ _html = f'<head><style>{CSS_STR}</style></head>'
362
+ if preamble:
363
+ _html += f'\n{preamble}\n'
364
+ _html += "\n<div class='h5tb-warp'>"
365
+ _html += self.__group__(h5group.name.rsplit('/', 1)[1], h5group)
366
+ _html += "\n</div>"
367
+ return _html
368
+
369
+ def __stringdataset__(self, name, h5obj) -> str:
370
+ if h5obj.ndim == 0:
371
+ _pcns = perf_counter_ns().__str__()
372
+ _id1 = f'ds-1-{h5obj.name}-{_pcns}1'
373
+ _id2 = f'ds-2-{h5obj.name}-{_pcns}2'
374
+ return f"""\n
375
+ <ul id="{_id1}" class="h5tb-var-list">
376
+ <input id="{_id2}" class="h5tb-varname-in" type="checkbox" {self.checkbox_state}>
377
+ <label class='h5tb-varname'
378
+ for="{_id2}">{name}</label>: [{h5obj.dtype}] data={h5obj.values[()]}
379
+ """
380
+ elif h5obj.ndim == 1:
381
+ _pcns = perf_counter_ns().__str__()
382
+ _id1 = f'ds-1-{h5obj.name}-{_pcns}1'
383
+ _id2 = f'ds-2-{h5obj.name}-{_pcns}2'
384
+ _strdata = h5obj[()]
385
+ if isinstance(_strdata, xr.DataArray):
386
+ return f"""\n
387
+ <ul id="{_id1}" class="h5tb-var-list">
388
+ <input id="{_id2}" class="h5tb-varname-in" type="checkbox" {self.checkbox_state}>
389
+ <label class='h5tb-varname'
390
+ for="{_id2}">{name}</label>: [{h5obj.dtype}]
391
+ """
392
+ try:
393
+ str_values = ', '.join(_strdata)
394
+ except UnicodeDecodeError:
395
+ str_values = '<i>UnicodeDecodeError</i>'
396
+ except TypeError:
397
+ str_values = f'<i>TypeError: {type(_strdata)}</i>'
398
+ return f"""\n
399
+ <ul id="{_id1}" class="h5tb-var-list">
400
+ <input id="{_id2}" class="h5tb-varname-in" type="checkbox" {self.checkbox_state}>
401
+ <label class='h5tb-varname'
402
+ for="{_id2}">{name}</label>: [{h5obj.dtype}] data="{str_values}"
403
+ """
404
+ return self.__NDdataset__(name, h5obj)
405
+
406
+ def __0Ddataset__(self, name: str, h5obj: h5py.Dataset) -> str:
407
+ _id1 = f'ds-1-{h5obj.name}-{perf_counter_ns().__str__()}'
408
+ _id2 = f'ds-2-{h5obj.name}-{perf_counter_ns().__str__()}'
409
+ # figure out unit attribute. It may be unit or defined in IRI
410
+ units = h5obj.attrs.get('units', None)
411
+ if units is None:
412
+ units = ''
413
+ pred_dict = h5obj.attrs.get(RDF_PREDICATE_ATTR_NAME, None)
414
+ if pred_dict:
415
+ for k, v in pred_dict.items():
416
+ if v == hasUnitIRI:
417
+ units = h5obj.attrs[k].rsplit('/', 1)[-1]
418
+ if h5obj.dtype.char != 'O':
419
+ data_values = h5obj.values[()]
420
+ else:
421
+ data_values = ''
422
+ dtype = h5obj.dtype
423
+ if units:
424
+ units_str = f"[{units}]"
425
+ else:
426
+ units_str = ""
427
+ _html = f"""\n
428
+ <ul id="{_id1}" class="h5tb-var-list">
429
+ <input id="{_id2}" class="h5tb-varname-in" type="checkbox" {self.checkbox_state}>
430
+ <label class='h5tb-varname' for="{_id2}">{name}</label>
431
+ <span class="h5tb-dims">{data_values} {units_str} [{dtype}]</span>"""
432
+ return _html
433
+
434
+ def __NDdataset__(self, name, h5obj: h5py.Dataset):
435
+ ds_dirname = os.path.dirname(h5obj.name)
436
+ _shape = h5obj.shape
437
+ if get_config('advanced_shape_repr'):
438
+ _shape_repr = '('
439
+ ndim = h5obj.ndim
440
+ for i in range(ndim):
441
+ orig_dim_name = None
442
+ try:
443
+ orig_dim_name = h5obj.dims[i][0].name
444
+ except RuntimeError:
445
+ pass # no dimension scale could be found
446
+ if orig_dim_name:
447
+ if os.path.dirname(orig_dim_name) == ds_dirname:
448
+ dim_name = os.path.basename(orig_dim_name)
449
+ else:
450
+ dim_name = orig_dim_name
451
+ if i == 0:
452
+ _shape_repr += f'{dim_name}: {_shape[i]}'
453
+ else:
454
+ _shape_repr += f', {dim_name}: {_shape[i]}'
455
+ else:
456
+ if i == 0:
457
+ _shape_repr += f'{_shape[i]}'
458
+ else:
459
+ _shape_repr += f', {_shape[i]}'
460
+ _shape_repr += ')'
461
+ if _shape_repr == '()' and ndim > 0:
462
+ _shape_repr = _shape
463
+ else:
464
+ _shape_repr = _shape
465
+
466
+ if self._obj_cfg['chunks']:
467
+ chunks_str = f' chunks={h5obj.chunks}'
468
+ else:
469
+ chunks_str = ''
470
+
471
+ if self._obj_cfg['maxshape']:
472
+ maxshape_str = f' maxshape={h5obj.maxshape}'
473
+ else:
474
+ maxshape_str = ''
475
+
476
+ _id1 = f'ds-1-{h5obj.name}-{perf_counter_ns().__str__()}'
477
+ _id2 = f'ds-2-{h5obj.name}-{perf_counter_ns().__str__()}'
478
+
479
+ _html = f"""\n
480
+ <ul id="{_id1}" class="h5tb-var-list">
481
+ <input id="{_id2}" class="h5tb-varname-in" type="checkbox" {self.checkbox_state}>
482
+ <label class='h5tb-varname' for="{_id2}">{name}</label>
483
+ <span class="h5tb-dims">{_shape_repr} [{h5obj.dtype}]{chunks_str}{maxshape_str}</span>"""
484
+ return _html
485
+
486
+ def __dataset__(self, name, h5obj) -> str:
487
+ """generate html representation of a dataset"""
488
+ self_predicate = h5obj.rdf.predicate.get('SELF', None)
489
+ self_type = h5obj.rdf.type
490
+ self_ID = h5obj.rdf.subject
491
+ _dsname = name
492
+
493
+ if self_type is not None:
494
+ _dsname += get_iri_icon_href(
495
+ self_type, icon_url=TYPE_ICON,
496
+ tooltiptext=f'@type={self_type}')
497
+
498
+ if self_ID is not None:
499
+ _dsname += get_iri_icon_href(
500
+ self_ID, icon_url=ID_ICON,
501
+ tooltiptext=f'@id={self_ID}')
502
+
503
+ if self_predicate is not None:
504
+ _dsname += get_iri_icon_href(self_predicate, icon_url=IRI_ICON)
505
+
506
+ is_string_dataset = h5obj.dtype.char == 'S'
507
+ if is_string_dataset:
508
+ _html_pre = self.__stringdataset__(_dsname, h5obj)
509
+ else:
510
+ if h5obj.ndim == 0:
511
+ _html_pre = self.__0Ddataset__(_dsname, h5obj)
512
+ else:
513
+ _html_pre = self.__NDdataset__(_dsname, h5obj)
514
+
515
+ # now all attributes of the dataset:
516
+ # open attribute section:
517
+ _html_ds_attrs = """\n <ul class="h5tb-attr-list">"""
518
+
519
+ for k in h5obj.attrs.keys():
520
+ if k not in self.ignore_attrs and not k.isupper():
521
+ _html_ds_attrs += self.__attrs__(k, h5obj)
522
+
523
+ # close attribute section
524
+ _html_ds_attrs += """\n </ul>"""
525
+
526
+ # close dataset section
527
+ _html_post = """\n </ul>"""
528
+ _html_ds = _html_pre + _html_ds_attrs + _html_post
529
+ return _html_ds
530
+
531
+ def __group__(self, name, h5obj: "h5tbx.Group") -> str:
532
+ nkeys = len(h5obj.keys())
533
+ _id = f'ds-{name}-{perf_counter_ns().__str__()}'
534
+ _groupname = os.path.basename(h5obj.name)
535
+
536
+ if _groupname == '':
537
+ _groupname = '/' # recover root name
538
+ checkbox_state = 'checked'
539
+ else:
540
+ checkbox_state = self.checkbox_state
541
+
542
+ self_predicate = h5obj.rdf.predicate.get('SELF', None)
543
+ self_type = h5obj.rdf.type
544
+ self_ID = h5obj.rdf.subject
545
+
546
+ if self_type is not None:
547
+ _groupname += get_iri_icon_href(self_type, icon_url=TYPE_ICON,
548
+ tooltiptext=f'@type: {self_type}')
549
+
550
+ if self_ID is not None:
551
+ _groupname += get_iri_icon_href(self_ID, icon_url=ID_ICON,
552
+ tooltiptext=f'@id: {self_ID}')
553
+
554
+ if self_predicate is not None:
555
+ _groupname += get_iri_icon_href(self_predicate, icon_url=IRI_ICON)
556
+
557
+ _html = f"""\n
558
+ <ul style="list-style-type: none;" class="h5grp-sections">
559
+ <li>
560
+ <input id="group-{_id}" type="checkbox" {checkbox_state}>
561
+ <label style="font-weight: bold" for="group-{_id}">
562
+ {_groupname}<span>({nkeys})</span></label>
563
+ """
564
+
565
+ _html += """\n<ul class="h5tb-attr-list">"""
566
+ # write attributes:
567
+ for k in h5obj.attrs.keys():
568
+ if not k.isupper():
569
+ _html += self.__attrs__(k, h5obj)
570
+ # close attribute section
571
+ _html += """
572
+ </ul>"""
573
+
574
+ datasets = [(k, v) for k, v in h5obj.items() if isinstance(v, h5py.Dataset)]
575
+ groups = [(k, v) for k, v in h5obj.items() if isinstance(v, h5py.Group)]
576
+
577
+ for k, v in datasets:
578
+ _html += self.__dataset__(k, v)
579
+
580
+ for k, v in groups:
581
+ _html += self.__group__(k, v)
582
+ _html += '\n</li>'
583
+ _html += '\n</ul>'
584
+ return _html
585
+
586
+ def __attrs__(self, name, h5obj):
587
+ attr_value = h5obj.attrs.raw[name]
588
+ is_root_group = h5obj.name == "/"
589
+ try:
590
+ if isinstance(attr_value, np.bytes_):
591
+ try:
592
+ attr_value = attr_value.decode('utf-8')
593
+ except UnicodeDecodeError:
594
+ warnings.warn(f'Cannot decode attribute value for {name}', RuntimeWarning)
595
+
596
+ rdf = h5obj.rdf.get(name)
597
+
598
+ disp_name = name
599
+
600
+ rdf_predicate = rdf.predicate
601
+ if rdf_predicate is not None:
602
+ disp_name += get_iri_icon_href(rdf_predicate, icon_url=IRI_ICON)
603
+
604
+ if is_root_group:
605
+ frdf = h5obj.frdf.get(name)
606
+ frdf_predicate = frdf.predicate
607
+ if frdf_predicate is not None:
608
+ disp_name += get_iri_icon_href(frdf_predicate, icon_url=IRI_ICON)
609
+
610
+ attrs_def = h5obj.rdf[name].definition
611
+ if attrs_def is not None:
612
+ disp_name += get_def_icon_href(attrs_def)
613
+
614
+ rdf_object = rdf.object
615
+ if is_root_group:
616
+ frdf_object = frdf.object
617
+ else:
618
+ frdf_object = None
619
+
620
+
621
+ if isinstance(attr_value, ndarray):
622
+
623
+ if all(isinstance(item, str) for item in attr_value):
624
+ _string_value_list = []
625
+ for item in attr_value:
626
+ _value, is_url = process_string_for_link(item)
627
+ if is_url:
628
+ _string_value_list.append(_value)
629
+ else:
630
+ _string_value_list.append(item)
631
+ _value_str = ", ".join(_string_value_list)
632
+
633
+ if rdf_object is not None:
634
+ _value_str += get_iri_icon_href(rdf_object, icon_url=IRI_ICON)
635
+ if frdf_object is not None:
636
+ _value_str += get_iri_icon_href(frdf_object, icon_url=IRI_ICON)
637
+ return '<li style="list-style-type: none; ' \
638
+ f'font-style: italic">{disp_name}: {_value_str}</li>'
639
+ else:
640
+ _value = attr_value.__repr__()
641
+
642
+ if len(_value) > self.max_attr_length:
643
+ _value = f'{_value[0:self.max_attr_length]}...'
644
+
645
+ if rdf_object is not None:
646
+ _value += get_iri_icon_href(rdf_object, icon_url=IRI_ICON)
647
+ if frdf_object is not None:
648
+ _value += get_iri_icon_href(frdf_object, icon_url=IRI_ICON)
649
+
650
+ return f'<li style="list-style-type: none; font-style: italic">{disp_name}: {_value}</li>'
651
+
652
+ elif isinstance(attr_value, str):
653
+ _value_str = f'{attr_value}'
654
+ if len(_value_str) > 1:
655
+ if _value_str[0] == '<' and _value_str[-1] == '>':
656
+ _value_str = _value_str[1:-1]
657
+
658
+ # check if it is an identifier:
659
+ if _value_str.startswith('{'):
660
+ _value_html = _value_str
661
+ is_url = False
662
+ else:
663
+ identifier = identifiers.from_url(_value_str)
664
+ if identifier is not None:
665
+ _value_html = identifier._repr_html_()
666
+ is_url = True
667
+ else: # maybe some other url:
668
+ _value_html, is_url = process_string_for_link(_value_str)
669
+ # if is_url and not _value_html.startswith('{'):
670
+
671
+ # add rdf icon if available:
672
+ if rdf_object is not None:
673
+ _value_html += get_iri_icon_href(rdf_object, icon_url=IRI_ICON)
674
+ if frdf_object is not None:
675
+ _value_html += get_iri_icon_href(frdf_object, icon_url=IRI_ICON)
676
+
677
+ if is_url and not _value_html.startswith('{'): # TODO: why the second condition?
678
+ return f'<li style="list-style-type: none; font-style: italic">{disp_name}: {_value_html}</li>'
679
+ else:
680
+ if self.max_attr_length:
681
+ if len(_value_str) > self.max_attr_length:
682
+ _value_str = f'{_value_str[0:self.max_attr_length - 3]}...'
683
+ else:
684
+ _value_str = attr_value
685
+ else:
686
+ _value_str = attr_value
687
+ if rdf_object is not None:
688
+ _value_str += get_iri_icon_href(rdf_object, icon_url=IRI_ICON)
689
+ if frdf_object is not None:
690
+ _value_str += get_iri_icon_href(frdf_object, icon_url=IRI_ICON)
691
+ return f'<li style="list-style-type: none; font-style: italic">{disp_name}: {_value_str}</li>'
692
+
693
+ elif not isinstance(attr_value, ndarray):
694
+ if getattr(attr_value, '_repr_html_', None):
695
+ _value_str = attr_value._repr_html_()
696
+ else:
697
+ _value_str = str(attr_value)
698
+ if _value_str[0] == '<' and _value_str[-1] == '>':
699
+ _value_str = _value_str[1:-1]
700
+ if self.max_attr_length:
701
+ if len(_value_str) > self.max_attr_length:
702
+ _value_str = f'{_value_str[0:self.max_attr_length - 3]}...'
703
+ else:
704
+ _value_str = attr_value
705
+ else:
706
+ _value_str = attr_value
707
+ else:
708
+ _value_str = attr_value
709
+
710
+ if rdf_object is not None:
711
+ _value_str += get_iri_icon_href(rdf_object, icon_url=IRI_ICON)
712
+ if frdf_object is not None:
713
+ _value_str += get_iri_icon_href(frdf_object, icon_url=IRI_ICON)
714
+
715
+ return f'<li style="list-style-type: none; font-style: italic">{name}: {_value_str}</li>'
716
+
717
+ except Exception as e:
718
+ return f'<li style="list-style-type: none; font-style: italic">{name}: {attr_value}</li>'
719
+
720
+ class H5Repr:
721
+ """Class managing the sting/html output of HDF5 content"""
722
+
723
+ def __init__(self, str_repr: _HDF5StructureRepr = None, html_repr: _HDF5StructureRepr = None,
724
+ **kwargs):
725
+ if str_repr is None:
726
+ self.str_repr = HDF5StructureStrRepr(**kwargs)
727
+ else:
728
+ self.str_repr = str_repr
729
+
730
+ if html_repr is None:
731
+ self.html_repr = HDF5StructureHTMLRepr(**kwargs)
732
+ else:
733
+ self.html_repr = html_repr
734
+
735
+ def __html__(self, group, collapsed: bool = True, preamble: str = None,
736
+ chunks: bool = False, maxshape: bool = False) -> None:
737
+ display(
738
+ HTML(
739
+ self.html_repr(
740
+ group=group,
741
+ collapsed=collapsed,
742
+ preamble=preamble,
743
+ chunks=chunks,
744
+ maxshape=maxshape
745
+ )
746
+ )
747
+ )