dclab 0.67.0__cp314-cp314t-macosx_10_13_x86_64.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.

Files changed (142) hide show
  1. dclab/__init__.py +41 -0
  2. dclab/_version.py +34 -0
  3. dclab/cached.py +97 -0
  4. dclab/cli/__init__.py +10 -0
  5. dclab/cli/common.py +237 -0
  6. dclab/cli/task_compress.py +126 -0
  7. dclab/cli/task_condense.py +223 -0
  8. dclab/cli/task_join.py +229 -0
  9. dclab/cli/task_repack.py +98 -0
  10. dclab/cli/task_split.py +154 -0
  11. dclab/cli/task_tdms2rtdc.py +186 -0
  12. dclab/cli/task_verify_dataset.py +75 -0
  13. dclab/definitions/__init__.py +79 -0
  14. dclab/definitions/feat_const.py +202 -0
  15. dclab/definitions/feat_logic.py +182 -0
  16. dclab/definitions/meta_const.py +252 -0
  17. dclab/definitions/meta_logic.py +111 -0
  18. dclab/definitions/meta_parse.py +94 -0
  19. dclab/downsampling.cpython-314t-darwin.so +0 -0
  20. dclab/downsampling.pyx +230 -0
  21. dclab/external/__init__.py +4 -0
  22. dclab/external/packaging/LICENSE +3 -0
  23. dclab/external/packaging/LICENSE.APACHE +177 -0
  24. dclab/external/packaging/LICENSE.BSD +23 -0
  25. dclab/external/packaging/__init__.py +6 -0
  26. dclab/external/packaging/_structures.py +61 -0
  27. dclab/external/packaging/version.py +505 -0
  28. dclab/external/skimage/LICENSE +28 -0
  29. dclab/external/skimage/__init__.py +2 -0
  30. dclab/external/skimage/_find_contours.py +216 -0
  31. dclab/external/skimage/_find_contours_cy.cpython-314t-darwin.so +0 -0
  32. dclab/external/skimage/_find_contours_cy.pyx +188 -0
  33. dclab/external/skimage/_pnpoly.cpython-314t-darwin.so +0 -0
  34. dclab/external/skimage/_pnpoly.pyx +99 -0
  35. dclab/external/skimage/_shared/__init__.py +1 -0
  36. dclab/external/skimage/_shared/geometry.cpython-314t-darwin.so +0 -0
  37. dclab/external/skimage/_shared/geometry.pxd +6 -0
  38. dclab/external/skimage/_shared/geometry.pyx +55 -0
  39. dclab/external/skimage/measure.py +7 -0
  40. dclab/external/skimage/pnpoly.py +53 -0
  41. dclab/external/statsmodels/LICENSE +35 -0
  42. dclab/external/statsmodels/__init__.py +6 -0
  43. dclab/external/statsmodels/nonparametric/__init__.py +1 -0
  44. dclab/external/statsmodels/nonparametric/_kernel_base.py +203 -0
  45. dclab/external/statsmodels/nonparametric/kernel_density.py +165 -0
  46. dclab/external/statsmodels/nonparametric/kernels.py +36 -0
  47. dclab/features/__init__.py +9 -0
  48. dclab/features/bright.py +81 -0
  49. dclab/features/bright_bc.py +93 -0
  50. dclab/features/bright_perc.py +63 -0
  51. dclab/features/contour.py +161 -0
  52. dclab/features/emodulus/__init__.py +339 -0
  53. dclab/features/emodulus/load.py +252 -0
  54. dclab/features/emodulus/lut_HE-2D-FEM-22.txt +16432 -0
  55. dclab/features/emodulus/lut_HE-3D-FEM-22.txt +1276 -0
  56. dclab/features/emodulus/lut_LE-2D-FEM-19.txt +13082 -0
  57. dclab/features/emodulus/pxcorr.py +135 -0
  58. dclab/features/emodulus/scale_linear.py +247 -0
  59. dclab/features/emodulus/viscosity.py +260 -0
  60. dclab/features/fl_crosstalk.py +95 -0
  61. dclab/features/inert_ratio.py +377 -0
  62. dclab/features/volume.py +242 -0
  63. dclab/http_utils.py +322 -0
  64. dclab/isoelastics/__init__.py +468 -0
  65. dclab/isoelastics/iso_HE-2D-FEM-22-area_um-deform.txt +2440 -0
  66. dclab/isoelastics/iso_HE-2D-FEM-22-volume-deform.txt +2635 -0
  67. dclab/isoelastics/iso_HE-3D-FEM-22-area_um-deform.txt +1930 -0
  68. dclab/isoelastics/iso_HE-3D-FEM-22-volume-deform.txt +2221 -0
  69. dclab/isoelastics/iso_LE-2D-FEM-19-area_um-deform.txt +2151 -0
  70. dclab/isoelastics/iso_LE-2D-FEM-19-volume-deform.txt +2250 -0
  71. dclab/isoelastics/iso_LE-2D-ana-18-area_um-deform.txt +1266 -0
  72. dclab/kde/__init__.py +1 -0
  73. dclab/kde/base.py +459 -0
  74. dclab/kde/contours.py +222 -0
  75. dclab/kde/methods.py +313 -0
  76. dclab/kde_contours.py +10 -0
  77. dclab/kde_methods.py +11 -0
  78. dclab/lme4/__init__.py +5 -0
  79. dclab/lme4/lme4_template.R +94 -0
  80. dclab/lme4/rsetup.py +204 -0
  81. dclab/lme4/wrapr.py +386 -0
  82. dclab/polygon_filter.py +398 -0
  83. dclab/rtdc_dataset/__init__.py +15 -0
  84. dclab/rtdc_dataset/check.py +902 -0
  85. dclab/rtdc_dataset/config.py +533 -0
  86. dclab/rtdc_dataset/copier.py +353 -0
  87. dclab/rtdc_dataset/core.py +896 -0
  88. dclab/rtdc_dataset/export.py +867 -0
  89. dclab/rtdc_dataset/feat_anc_core/__init__.py +24 -0
  90. dclab/rtdc_dataset/feat_anc_core/af_basic.py +75 -0
  91. dclab/rtdc_dataset/feat_anc_core/af_emodulus.py +160 -0
  92. dclab/rtdc_dataset/feat_anc_core/af_fl_max_ctc.py +133 -0
  93. dclab/rtdc_dataset/feat_anc_core/af_image_contour.py +113 -0
  94. dclab/rtdc_dataset/feat_anc_core/af_ml_class.py +102 -0
  95. dclab/rtdc_dataset/feat_anc_core/ancillary_feature.py +320 -0
  96. dclab/rtdc_dataset/feat_anc_ml/__init__.py +32 -0
  97. dclab/rtdc_dataset/feat_anc_plugin/__init__.py +3 -0
  98. dclab/rtdc_dataset/feat_anc_plugin/plugin_feature.py +329 -0
  99. dclab/rtdc_dataset/feat_basin.py +762 -0
  100. dclab/rtdc_dataset/feat_temp.py +102 -0
  101. dclab/rtdc_dataset/filter.py +263 -0
  102. dclab/rtdc_dataset/fmt_dcor/__init__.py +7 -0
  103. dclab/rtdc_dataset/fmt_dcor/access_token.py +52 -0
  104. dclab/rtdc_dataset/fmt_dcor/api.py +173 -0
  105. dclab/rtdc_dataset/fmt_dcor/base.py +299 -0
  106. dclab/rtdc_dataset/fmt_dcor/basin.py +73 -0
  107. dclab/rtdc_dataset/fmt_dcor/logs.py +26 -0
  108. dclab/rtdc_dataset/fmt_dcor/tables.py +66 -0
  109. dclab/rtdc_dataset/fmt_dict.py +103 -0
  110. dclab/rtdc_dataset/fmt_hdf5/__init__.py +6 -0
  111. dclab/rtdc_dataset/fmt_hdf5/base.py +192 -0
  112. dclab/rtdc_dataset/fmt_hdf5/basin.py +30 -0
  113. dclab/rtdc_dataset/fmt_hdf5/events.py +276 -0
  114. dclab/rtdc_dataset/fmt_hdf5/feat_defect.py +164 -0
  115. dclab/rtdc_dataset/fmt_hdf5/logs.py +33 -0
  116. dclab/rtdc_dataset/fmt_hdf5/tables.py +60 -0
  117. dclab/rtdc_dataset/fmt_hierarchy/__init__.py +11 -0
  118. dclab/rtdc_dataset/fmt_hierarchy/base.py +278 -0
  119. dclab/rtdc_dataset/fmt_hierarchy/events.py +146 -0
  120. dclab/rtdc_dataset/fmt_hierarchy/hfilter.py +140 -0
  121. dclab/rtdc_dataset/fmt_hierarchy/mapper.py +134 -0
  122. dclab/rtdc_dataset/fmt_http.py +102 -0
  123. dclab/rtdc_dataset/fmt_s3.py +354 -0
  124. dclab/rtdc_dataset/fmt_tdms/__init__.py +476 -0
  125. dclab/rtdc_dataset/fmt_tdms/event_contour.py +264 -0
  126. dclab/rtdc_dataset/fmt_tdms/event_image.py +220 -0
  127. dclab/rtdc_dataset/fmt_tdms/event_mask.py +62 -0
  128. dclab/rtdc_dataset/fmt_tdms/event_trace.py +146 -0
  129. dclab/rtdc_dataset/fmt_tdms/exc.py +37 -0
  130. dclab/rtdc_dataset/fmt_tdms/naming.py +151 -0
  131. dclab/rtdc_dataset/load.py +77 -0
  132. dclab/rtdc_dataset/meta_table.py +25 -0
  133. dclab/rtdc_dataset/writer.py +1019 -0
  134. dclab/statistics.py +226 -0
  135. dclab/util.py +176 -0
  136. dclab/warn.py +15 -0
  137. dclab-0.67.0.dist-info/METADATA +153 -0
  138. dclab-0.67.0.dist-info/RECORD +142 -0
  139. dclab-0.67.0.dist-info/WHEEL +6 -0
  140. dclab-0.67.0.dist-info/entry_points.txt +8 -0
  141. dclab-0.67.0.dist-info/licenses/LICENSE +283 -0
  142. dclab-0.67.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,476 @@
