dclab 0.67.0__cp314-cp314t-macosx_11_0_arm64.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.
Potentially problematic release.
This version of dclab might be problematic. Click here for more details.
- dclab/__init__.py +41 -0
- dclab/_version.py +34 -0
- dclab/cached.py +97 -0
- dclab/cli/__init__.py +10 -0
- dclab/cli/common.py +237 -0
- dclab/cli/task_compress.py +126 -0
- dclab/cli/task_condense.py +223 -0
- dclab/cli/task_join.py +229 -0
- dclab/cli/task_repack.py +98 -0
- dclab/cli/task_split.py +154 -0
- dclab/cli/task_tdms2rtdc.py +186 -0
- dclab/cli/task_verify_dataset.py +75 -0
- dclab/definitions/__init__.py +79 -0
- dclab/definitions/feat_const.py +202 -0
- dclab/definitions/feat_logic.py +182 -0
- dclab/definitions/meta_const.py +252 -0
- dclab/definitions/meta_logic.py +111 -0
- dclab/definitions/meta_parse.py +94 -0
- dclab/downsampling.cpython-314t-darwin.so +0 -0
- dclab/downsampling.pyx +230 -0
- dclab/external/__init__.py +4 -0
- dclab/external/packaging/LICENSE +3 -0
- dclab/external/packaging/LICENSE.APACHE +177 -0
- dclab/external/packaging/LICENSE.BSD +23 -0
- dclab/external/packaging/__init__.py +6 -0
- dclab/external/packaging/_structures.py +61 -0
- dclab/external/packaging/version.py +505 -0
- dclab/external/skimage/LICENSE +28 -0
- dclab/external/skimage/__init__.py +2 -0
- dclab/external/skimage/_find_contours.py +216 -0
- dclab/external/skimage/_find_contours_cy.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_find_contours_cy.pyx +188 -0
- dclab/external/skimage/_pnpoly.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_pnpoly.pyx +99 -0
- dclab/external/skimage/_shared/__init__.py +1 -0
- dclab/external/skimage/_shared/geometry.cpython-314t-darwin.so +0 -0
- dclab/external/skimage/_shared/geometry.pxd +6 -0
- dclab/external/skimage/_shared/geometry.pyx +55 -0
- dclab/external/skimage/measure.py +7 -0
- dclab/external/skimage/pnpoly.py +53 -0
- dclab/external/statsmodels/LICENSE +35 -0
- dclab/external/statsmodels/__init__.py +6 -0
- dclab/external/statsmodels/nonparametric/__init__.py +1 -0
- dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
- dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
- dclab/external/statsmodels/nonparametric/kernels.py +36 -0
- dclab/features/__init__.py +9 -0
- dclab/features/bright.py +81 -0
- dclab/features/bright_bc.py +93 -0
- dclab/features/bright_perc.py +63 -0
- dclab/features/contour.py +161 -0
- dclab/features/emodulus/__init__.py +339 -0
- dclab/features/emodulus/load.py +252 -0
- dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
- dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
- dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
- dclab/features/emodulus/pxcorr.py +135 -0
- dclab/features/emodulus/scale_linear.py +247 -0
- dclab/features/emodulus/viscosity.py +260 -0
- dclab/features/fl_crosstalk.py +95 -0
- dclab/features/inert_ratio.py +377 -0
- dclab/features/volume.py +242 -0
- dclab/http_utils.py +322 -0
- dclab/isoelastics/__init__.py +468 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
- dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
- dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
- dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
- dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
- dclab/kde/__init__.py +1 -0
- dclab/kde/base.py +459 -0
- dclab/kde/contours.py +222 -0
- dclab/kde/methods.py +313 -0
- dclab/kde_contours.py +10 -0
- dclab/kde_methods.py +11 -0
- dclab/lme4/__init__.py +5 -0
- dclab/lme4/lme4_template.R +94 -0
- dclab/lme4/rsetup.py +204 -0
- dclab/lme4/wrapr.py +386 -0
- dclab/polygon_filter.py +398 -0
- dclab/rtdc_dataset/__init__.py +15 -0
- dclab/rtdc_dataset/check.py +902 -0
- dclab/rtdc_dataset/config.py +533 -0
- dclab/rtdc_dataset/copier.py +353 -0
- dclab/rtdc_dataset/core.py +896 -0
- dclab/rtdc_dataset/export.py +867 -0
- dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
- dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
- dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
- dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
- dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
- dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
- dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
- dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
- dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
- dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
- dclab/rtdc_dataset/feat_basin.py +762 -0
- dclab/rtdc_dataset/feat_temp.py +102 -0
- dclab/rtdc_dataset/filter.py +263 -0
- dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
- dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
- dclab/rtdc_dataset/fmt_dcor/api.py +173 -0
- dclab/rtdc_dataset/fmt_dcor/base.py +299 -0
- dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
- dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
- dclab/rtdc_dataset/fmt_dcor/tables.py +66 -0
- dclab/rtdc_dataset/fmt_dict.py +103 -0
- dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
- dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
- dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
- dclab/rtdc_dataset/fmt_hdf5/events.py +276 -0
- dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
- dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
- dclab/rtdc_dataset/fmt_hdf5/tables.py +60 -0
- dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
- dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
- dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
- dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
- dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
- dclab/rtdc_dataset/fmt_http.py +102 -0
- dclab/rtdc_dataset/fmt_s3.py +354 -0
- dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
- dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
- dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
- dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
- dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
- dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
- dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
- dclab/rtdc_dataset/load.py +77 -0
- dclab/rtdc_dataset/meta_table.py +25 -0
- dclab/rtdc_dataset/writer.py +1019 -0
- dclab/statistics.py +226 -0
- dclab/util.py +176 -0
- dclab/warn.py +15 -0
- dclab-0.67.0.dist-info/METADATA +153 -0
- dclab-0.67.0.dist-info/RECORD +142 -0
- dclab-0.67.0.dist-info/WHEEL +6 -0
- dclab-0.67.0.dist-info/entry_points.txt +8 -0
- dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
- dclab-0.67.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""DCOR client interface"""
|
|
2
|
+
import logging
|
|
3
|
+
import pathlib
|
|
4
|
+
import re
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from ...util import hashobj
|
|
8
|
+
|
|
9
|
+
from ..config import Configuration
|
|
10
|
+
from ..core import RTDCBase
|
|
11
|
+
from ..feat_basin import PerishableRecord
|
|
12
|
+
|
|
13
|
+
from . import api
|
|
14
|
+
from .logs import DCORLogs
|
|
15
|
+
from .tables import DCORTables
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#: Append directories here where dclab should look for certificate bundles
|
|
22
|
+
#: for a specific host. The directory should contain files named after the
|
|
23
|
+
#: hostname, e.g. "dcor.mpl.mpg.de.cert".
|
|
24
|
+
DCOR_CERTS_SEARCH_PATHS = []
|
|
25
|
+
|
|
26
|
+
#: Regular expression for matching a DCOR resource URL
|
|
27
|
+
REGEXP_DCOR_URL = re.compile(
|
|
28
|
+
r"^(https?:\/\/)?" # scheme
|
|
29
|
+
r"([a-z0-9-\.]*\/?api\/3\/action\/dcserv\?id=)?" # host with API
|
|
30
|
+
r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$") # id
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RTDC_DCOR(RTDCBase):
|
|
34
|
+
def __init__(self, url, host="dcor.mpl.mpg.de", api_key="",
|
|
35
|
+
use_ssl=None, cert_path=None, dcserv_api_version=2,
|
|
36
|
+
*args, **kwargs):
|
|
37
|
+
"""Wrap around the DCOR API
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
url: str
|
|
42
|
+
Full URL or resource identifier; valid values are
|
|
43
|
+
|
|
44
|
+
- `<https://dcor.mpl.mpg.de/api/3/action/dcserv?id=
|
|
45
|
+
b1404eb5-f661-4920-be79-5ff4e85915d5>`_
|
|
46
|
+
- dcor.mpl.mpg.de/api/3/action/dcserv?id=b1404eb5-f
|
|
47
|
+
661-4920-be79-5ff4e85915d5
|
|
48
|
+
- b1404eb5-f661-4920-be79-5ff4e85915d5
|
|
49
|
+
host: str
|
|
50
|
+
The default host machine used if the host is not given in `url`
|
|
51
|
+
api_key: str
|
|
52
|
+
API key to access private resources
|
|
53
|
+
use_ssl: bool
|
|
54
|
+
Set this to False to disable SSL (should only be used for
|
|
55
|
+
testing). Defaults to None (does not force SSL if the URL
|
|
56
|
+
starts with "http://").
|
|
57
|
+
cert_path: pathlib.Path
|
|
58
|
+
The (optional) path to a server CA bundle; this should only
|
|
59
|
+
be necessary for DCOR instances in the intranet with a custom
|
|
60
|
+
CA or for certificate pinning.
|
|
61
|
+
dcserv_api_version: int
|
|
62
|
+
Version of the dcserv API to use. In version 0.13.2 of
|
|
63
|
+
ckanext-dc_serve, version 2 was introduced which entails
|
|
64
|
+
serving an S3-basin-only dataset.
|
|
65
|
+
*args:
|
|
66
|
+
Arguments for `RTDCBase`
|
|
67
|
+
**kwargs:
|
|
68
|
+
Keyword arguments for `RTDCBase`
|
|
69
|
+
|
|
70
|
+
Attributes
|
|
71
|
+
----------
|
|
72
|
+
path: str
|
|
73
|
+
Full URL to the DCOR resource
|
|
74
|
+
"""
|
|
75
|
+
if not api.REQUESTS_AVAILABLE:
|
|
76
|
+
raise ModuleNotFoundError(
|
|
77
|
+
"Package `requests` required for DCOR format!")
|
|
78
|
+
|
|
79
|
+
super(RTDC_DCOR, self).__init__(*args, **kwargs)
|
|
80
|
+
|
|
81
|
+
self._hash = None
|
|
82
|
+
self._cache_basin_dict = None
|
|
83
|
+
self.cache_basin_dict_time = 600
|
|
84
|
+
self.path = RTDC_DCOR.get_full_url(url, use_ssl, host)
|
|
85
|
+
|
|
86
|
+
if cert_path is None:
|
|
87
|
+
cert_path = get_server_cert_path(get_host_from_url(self.path))
|
|
88
|
+
|
|
89
|
+
self.api = api.APIHandler(url=self.path,
|
|
90
|
+
api_key=api_key,
|
|
91
|
+
cert_path=cert_path,
|
|
92
|
+
dcserv_api_version=dcserv_api_version)
|
|
93
|
+
|
|
94
|
+
# Parse configuration
|
|
95
|
+
self.config = Configuration(cfg=self.api.get(query="metadata"))
|
|
96
|
+
|
|
97
|
+
# Lazy logs
|
|
98
|
+
self.logs = DCORLogs(self.api)
|
|
99
|
+
|
|
100
|
+
# Lazy tables
|
|
101
|
+
self.tables = DCORTables(self.api)
|
|
102
|
+
|
|
103
|
+
# Get size
|
|
104
|
+
size = self.config["experiment"].get("event count")
|
|
105
|
+
if size is None:
|
|
106
|
+
size = int(self.api.get(query="size"))
|
|
107
|
+
self._size = size
|
|
108
|
+
|
|
109
|
+
self.title = f"{self.config['experiment']['sample']} - " \
|
|
110
|
+
+ f"M{self.config['experiment']['run index']}"
|
|
111
|
+
|
|
112
|
+
def __len__(self):
|
|
113
|
+
return self._size
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def hash(self):
|
|
117
|
+
"""Hash value based on file name and content"""
|
|
118
|
+
if self._hash is None:
|
|
119
|
+
tohash = [self.path]
|
|
120
|
+
self._hash = hashobj(tohash)
|
|
121
|
+
return self._hash
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def get_full_url(url, use_ssl, host=None):
|
|
125
|
+
"""Return the full URL to a DCOR resource
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
url: str
|
|
130
|
+
Full URL or resource identifier; valid values are
|
|
131
|
+
|
|
132
|
+
- https://dcor.mpl.mpg.de/api/3/action/dcserv?id=caab96f6-
|
|
133
|
+
df12-4299-aa2e-089e390aafd5'
|
|
134
|
+
- dcor.mpl.mpg.de/api/3/action/dcserv?id=caab96f6-df12-
|
|
135
|
+
4299-aa2e-089e390aafd5
|
|
136
|
+
- caab96f6-df12-4299-aa2e-089e390aafd5
|
|
137
|
+
use_ssl: bool or None
|
|
138
|
+
Set this to False to disable SSL (should only be used for
|
|
139
|
+
testing). Defaults to None (does not force SSL if the URL
|
|
140
|
+
starts with "http://").
|
|
141
|
+
host: str
|
|
142
|
+
Use this host if it is not specified in `url`
|
|
143
|
+
"""
|
|
144
|
+
if use_ssl is None:
|
|
145
|
+
if url.startswith("http://"):
|
|
146
|
+
# user wanted it that way
|
|
147
|
+
scheme = "http"
|
|
148
|
+
else:
|
|
149
|
+
scheme = "https"
|
|
150
|
+
elif use_ssl:
|
|
151
|
+
scheme = "https"
|
|
152
|
+
else:
|
|
153
|
+
scheme = "http"
|
|
154
|
+
if url.count("://"):
|
|
155
|
+
base = url.split("://", 1)[1]
|
|
156
|
+
else:
|
|
157
|
+
base = url
|
|
158
|
+
# determine the api_path and the netloc
|
|
159
|
+
if base.count("/"):
|
|
160
|
+
netloc, api_path = base.split("/", 1)
|
|
161
|
+
else:
|
|
162
|
+
netloc = None # default to `host`
|
|
163
|
+
api_path = "api/3/action/dcserv?id=" + base
|
|
164
|
+
# remove https from host string (user convenience)
|
|
165
|
+
if host is not None:
|
|
166
|
+
host = host.split("://")[-1]
|
|
167
|
+
|
|
168
|
+
netloc = host if netloc is None else netloc
|
|
169
|
+
new_url = f"{scheme}://{netloc}/{api_path}"
|
|
170
|
+
return new_url
|
|
171
|
+
|
|
172
|
+
def _basin_refresh(self, basin):
|
|
173
|
+
"""Refresh the specified basin"""
|
|
174
|
+
# Retrieve the basin dictionary from DCOR
|
|
175
|
+
basin_dicts = self.basins_get_dicts()
|
|
176
|
+
for bn_dict in basin_dicts:
|
|
177
|
+
if bn_dict.get("name") == basin.name:
|
|
178
|
+
break
|
|
179
|
+
else:
|
|
180
|
+
raise ValueError(f"Basin '{basin.name}' not found in {self}")
|
|
181
|
+
|
|
182
|
+
tre = bn_dict["time_request"]
|
|
183
|
+
ttl = bn_dict["time_expiration"]
|
|
184
|
+
# remember time relative to time.time, subtract 30s to be on safe side
|
|
185
|
+
tex = bn_dict["time_local_request"] + (ttl - tre) - 30
|
|
186
|
+
|
|
187
|
+
if isinstance(basin.perishable, bool):
|
|
188
|
+
logger.debug("Initializing basin perishable %s", basin.name)
|
|
189
|
+
# create a perishable record
|
|
190
|
+
basin.perishable = PerishableRecord(
|
|
191
|
+
basin=basin,
|
|
192
|
+
expiration_func=self._basin_expiration,
|
|
193
|
+
expiration_kwargs={"time_local_expiration": tex},
|
|
194
|
+
refresh_func=self._basin_refresh,
|
|
195
|
+
)
|
|
196
|
+
else:
|
|
197
|
+
logger.debug("Refreshing basin perishable %s", basin.name)
|
|
198
|
+
# only update (this also works with weakref.ProxyType)
|
|
199
|
+
basin.perishable.expiration_kwargs = {"time_local_expiration": tex}
|
|
200
|
+
|
|
201
|
+
if len(bn_dict["urls"]) > 1:
|
|
202
|
+
logger.warning(f"Basin {basin.name} has multiple URLs. I am not "
|
|
203
|
+
f"checking their availability: {bn_dict}")
|
|
204
|
+
basin.location = bn_dict["urls"][0]
|
|
205
|
+
|
|
206
|
+
def _basin_expiration(self, basin, time_local_expiration):
|
|
207
|
+
"""Check whether the basin has perished"""
|
|
208
|
+
return time_local_expiration < time.time()
|
|
209
|
+
|
|
210
|
+
def _basins_get_dicts(self):
|
|
211
|
+
try:
|
|
212
|
+
basin_dicts = self.api.get(query="basins")
|
|
213
|
+
# Fill in missing timing information
|
|
214
|
+
for bn_dict in basin_dicts:
|
|
215
|
+
if (bn_dict.get("format") == "http"
|
|
216
|
+
and "perishable" not in bn_dict):
|
|
217
|
+
# We are communicating with an older version of
|
|
218
|
+
# ckanext-dc_serve. Take a look at the URL and check
|
|
219
|
+
# whether we have a perishable (~1 hour) URL or whether
|
|
220
|
+
# this is a public resource.
|
|
221
|
+
expires_regexp = re.compile(".*expires=([0-9]*)$")
|
|
222
|
+
for url in bn_dict.get("urls", []):
|
|
223
|
+
if match := expires_regexp.match(url.lower()):
|
|
224
|
+
logger.debug("Detected perishable basin: %s",
|
|
225
|
+
bn_dict["name"])
|
|
226
|
+
bn_dict["perishable"] = True
|
|
227
|
+
bn_dict["time_request"] = time.time()
|
|
228
|
+
bn_dict["time_expiration"] = int(match.group(1))
|
|
229
|
+
# add part of the resource ID to the name
|
|
230
|
+
infourl = url.split(bn_dict["name"], 1)[-1]
|
|
231
|
+
infourl = infourl.replace("/", "")
|
|
232
|
+
bn_dict["name"] += f"-{infourl[:5]}"
|
|
233
|
+
break
|
|
234
|
+
else:
|
|
235
|
+
bn_dict["perishable"] = False
|
|
236
|
+
# If we have a perishable basin, add the local request time
|
|
237
|
+
if bn_dict.get("perishable"):
|
|
238
|
+
bn_dict["time_local_request"] = time.time()
|
|
239
|
+
except api.DCORAccessError:
|
|
240
|
+
# TODO: Do not catch this exception when all DCOR instances
|
|
241
|
+
# implement the 'basins' query.
|
|
242
|
+
# This means that the server does not implement the 'basins' query.
|
|
243
|
+
basin_dicts = []
|
|
244
|
+
return basin_dicts
|
|
245
|
+
|
|
246
|
+
def basins_get_dicts(self):
|
|
247
|
+
"""Return list of dicts for all basins defined on DCOR
|
|
248
|
+
|
|
249
|
+
The return value of this method is cached for 10 minutes
|
|
250
|
+
(cache time defined in the `cache_basin_dict_time` [s] property).
|
|
251
|
+
"""
|
|
252
|
+
if (self._cache_basin_dict is None
|
|
253
|
+
or time.time() > (self._cache_basin_dict[1]
|
|
254
|
+
+ self.cache_basin_dict_time)):
|
|
255
|
+
self._cache_basin_dict = (self._basins_get_dicts(), time.time())
|
|
256
|
+
return self._cache_basin_dict[0]
|
|
257
|
+
|
|
258
|
+
def basins_retrieve(self):
|
|
259
|
+
"""Same as superclass, but add perishable information"""
|
|
260
|
+
basin_dicts = self.basins_get_dicts()
|
|
261
|
+
basins = super(RTDC_DCOR, self).basins_retrieve()
|
|
262
|
+
for bn in basins:
|
|
263
|
+
for bn_dict in basin_dicts:
|
|
264
|
+
if bn.name == bn_dict.get("name"):
|
|
265
|
+
# Determine whether we have to set a perishable record.
|
|
266
|
+
if bn_dict.get("perishable"):
|
|
267
|
+
# required for `_basin_refresh` to create a record
|
|
268
|
+
bn.perishable = True
|
|
269
|
+
# create the actual record
|
|
270
|
+
self._basin_refresh(bn)
|
|
271
|
+
break
|
|
272
|
+
return basins
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def get_host_from_url(url):
|
|
276
|
+
"""Extract the hostname from a URL"""
|
|
277
|
+
return url.split("://")[1].split("/")[0]
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def get_server_cert_path(host):
|
|
281
|
+
"""Return server certificate bundle for DCOR `host`"""
|
|
282
|
+
|
|
283
|
+
for path in DCOR_CERTS_SEARCH_PATHS:
|
|
284
|
+
path = pathlib.Path(path)
|
|
285
|
+
cert_path = path / f"{host}.cert"
|
|
286
|
+
if cert_path.exists():
|
|
287
|
+
break
|
|
288
|
+
else:
|
|
289
|
+
# use default certificate bundle
|
|
290
|
+
cert_path = api.requests.certs.where()
|
|
291
|
+
|
|
292
|
+
return cert_path
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def is_dcor_url(string):
|
|
296
|
+
if not isinstance(string, str):
|
|
297
|
+
return False
|
|
298
|
+
else:
|
|
299
|
+
return REGEXP_DCOR_URL.match(string.strip())
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from ..feat_basin import Basin
|
|
4
|
+
|
|
5
|
+
from .api import REQUESTS_AVAILABLE, APIHandler, DCORAccessError
|
|
6
|
+
from .base import RTDC_DCOR
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
REGEXP_FULL_DCOR_URL = re.compile(
|
|
10
|
+
r"^https?:\/\/" # scheme
|
|
11
|
+
r"[a-z0-9-\.]*\.[a-z0-9-\.]*\/?api\/3\/action\/dcserv\?id=" # host and API
|
|
12
|
+
r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$") # id
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DCORBasin(Basin):
|
|
16
|
+
basin_format = "dcor"
|
|
17
|
+
basin_type = "remote"
|
|
18
|
+
|
|
19
|
+
def __init__(self, *args, **kwargs):
|
|
20
|
+
"""Access to private and public DCOR resources
|
|
21
|
+
|
|
22
|
+
Since version 2 of the DCOR data API, all feature data are
|
|
23
|
+
accessed via :class:`.HTTPBasin`s on S3. The DCOR basin is just
|
|
24
|
+
a wrapper around those `HTTPBasin`s.
|
|
25
|
+
|
|
26
|
+
For private resources, the DCOR format facilitates authentication
|
|
27
|
+
via access tokens. Behind the scenes, DCOR creates a pre-signed
|
|
28
|
+
URL to access private data on an S3 object storage provider.
|
|
29
|
+
Note that you must let dclab know your DCOR access
|
|
30
|
+
token via :func:`.APIHandler.add_api_key` for this to work.
|
|
31
|
+
|
|
32
|
+
The `location` must be a full DCOR URL, including the scheme
|
|
33
|
+
and netloc, e.g:
|
|
34
|
+
|
|
35
|
+
https://dcor.mpl.mpg.de/api/3/action/dcserv?
|
|
36
|
+
id=b1404eb5-f661-4920-be79-5ff4e85915d5
|
|
37
|
+
"""
|
|
38
|
+
self._available_verified = None
|
|
39
|
+
super(DCORBasin, self).__init__(*args, **kwargs)
|
|
40
|
+
|
|
41
|
+
def _load_dataset(self, location, **kwargs):
|
|
42
|
+
return RTDC_DCOR(location, **kwargs)
|
|
43
|
+
|
|
44
|
+
def is_available(self):
|
|
45
|
+
"""Check whether a DCOR resource is available
|
|
46
|
+
|
|
47
|
+
Notes
|
|
48
|
+
-----
|
|
49
|
+
- Make sure that your DCOR access token is stored in
|
|
50
|
+
:class:`.APIHandler`. You can add tokens with
|
|
51
|
+
:func:`.APIHandler.add_api_key`.
|
|
52
|
+
"""
|
|
53
|
+
with self._av_check_lock:
|
|
54
|
+
if not REQUESTS_AVAILABLE:
|
|
55
|
+
# don't even bother
|
|
56
|
+
self._available_verified = False
|
|
57
|
+
elif not is_full_dcor_url(self.location):
|
|
58
|
+
# not a full DCOR URL
|
|
59
|
+
self._available_verified = False
|
|
60
|
+
if self._available_verified is None:
|
|
61
|
+
api = APIHandler(self.location)
|
|
62
|
+
try:
|
|
63
|
+
self._available_verified = api.get("valid")
|
|
64
|
+
except DCORAccessError:
|
|
65
|
+
self._available_verified = False
|
|
66
|
+
return self._available_verified
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def is_full_dcor_url(string):
|
|
70
|
+
if not isinstance(string, str):
|
|
71
|
+
return False
|
|
72
|
+
else:
|
|
73
|
+
return REGEXP_FULL_DCOR_URL.match(string.strip())
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class DCORLogs:
|
|
2
|
+
def __init__(self, api):
|
|
3
|
+
self.api = api
|
|
4
|
+
self._logs_cache = None
|
|
5
|
+
|
|
6
|
+
def __contains__(self, key):
|
|
7
|
+
return key in self.keys()
|
|
8
|
+
|
|
9
|
+
def __getitem__(self, key):
|
|
10
|
+
return self._logs[key]
|
|
11
|
+
|
|
12
|
+
def __iter__(self):
|
|
13
|
+
for key in self.keys():
|
|
14
|
+
yield key
|
|
15
|
+
|
|
16
|
+
def __len__(self):
|
|
17
|
+
return len(self._logs)
|
|
18
|
+
|
|
19
|
+
def keys(self):
|
|
20
|
+
return self._logs.keys()
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def _logs(self):
|
|
24
|
+
if self._logs_cache is None:
|
|
25
|
+
self._logs_cache = self.api.get(query="logs", timeout=5)
|
|
26
|
+
return self._logs_cache
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from ..meta_table import MetaTable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DCORTables:
|
|
7
|
+
def __init__(self, api):
|
|
8
|
+
self.api = api
|
|
9
|
+
self._tables_cache = None
|
|
10
|
+
|
|
11
|
+
def __contains__(self, key):
|
|
12
|
+
return key in self.keys()
|
|
13
|
+
|
|
14
|
+
def __getitem__(self, key):
|
|
15
|
+
return self._tables[key]
|
|
16
|
+
|
|
17
|
+
def __iter__(self):
|
|
18
|
+
for key in self.keys():
|
|
19
|
+
yield key
|
|
20
|
+
|
|
21
|
+
def __len__(self):
|
|
22
|
+
return len(self._tables)
|
|
23
|
+
|
|
24
|
+
def keys(self):
|
|
25
|
+
return self._tables.keys()
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def _tables(self):
|
|
29
|
+
if self._tables_cache is None:
|
|
30
|
+
table_data = self.api.get(query="tables", timeout=13)
|
|
31
|
+
# assemble the tables
|
|
32
|
+
tables = {}
|
|
33
|
+
for key in table_data:
|
|
34
|
+
tables[key] = DCORTable(table_data[key])
|
|
35
|
+
|
|
36
|
+
self._tables_cache = tables
|
|
37
|
+
return self._tables_cache
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class DCORTable(MetaTable):
|
|
41
|
+
def __init__(self, table_content):
|
|
42
|
+
self._columns, data = table_content
|
|
43
|
+
self._tab_data = np.asarray(data)
|
|
44
|
+
if self._columns is not None:
|
|
45
|
+
# We have a rec-array (named columns)
|
|
46
|
+
|
|
47
|
+
ds_dt = np.dtype({'names': self._columns,
|
|
48
|
+
'formats': [np.float64] * len(self._columns)})
|
|
49
|
+
self._tab_data = np.rec.array(self._tab_data, dtype=ds_dt)
|
|
50
|
+
|
|
51
|
+
def __array__(self, *args, **kwargs):
|
|
52
|
+
return self._tab_data.__array__(*args, **kwargs)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def meta(self):
|
|
56
|
+
# TODO: Implement metadata sending from DCOR.
|
|
57
|
+
return {}
|
|
58
|
+
|
|
59
|
+
def has_graphs(self):
|
|
60
|
+
return self._columns is not None
|
|
61
|
+
|
|
62
|
+
def keys(self):
|
|
63
|
+
return self._columns
|
|
64
|
+
|
|
65
|
+
def __getitem__(self, key):
|
|
66
|
+
return self._tab_data[key]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""RT-DC dictionary format"""
|
|
2
|
+
import collections
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from .. import definitions as dfn
|
|
8
|
+
from ..util import hashobj
|
|
9
|
+
|
|
10
|
+
from .config import Configuration
|
|
11
|
+
from .core import RTDCBase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DictContourEvent:
|
|
15
|
+
def __init__(self, contours):
|
|
16
|
+
assert contours[0].shape[1] == 2
|
|
17
|
+
self.shape = (len(contours), np.nan, 2)
|
|
18
|
+
self.contours = contours
|
|
19
|
+
|
|
20
|
+
def __iter__(self):
|
|
21
|
+
return iter(self.contours)
|
|
22
|
+
|
|
23
|
+
def __getitem__(self, item):
|
|
24
|
+
return self.contours[item]
|
|
25
|
+
|
|
26
|
+
def __len__(self):
|
|
27
|
+
return len(self.contours)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DictTraceEvent(collections.UserDict):
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def shape(self):
|
|
34
|
+
key0 = sorted(self.keys())[0]
|
|
35
|
+
return len(self), len(self[key0]), len(self[key0][0])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RTDC_Dict(RTDCBase):
|
|
39
|
+
_local_basins_allowed = True
|
|
40
|
+
|
|
41
|
+
def __init__(self, ddict, *args, **kwargs):
|
|
42
|
+
"""Dictionary-based RT-DC dataset
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
ddict: dict
|
|
47
|
+
Dictionary with features as keys (valid features like
|
|
48
|
+
"area_cvx", "deform", "image" are defined by
|
|
49
|
+
`dclab.definitions.feature_exists`) with which the class
|
|
50
|
+
will be instantiated. The configuration is set to the
|
|
51
|
+
default configuration of dclab.
|
|
52
|
+
|
|
53
|
+
.. versionchanged:: 0.27.0
|
|
54
|
+
Scalar features are automatically converted to arrays.
|
|
55
|
+
*args:
|
|
56
|
+
Arguments for `RTDCBase`
|
|
57
|
+
**kwargs:
|
|
58
|
+
Keyword arguments for `RTDCBase`
|
|
59
|
+
"""
|
|
60
|
+
assert ddict
|
|
61
|
+
|
|
62
|
+
super(RTDC_Dict, self).__init__(*args, **kwargs)
|
|
63
|
+
|
|
64
|
+
t = time.localtime()
|
|
65
|
+
|
|
66
|
+
# Get an identifying string
|
|
67
|
+
keys = list(ddict.keys())
|
|
68
|
+
keys.sort()
|
|
69
|
+
ids = hashobj(ddict[keys[0]])
|
|
70
|
+
self._ids = ids
|
|
71
|
+
self.path = "none"
|
|
72
|
+
self.title = "{}_{:02d}_{:02d}/{}.dict".format(t[0], t[1], t[2], ids)
|
|
73
|
+
|
|
74
|
+
# Populate events
|
|
75
|
+
for feat in ddict:
|
|
76
|
+
if dfn.feature_exists(feat):
|
|
77
|
+
if dfn.scalar_feature_exists(feat):
|
|
78
|
+
data = np.array(ddict[feat])
|
|
79
|
+
elif feat == "contour":
|
|
80
|
+
data = DictContourEvent(ddict[feat])
|
|
81
|
+
elif feat == "trace":
|
|
82
|
+
data = DictTraceEvent(ddict[feat])
|
|
83
|
+
elif isinstance(ddict[feat], list):
|
|
84
|
+
# convert e.g. image data to arrays
|
|
85
|
+
data = np.array(ddict[feat])
|
|
86
|
+
else:
|
|
87
|
+
data = ddict[feat]
|
|
88
|
+
else:
|
|
89
|
+
raise ValueError("Invalid feature name '{}'".format(feat))
|
|
90
|
+
if isinstance(data, np.ndarray):
|
|
91
|
+
# Convert numpy array to read-only array
|
|
92
|
+
data = data.view()
|
|
93
|
+
data.setflags(write=False)
|
|
94
|
+
self._events[feat] = data
|
|
95
|
+
|
|
96
|
+
event_count = len(ddict[list(ddict.keys())[0]])
|
|
97
|
+
|
|
98
|
+
self.config = Configuration()
|
|
99
|
+
self.config["experiment"]["event count"] = event_count
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def hash(self):
|
|
103
|
+
return self._ids
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# flake8: noqa: F401
|
|
2
|
+
from .base import RTDC_HDF5, MIN_DCLAB_EXPORT_VERSION
|
|
3
|
+
from .basin import HDF5Basin # import means registering
|
|
4
|
+
from .events import (
|
|
5
|
+
H5Events, H5MaskEvent, H5TraceEvent, H5ScalarEvent, H5ContourEvent)
|
|
6
|
+
from .feat_defect import DEFECTIVE_FEATURES
|