1
+ """RT-DC .tdms file format"""
2
+ import copy
3
+ import pathlib
4
+ import time
5
+
6
+ from ...external.packaging import parse as parse_version
7
+
8
+ from .exc import ContourIndexingError # noqa:F401
9
+ from .exc import InvalidTDMSFileFormatError # noqa:F401
10
+ from .exc import IncompleteTDMSFileFormatError # noqa:F401
11
+ from .exc import InvalidVideoFileError # noqa:F401
12
+
13
+ try:
14
+ import nptdms
15
+ except ModuleNotFoundError:
16
+ NPTDMS_AVAILABLE = False
17
+ else:
18
+ if parse_version(nptdms.__version__) < parse_version("0.23.0"):
19
+ raise ValueError("Please install nptdms>=0.23.0")
20
+ NPTDMS_AVAILABLE = True
21
+ from .event_contour import ContourColumn
22
+ from .event_image import ImageColumn
23
+ from .event_mask import MaskColumn
24
+ from .event_trace import TraceColumn
25
+
26
+ import numpy as np
27
+
28
+ from ... import definitions as dfn
29
+ from ...util import hashobj, hashfile
30
+
31
+ from ..config import Configuration
32
+ from ..core import RTDCBase
33
+
34
+ from . import naming
35
+
36
+
37
+ class RTDC_TDMS(RTDCBase):
38
+ def __init__(self, tdms_path, *args, **kwargs):
39
+ """TDMS file format for RT-DC measurements
40
+
41
+ Parameters
42
+ ----------
43
+ tdms_path: str or pathlib.Path
44
+ Path to a '.tdms' measurement file.
45
+ *args:
46
+ Arguments for `RTDCBase`
47
+ **kwargs:
48
+ Keyword arguments for `RTDCBase`
49
+
50
+ Attributes
51
+ ----------
52
+ path: pathlib.Path
53
+ Path to the experimental dataset (main .tdms file)
54
+ """
55
+ if not NPTDMS_AVAILABLE:
56
+ raise ModuleNotFoundError(
57
+ "Package `nptdms` required for TDMS format!")
58
+ # Initialize RTDCBase
59
+ super(RTDC_TDMS, self).__init__(*args, **kwargs)
60
+
61
+ tdms_path = pathlib.Path(tdms_path)
62
+ # Events is a simple dictionary
63
+ self._events = {}
64
+ self._hash = None
65
+ self.path = tdms_path
66
+ self.title = get_project_name_from_path(tdms_path, append_mx=True)
67
+
68
+ # tdms-related convenience properties
69
+ self._fdir = tdms_path.parent
70
+ self._mid = tdms_path.name.split("_")[0]
71
+
72
+ self._init_data_with_tdms(tdms_path)
73
+
74
+ # Add additional features
75
+ # event images
76
+ self._events["image"] = ImageColumn(self)
77
+ # event contours (requires image)
78
+ self._events["contour"] = ContourColumn(self)
79
+ # event masks (requires contour)
80
+ self._events["mask"] = MaskColumn(self)
81
+ # event traces
82
+ self._events["trace"] = TraceColumn(self)
83
+
84
+ def __contains__(self, key):
85
+ ct = False
86
+ if key in ["contour", "image", "mask", "trace"]:
87
+ # Take into account special cases of the tdms file format:
88
+ # tdms features "image", "trace", "contour", and "mask"
89
+ # evaluate to True (len()!=0) if the data exist on disk
90
+ if key in self._events and self._events[key]:
91
+ ct = True
92
+ else:
93
+ ct = super(RTDC_TDMS, self).__contains__(key)
94
+ return ct
95
+
96
+ @staticmethod
97
+ def extract_tdms_config(path,
98
+ features_available=None,
99
+ ret_source_files=False,
100
+ ignore_missing=False):
101
+ """Extract as much metadata as possible for a .tdms dataset
102
+
103
+ Parameters
104
+ ----------
105
+ path: str or pathlib.Path
106
+ A path representing the dataset. This could be either a
107
+ .tdms file or an .avi file. The only thing important here
108
+ is the prefix (before the underscore "_") which determines
109
+ the location of the camera.ini and para.ini files.
110
+ features_available: list of str
111
+ List of features known to be available for this dataset.
112
+ Used for defining e.g. fluorescnence metadata.
113
+ ret_source_files: bool
114
+ Return the list of files used to extract metadata from.
115
+ ignore_missing: bool
116
+ Nevermind when para.ini is missing.
117
+
118
+ Returns
119
+ -------
120
+ config: .Configuration
121
+ The metadata Configuration instance
122
+ source_paths: str
123
+ List of metadata file paths, only returned when ret_source_files
124
+ is True
125
+ """
126
+ if features_available is None:
127
+ features_available = []
128
+ mid = path.name.split("_")[0]
129
+ # Set up configuration
130
+ config_paths = []
131
+ para_ini = path.with_name(mid + "_para.ini")
132
+ if not para_ini.exists() and not ignore_missing:
133
+ raise IncompleteTDMSFileFormatError(f"Could not find {para_ini}!")
134
+ for pp in [para_ini,
135
+ path.with_name(mid + "_camera.ini"),
136
+ path.with_name(mid + "_SoftwareSettings.ini")]:
137
+ if pp.exists():
138
+ config_paths.append(pp)
139
+
140
+ tdms_config = Configuration(files=config_paths, disable_checks=True)
141
+
142
+ dclab_config = Configuration()
143
+
144
+ source_files = copy.copy(config_paths)
145
+
146
+ for cfgii in [naming.configmap, naming.config_map_set]:
147
+ for section in cfgii:
148
+ for pname in cfgii[section]:
149
+ meta = cfgii[section][pname]
150
+ convfunc = dfn.get_config_value_func(section, pname)
151
+ if isinstance(meta, tuple):
152
+ osec, opar = meta
153
+ if osec in tdms_config and opar in tdms_config[osec]:
154
+ val = tdms_config[osec].pop(opar)
155
+ dclab_config[section][pname] = convfunc(val)
156
+ else:
157
+ dclab_config[section][pname] = convfunc(meta)
158
+
159
+ # Additional information from log file
160
+ rtfdc_log = path.with_name(mid + "_log.ini")
161
+ if rtfdc_log.exists():
162
+ source_files.append(rtfdc_log)
163
+ with rtfdc_log.open("r", errors="replace") as fd:
164
+ loglines = fd.readlines()
165
+ for line in loglines:
166
+ if line.startswith("[EVENT LOG]"):
167
+ sv = line.split("]")[1].strip()
168
+ if sv:
169
+ dclab_config["setup"]["software version"] = sv
170
+
171
+ rtfdc_parm = path.with_name("parameters.txt")
172
+ if rtfdc_parm.exists():
173
+ source_files.append(rtfdc_parm)
174
+ with rtfdc_parm.open("r", errors="replace") as fd:
175
+ parlines = fd.readlines()
176
+ p1 = None
177
+ p2 = None
178
+ p3 = None
179
+ for line in parlines:
180
+ if line.startswith("pulse_led"):
181
+ fdur = float(line.split()[1])
182
+ dclab_config["imaging"]["flash duration"] = fdur
183
+ elif line.startswith("numberofchannels"):
184
+ nc = int(line.split()[1])
185
+ dclab_config["fluorescence"]["channel count"] = nc
186
+ elif line.startswith("laser488"):
187
+ p1 = float(line.split()[1])
188
+ dclab_config["fluorescence"]["laser 1 lambda"] = 488
189
+ dclab_config["fluorescence"]["laser 1 power"] = p1
190
+ elif line.startswith("laser561"):
191
+ p2 = float(line.split()[1])
192
+ dclab_config["fluorescence"]["laser 2 lambda"] = 561
193
+ dclab_config["fluorescence"]["laser 2 power"] = p2
194
+ elif line.startswith("laser640"):
195
+ p3 = float(line.split()[1])
196
+ dclab_config["fluorescence"]["laser 3 lambda"] = 640
197
+ dclab_config["fluorescence"]["laser 3 power"] = p3
198
+ elif line.startswith("samplerate"):
199
+ sr = int(float(line.split()[1]))
200
+ dclab_config["fluorescence"]["sample rate"] = sr
201
+ elif line.startswith("samplesperframe"):
202
+ spe = int(line.split()[1])
203
+ dclab_config["fluorescence"]["samples per event"] = spe
204
+ elif line.startswith("Vmin"):
205
+ vmin = float(line.split()[1])
206
+ dclab_config["fluorescence"]["signal min"] = vmin
207
+ elif line.startswith("Vmax"):
208
+ vmax = float(line.split()[1])
209
+ dclab_config["fluorescence"]["signal max"] = vmax
210
+ elif line.startswith("median_pmt"):
211
+ mfs = int(line.split()[1])
212
+ dclab_config["fluorescence"]["trace median"] = mfs
213
+ # Add generic channel names (independent of lasers)
214
+ for ii in range(1, 4):
215
+ chn = "channel {} name".format(ii)
216
+ fln = "fl{}_max".format(ii)
217
+ if (fln in features_available
218
+ and chn not in dclab_config["fluorescence"]):
219
+ dclab_config["fluorescence"][chn] = "FL{}".format(ii)
220
+ lc = bool(p1) + bool(p2) + bool(p3)
221
+ dclab_config["fluorescence"]["laser count"] = lc
222
+ li = (p1 is not None) + (p2 is not None) + (p3 is not None)
223
+ dclab_config["fluorescence"]["lasers installed"] = li
224
+ dclab_config["fluorescence"]["channels installed"] = 3
225
+
226
+ # fluorescence
227
+ if ("fluorescence" in dclab_config
228
+ or "fl1_max" in features_available
229
+ or "fl2_max" in features_available
230
+ or "fl3_max" in features_available):
231
+ # hardware-defined (always the same)
232
+ dclab_config["fluorescence"].setdefault("bit depth", 16)
233
+ dclab_config["fluorescence"].setdefault("laser 1 lambda", 488.)
234
+ dclab_config["fluorescence"].setdefault("laser 2 lambda", 561.)
235
+ dclab_config["fluorescence"].setdefault("laser 3 lambda", 640.)
236
+
237
+ # Additional information from commented-out log-file (manual)
238
+ if para_ini.exists():
239
+ text = para_ini.read_text(errors="replace").split("\n")
240
+ lns = [s[1:].strip() for s in text if s.startswith("#")]
241
+ if lns and lns[0] == "[FLUOR]":
242
+ if ("software version" not in dclab_config["setup"]
243
+ and lns[1].startswith("fRTDC")):
244
+ dclab_config["setup"]["software version"] = lns[1]
245
+ for ll in lns[2:]:
246
+ if ("sample rate" not in dclab_config["fluorescence"]
247
+ and ll.startswith("Samplerate")):
248
+ val = int(float(ll.split("=")[1]))
249
+ dclab_config["fluorescence"]["sample rate"] = val
250
+ elif ("signal min" not in dclab_config["fluorescence"]
251
+ and ll.startswith("ADCmin")):
252
+ val = float(ll.split("=")[1])
253
+ dclab_config["fluorescence"]["signal min"] = val
254
+ elif ("signal max" not in dclab_config["fluorescence"]
255
+ and ll.startswith("ADCmax")):
256
+ val = float(ll.split("=")[1])
257
+ dclab_config["fluorescence"]["signal max"] = val
258
+
259
+ if dclab_config["imaging"].get("frame rate") == 0:
260
+ dclab_config["imaging"].pop("frame rate")
261
+
262
+ if dclab_config["setup"].get("flow rate") == 0:
263
+ dclab_config["setup"].pop("flow rate")
264
+
265
+ if "channel width" not in dclab_config["setup"]:
266
+ if "channel width" in tdms_config.get("general", {}):
267
+ channel_width = tdms_config["general"]["channel width"]
268
+ elif dclab_config["setup"].get("flow rate", 0) < 0.16:
269
+ channel_width = 20.
270
+ else:
271
+ channel_width = 30.
272
+ dclab_config["setup"]["channel width"] = channel_width
273
+
274
+ if "sample" not in dclab_config["experiment"]:
275
+ # Measured sample or user-defined reference
276
+ sample = get_project_name_from_path(path)
277
+ dclab_config["experiment"]["sample"] = sample
278
+
279
+ # imaging
280
+ dclab_config["imaging"].setdefault("pixel size", 0.34)
281
+
282
+ # medium convention for CellCarrierB
283
+ if ("medium" in dclab_config["setup"] and
284
+ dclab_config["setup"]["medium"].lower() == "cellcarrier b"):
285
+ dclab_config["setup"]["medium"] = "CellCarrierB"
286
+
287
+ # replace "+" with ","
288
+ if "module composition" in dclab_config["setup"]:
289
+ mc = dclab_config["setup"]["module composition"]
290
+ if mc.count("+"):
291
+ mc2 = ", ".join([m.strip() for m in mc.split("+")])
292
+ dclab_config["setup"]["module composition"] = mc2
293
+
294
+ dclab_config["imaging"].setdefault("flash device", "LED")
295
+ dclab_config["imaging"].setdefault("flash duration", 2.0)
296
+ dclab_config["imaging"].setdefault("roi position x", 0)
297
+ dclab_config["imaging"].setdefault("roi position y", 0)
298
+
299
+ if mid.startswith("m") and mid[1] in "0123456789":
300
+ run_index = int(mid.strip("mM"))
301
+ else:
302
+ run_index = 1
303
+ dclab_config["experiment"].setdefault("run index", run_index)
304
+
305
+ if ret_source_files:
306
+ return dclab_config, source_files
307
+ else:
308
+ return dclab_config
309
+
310
+ def _init_data_with_tdms(self, tdms_filename):
311
+ """Initializes the current RT-DC dataset with a tdms file.
312
+ """
313
+ tdms_file = nptdms.TdmsFile(str(tdms_filename))
314
+ # time is always there
315
+ table = "Cell Track"
316
+ # Edit naming.dclab2tdms to add features
317
+ for arg in naming.tdms2dclab:
318
+ try:
319
+ data = tdms_file[table][arg].data
320
+ except KeyError:
321
+ pass
322
+ else:
323
+ if data is None or len(data) == 0:
324
+ # Ignore empty features. npTDMS treats empty
325
+ # features in the following way:
326
+ # - in nptdms 0.8.2, `data` is `None`
327
+ # - in nptdms 0.9.0, `data` is an array of length 0
328
+ continue
329
+ self._events[naming.tdms2dclab[arg]] = data
330
+ if len(self._events) == 0:
331
+ raise IncompleteTDMSFileFormatError(
332
+ "No usable feature data found in '{}'!".format(tdms_filename))
333
+
334
+ self.config, config_paths = self.extract_tdms_config(
335
+ self.path,
336
+ features_available=sorted(self._events.keys()),
337
+ ret_source_files=True)
338
+ self._complete_config_with_data()
339
+
340
+ # Load log files
341
+ log_files = config_paths
342
+ for name in [self._mid + "_events.txt",
343
+ self._mid + "_log.ini",
344
+ self._mid + "_SoftwareSettings.ini",
345
+ "FG_Config.mcf",
346
+ "parameters.txt"]:
347
+ pl = self.path.with_name(name)
348
+ if pl.exists():
349
+ log_files.append(pl)
350
+ for pp in sorted(set(log_files)): # avoid duplicates
351
+ with pp.open("r", errors="replace") as f:
352
+ cfg = [s.strip() for s in f.readlines()]
353
+ self.logs[pp.name] = cfg
354
+
355
+ def _complete_config_with_data(self):
356
+ # measurement start time
357
+ tse = self.path.stat().st_mtime
358
+ if "time" in self:
359
+ # correct for duration of experiment
360
+ tse -= self["time"][-1]
361
+ loct = time.localtime(tse)
362
+
363
+ # Start time of measurement ('HH:MM:SS')
364
+ timestr = time.strftime("%H:%M:%S", loct)
365
+ self.config["experiment"].setdefault("time", timestr)
366
+
367
+ # Date of measurement ('YYYY-MM-DD')
368
+ datestr = time.strftime("%Y-%m-%d", loct)
369
+ self.config["experiment"].setdefault("date", datestr)
370
+
371
+ # Number of recorded events
372
+ self.config["experiment"].setdefault("event count", len(self))
373
+
374
+ # fmt_tdms
375
+ self.config["fmt_tdms"].setdefault("video frame offset", 1)
376
+
377
+ # setup (compatibility to old tdms formats)
378
+ self.config["setup"].setdefault("flow rate", np.nan)
379
+
380
+ @staticmethod
381
+ def can_open(h5path):
382
+ """Check whether a given file is in the .tdms file format"""
383
+ return pathlib.Path(h5path).suffix == ".tdms"
384
+
385
+ @property
386
+ def hash(self):
387
+ """Hash value based on file name and .ini file content"""
388
+ if self._hash is None:
389
+ # Only hash _camera.ini and _para.ini
390
+ fsh = [self.path.with_name(self._mid + "_camera.ini"),
391
+ self.path.with_name(self._mid + "_para.ini")]
392
+ tohash = [hashfile(f) for f in fsh]
393
+ tohash.append(self.path.name)
394
+ # Hash a maximum of ~1MB of the tdms file
395
+ tohash.append(hashfile(self.path, blocksize=65536, count=20))
396
+ self._hash = hashobj(tohash)
397
+ return self._hash
398
+
399
+ def close(self):
400
+ super(RTDC_TDMS, self).close()
401
+ del self._events["image"]._image_data
402
+
403
+
404
+ def get_project_name_from_path(path, append_mx=False):
405
+ """Get the project name from a path.
406
+
407
+ For a path "/home/peter/hans/HLC12398/online/M1_13.tdms" or
408
+ For a path "/home/peter/hans/HLC12398/online/data/M1_13.tdms" or
409
+ without the ".tdms" file, this will return always "HLC12398".
410
+
411
+ Parameters
412
+ ----------
413
+ path: str or pathlib.Path
414
+ path to tdms file
415
+ append_mx: bool
416
+ append measurement number, e.g. "M1"
417
+ """
418
+ path = pathlib.Path(path)
419
+ if path.suffix == ".tdms":
420
+ dirn = path.parent
421
+ mx = path.name.split("_")[0]
422
+ elif path.is_dir():
423
+ dirn = path
424
+ mx = ""
425
+ else:
426
+ dirn = path.parent
427
+ mx = ""
428
+
429
+ project = ""
430
+ if mx:
431
+ # check para.ini
432
+ para = dirn / (mx + "_para.ini")
433
+ if para.exists():
434
+ with para.open("r", errors="replace") as fd:
435
+ lines = fd.readlines()
436
+ for line in lines:
437
+ if line.startswith("Sample Name ="):
438
+ project = line.split("=")[1].strip()
439
+ break
440
+
441
+ if not project:
442
+ # check if the directory contains data or is online
443
+ root1, trail1 = dirn.parent, dirn.name
444
+ root2, trail2 = root1.parent, root1.name
445
+ trail3 = root2.name
446
+
447
+ if trail1.lower() in ["online", "offline"]:
448
+ # /home/peter/hans/HLC12398/online/
449
+ project = trail2
450
+ elif (trail1.lower() == "data" and
451
+ trail2.lower() in ["online", "offline"]):
452
+ # this is olis new folder sctructure
453
+ # /home/peter/hans/HLC12398/online/data/
454
+ project = trail3
455
+ else:
456
+ project = trail1
457
+
458
+ if append_mx:
459
+ project += " - " + mx
460
+
461
+ return project
462
+
463
+
464
+ def get_tdms_files(directory):
465
+ """Recursively find projects based on '.tdms' file endings
466
+
467
+ Searches the `directory` recursively and return a sorted list
468
+ of all found '.tdms' project files, except fluorescence
469
+ data trace files which end with `_traces.tdms`.
470
+ """
471
+ path = pathlib.Path(directory).resolve()
472
+ # get all tdms files
473
+ tdmslist = [r for r in path.rglob("*.tdms") if r.is_file()]
474
+ # exclude traces files
475
+ tdmslist = [r for r in tdmslist if not r.name.endswith("_traces.tdms")]
476
+ return sorted(tdmslist)