ECGProcess 1.1.0__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.
- ecgprocess/__init__.py +1 -0
- ecgprocess/_version.py +1 -0
- ecgprocess/constants.py +170 -0
- ecgprocess/errors.py +380 -0
- ecgprocess/example_data/__init__.py +1 -0
- ecgprocess/example_data/example_datasets/.example-XML1.xml.un~ +0 -0
- ecgprocess/example_data/example_datasets/.example-XML1.xsd.un~ +0 -0
- ecgprocess/example_data/example_datasets/.example-XML1_a.xsd.un~ +0 -0
- ecgprocess/example_data/example_datasets/.example-XML1_d.xsd.un~ +0 -0
- ecgprocess/example_data/example_datasets/.example-XML1_wrong.xsd.un~ +0 -0
- ecgprocess/example_data/example_datasets/example-DICOM1.dcm +0 -0
- ecgprocess/example_data/example_datasets/example-DICOM2.dcm +0 -0
- ecgprocess/example_data/example_datasets/example-XML1.xml +675 -0
- ecgprocess/example_data/example_datasets/example-XML1.xsd +518 -0
- ecgprocess/example_data/example_datasets/example-XML1_error.xsd +519 -0
- ecgprocess/example_data/example_datasets/example-XML1_wrong.xsd +519 -0
- ecgprocess/example_data/example_datasets/string_data.txt +1 -0
- ecgprocess/example_data/examples.py +501 -0
- ecgprocess/plot_ecgs.py +921 -0
- ecgprocess/process_dicom.py +243 -0
- ecgprocess/process_xml.py +319 -0
- ecgprocess/tabular.py +1394 -0
- ecgprocess/utils/__init__.py +1 -0
- ecgprocess/utils/config_tools.py +581 -0
- ecgprocess/utils/ecg_tools.py +311 -0
- ecgprocess/utils/engineering_tools.py +385 -0
- ecgprocess/utils/general.py +399 -0
- ecgprocess/utils/reader_tools.py +508 -0
- ecgprocess-1.1.0.dist-info/METADATA +153 -0
- ecgprocess-1.1.0.dist-info/RECORD +33 -0
- ecgprocess-1.1.0.dist-info/WHEEL +5 -0
- ecgprocess-1.1.0.dist-info/licenses/LICENCE.txt +17 -0
- ecgprocess-1.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
"""Provides centralised access to example data sets that can be used in tests
|
|
2
|
+
and also in example code and/or jupyter notebooks.
|
|
3
|
+
|
|
4
|
+
Notes
|
|
5
|
+
-----
|
|
6
|
+
Data can be "added" either through functions that generate the data on the fly
|
|
7
|
+
or via functions that load the data from a static file located in the
|
|
8
|
+
``example_data`` directory. The data files being added should be as small as
|
|
9
|
+
possible (i.e. kilobyte/megabyte range). The dataset functions should be
|
|
10
|
+
decorated with the ``@dataset`` decorator, so the example module knows about
|
|
11
|
+
them. If the function is loading a dataset from a file in the package, it
|
|
12
|
+
should look for the path in ``_ROOT_DATASETS_DIR``.
|
|
13
|
+
|
|
14
|
+
Examples
|
|
15
|
+
--------
|
|
16
|
+
|
|
17
|
+
Registering a function as a dataset providing function:
|
|
18
|
+
|
|
19
|
+
>>> @dataset
|
|
20
|
+
>>> def dummy_data(*args, **kwargs):
|
|
21
|
+
>>> \"\"\"A dummy dataset function that returns a small list.
|
|
22
|
+
>>>
|
|
23
|
+
>>> Returns
|
|
24
|
+
>>> -------
|
|
25
|
+
>>> data : `list`
|
|
26
|
+
>>> A list of length 3 with ``['A', 'B', 'C']``
|
|
27
|
+
>>>
|
|
28
|
+
>>> Notes
|
|
29
|
+
>>> -----
|
|
30
|
+
>>> This function is called ``dummy_data`` and has been decorated with a
|
|
31
|
+
>>> ``@dataset`` decorator which makes it available with the
|
|
32
|
+
>>> `example_data.get_data(<NAME>)` function and also
|
|
33
|
+
>>> `example_data.help(<NAME>)` functions.
|
|
34
|
+
>>> \"\"\"
|
|
35
|
+
>>> return ['A', 'B', 'C']
|
|
36
|
+
|
|
37
|
+
The dataset can then be used as follows:
|
|
38
|
+
|
|
39
|
+
>>> from ecgprocess.example_data import examples
|
|
40
|
+
>>> examples.get_data('dummy_data')
|
|
41
|
+
>>> ['A', 'B', 'C']
|
|
42
|
+
|
|
43
|
+
A dataset function that loads a dataset from file, these functions should load
|
|
44
|
+
from the ``_ROOT_DATASETS_DIR``:
|
|
45
|
+
|
|
46
|
+
>>> @dataset
|
|
47
|
+
>>> def dummy_load_data(*args, **kwargs):
|
|
48
|
+
>>> \"\"\"A dummy dataset function that loads a string from a file.
|
|
49
|
+
>>>
|
|
50
|
+
>>> Returns
|
|
51
|
+
>>> -------
|
|
52
|
+
>>> str_data : `str`
|
|
53
|
+
>>> A string of data loaded from an example data file.
|
|
54
|
+
>>>
|
|
55
|
+
>>> Notes
|
|
56
|
+
>>> -----
|
|
57
|
+
>>> This function is called ``dummy_data`` and has been decorated with a
|
|
58
|
+
>>> ``@dataset`` decorator which makes it available with the
|
|
59
|
+
>>> `example_data.get_data(<NAME>)` function and also
|
|
60
|
+
>>> `example_data.help(<NAME>)` functions. The path to this dataset is
|
|
61
|
+
>>> built from ``_ROOT_DATASETS_DIR``.
|
|
62
|
+
>>> \"\"\"
|
|
63
|
+
>>> load_path = os.path.join(_ROOT_DATASETS_DIR, "string_data.txt")
|
|
64
|
+
>>> with open(load_path) as data_file:
|
|
65
|
+
>>> return data_file.read().strip()
|
|
66
|
+
|
|
67
|
+
The dataset can then be used as follows:
|
|
68
|
+
|
|
69
|
+
>>> from ecgprocess.example_data import examples
|
|
70
|
+
>>> examples.get_data('dummy_load_data')
|
|
71
|
+
>>> 'an example data string'
|
|
72
|
+
"""
|
|
73
|
+
import os
|
|
74
|
+
import re
|
|
75
|
+
import pandas as pd
|
|
76
|
+
import numpy as np
|
|
77
|
+
import ecgprocess.utils.config_tools as config_utils
|
|
78
|
+
from tempfile import NamedTemporaryFile
|
|
79
|
+
from ecgprocess.constants import CoreData as Core
|
|
80
|
+
CLeads = Core.Leads
|
|
81
|
+
# The name of the example datasets directory
|
|
82
|
+
_EXAMPLE_DATASETS = "example_datasets"
|
|
83
|
+
"""The example dataset directory name (`str`)
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
_ROOT_DATASETS_DIR = os.path.join(os.path.dirname(__file__), _EXAMPLE_DATASETS)
|
|
87
|
+
"""The root path to the dataset files that are available (`str`)
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
_DATASETS = dict()
|
|
91
|
+
"""This will hold the registered dataset functions (`dict`)
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
96
|
+
def dataset(func):
|
|
97
|
+
"""Register a dataset generating function. This function should be used as
|
|
98
|
+
a decorator.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
func : `function`
|
|
103
|
+
The function to register as a dataset. It is registered as under the
|
|
104
|
+
function name.
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
func : `function`
|
|
109
|
+
The function that has been registered.
|
|
110
|
+
|
|
111
|
+
Raises
|
|
112
|
+
------
|
|
113
|
+
KeyError
|
|
114
|
+
If a function of the same name has already been registered.
|
|
115
|
+
|
|
116
|
+
Notes
|
|
117
|
+
-----
|
|
118
|
+
The dataset function should accept ``*args`` and ``**kwargs`` and should be
|
|
119
|
+
decorated with the ``@dataset`` decorator.
|
|
120
|
+
|
|
121
|
+
Examples
|
|
122
|
+
--------
|
|
123
|
+
Create a dataset function that returns a dictionary.
|
|
124
|
+
|
|
125
|
+
>>> @dataset
|
|
126
|
+
>>> def get_dict(*args, **kwargs):
|
|
127
|
+
>>> \"\"\"A dictionary to test or use as an example.
|
|
128
|
+
>>>
|
|
129
|
+
>>> Returns
|
|
130
|
+
>>> -------
|
|
131
|
+
>>> test_dict : `dict`
|
|
132
|
+
>>> A small dictionary of string keys and numeric values
|
|
133
|
+
>>> \"\"\"
|
|
134
|
+
>>> return {'A': 1, 'B': 2, 'C': 3}
|
|
135
|
+
>>>
|
|
136
|
+
|
|
137
|
+
The dataset can then be used as follows:
|
|
138
|
+
|
|
139
|
+
>>> from ecgprocess.example_data import examples
|
|
140
|
+
>>> examples.get_data('get_dict')
|
|
141
|
+
>>> {'A': 1, 'B': 2, 'C': 3}
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
_DATASETS[func.__name__]
|
|
146
|
+
raise KeyError("function already registered")
|
|
147
|
+
except KeyError:
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
_DATASETS[func.__name__] = func
|
|
151
|
+
return func
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
155
|
+
def get_data(name, *args, **kwargs):
|
|
156
|
+
"""Central point to get the datasets.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
name : `str`
|
|
161
|
+
A name for the dataset that should correspond to a registered
|
|
162
|
+
dataset function.
|
|
163
|
+
*args
|
|
164
|
+
Arguments to the data generating functions
|
|
165
|
+
**kwargs
|
|
166
|
+
Keyword arguments to the data generating functions
|
|
167
|
+
|
|
168
|
+
Returns
|
|
169
|
+
-------
|
|
170
|
+
dataset : `Any`
|
|
171
|
+
The requested datasets
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
return _DATASETS[name](*args, **kwargs)
|
|
175
|
+
except KeyError as e:
|
|
176
|
+
raise KeyError("dataset not available: {0}".format(name)) from e
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
180
|
+
def list_datasets():
|
|
181
|
+
"""List all the registered datasets.
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
datasets : `list` of `tuple`
|
|
186
|
+
The registered datasets. Element [0] for each tuple is the dataset name
|
|
187
|
+
and element [1] is a short description captured from the docstring.
|
|
188
|
+
"""
|
|
189
|
+
datasets = []
|
|
190
|
+
for d in _DATASETS.keys():
|
|
191
|
+
desc = re.sub(
|
|
192
|
+
r'(Parameters|Returns).*$', '', _DATASETS[d].__doc__.replace(
|
|
193
|
+
'\n', ' '
|
|
194
|
+
)
|
|
195
|
+
).strip()
|
|
196
|
+
datasets.append((d, desc))
|
|
197
|
+
return datasets
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
201
|
+
def help(name):
|
|
202
|
+
"""Central point to get help for the datasets.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
name : `str`
|
|
207
|
+
A name for the dataset that should correspond to a unique key in the
|
|
208
|
+
DATASETS module level dictionary.
|
|
209
|
+
|
|
210
|
+
Returns
|
|
211
|
+
-------
|
|
212
|
+
help : `str`
|
|
213
|
+
The docstring for the function
|
|
214
|
+
"""
|
|
215
|
+
docs = ["Dataset: {0}\n{1}\n\n".format(name, "-" * (len(name) + 9))]
|
|
216
|
+
try:
|
|
217
|
+
docs.extend(
|
|
218
|
+
["{0}\n".format(re.sub(r"^\s{4}", "", i))
|
|
219
|
+
for i in _DATASETS[name].__doc__.split("\n")]
|
|
220
|
+
)
|
|
221
|
+
return "".join(docs)
|
|
222
|
+
except KeyError as e:
|
|
223
|
+
raise KeyError("dataset not available: {0}".format(name)) from e
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
227
|
+
@dataset
|
|
228
|
+
def dummy_data():
|
|
229
|
+
"""A dummy dataset function that returns a small list.
|
|
230
|
+
|
|
231
|
+
Returns
|
|
232
|
+
-------
|
|
233
|
+
data : `list`
|
|
234
|
+
A list of length 3 with ``['A', 'B', 'C']``
|
|
235
|
+
|
|
236
|
+
Notes
|
|
237
|
+
-----
|
|
238
|
+
This function is called ``dummy_data`` and has been decorated with a
|
|
239
|
+
``@dataset`` decorator which makes it available with the
|
|
240
|
+
`example_data.get_data(<NAME>)` function and also
|
|
241
|
+
`example_data.help(<NAME>)` functions.
|
|
242
|
+
"""
|
|
243
|
+
return ['A', 'B', 'C']
|
|
244
|
+
|
|
245
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
246
|
+
@dataset
|
|
247
|
+
def dummy_load_data():
|
|
248
|
+
"""A dummy dataset function that loads a string from a file.
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
str_data : `str`
|
|
253
|
+
A string of data loaded from an example data file.
|
|
254
|
+
|
|
255
|
+
Notes
|
|
256
|
+
-----
|
|
257
|
+
This function is called ``dummy_data`` and has been decorated with a
|
|
258
|
+
``@dataset`` decorator which makes it available with the
|
|
259
|
+
`example_data.get_data(<NAME>)` function and also
|
|
260
|
+
`example_data.help(<NAME>)` functions. The path to this dataset is built
|
|
261
|
+
from ``_ROOT_DATASETS_DIR``.
|
|
262
|
+
"""
|
|
263
|
+
load_path = os.path.join(_ROOT_DATASETS_DIR, "string_data.txt")
|
|
264
|
+
with open(load_path) as data_file:
|
|
265
|
+
return data_file.read().strip()
|
|
266
|
+
|
|
267
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
268
|
+
@dataset
|
|
269
|
+
def list_dicom_paths():
|
|
270
|
+
"""
|
|
271
|
+
Returns the paths to the available DICOM example datasets.
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
paths: dict
|
|
276
|
+
The data names (keys) and paths (values) available in ECGProcess.
|
|
277
|
+
"""
|
|
278
|
+
# paths dictionary
|
|
279
|
+
paths_dict = {
|
|
280
|
+
'example_dicom_1': os.path.join(
|
|
281
|
+
_ROOT_DATASETS_DIR, 'example-DICOM1.dcm'),
|
|
282
|
+
'example_dicom_2': os.path.join(
|
|
283
|
+
_ROOT_DATASETS_DIR, 'example-DICOM2.dcm'),
|
|
284
|
+
}
|
|
285
|
+
# return
|
|
286
|
+
return paths_dict
|
|
287
|
+
|
|
288
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
289
|
+
@dataset
|
|
290
|
+
def list_xml_paths():
|
|
291
|
+
"""
|
|
292
|
+
Returns the paths to available XML and XSD schema paths.
|
|
293
|
+
|
|
294
|
+
Returns
|
|
295
|
+
-------
|
|
296
|
+
paths: dict
|
|
297
|
+
The data names (keys) and paths (values) available in ECGProcess.
|
|
298
|
+
"""
|
|
299
|
+
FILENAME='example-XML1'
|
|
300
|
+
# paths dictionary
|
|
301
|
+
paths_dict = {
|
|
302
|
+
'example_1': os.path.join(
|
|
303
|
+
_ROOT_DATASETS_DIR, FILENAME+'.xml'),
|
|
304
|
+
'example_1_schema': os.path.join(
|
|
305
|
+
_ROOT_DATASETS_DIR, FILENAME+'.xsd'),
|
|
306
|
+
'example_1_schema_error': os.path.join(
|
|
307
|
+
_ROOT_DATASETS_DIR, FILENAME+'_error.xsd'),
|
|
308
|
+
}
|
|
309
|
+
# return
|
|
310
|
+
return paths_dict
|
|
311
|
+
|
|
312
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
313
|
+
@dataset
|
|
314
|
+
def config_file(path=None, text:None | dict[str, list[str]]=None) -> str:
|
|
315
|
+
"""
|
|
316
|
+
Returns a toy example of a data configuration file as a string or uses
|
|
317
|
+
user-supplied text if provided in dictionary format.
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
path : `str`, default None
|
|
322
|
+
An optional path to write the text to disk. Uses utf-8 file encoding.
|
|
323
|
+
user_text : `dict` [`str`, `list` [`str`]], default None
|
|
324
|
+
Configuration content specified as a dictionary. Each key represents a
|
|
325
|
+
header (enclosed in square brackets in the output), and the
|
|
326
|
+
corresponding value is a list of attributes or entries. Tab characters
|
|
327
|
+
in list items are preserved, enabling target-source assignments in the
|
|
328
|
+
generated file.
|
|
329
|
+
|
|
330
|
+
Returns
|
|
331
|
+
-------
|
|
332
|
+
str
|
|
333
|
+
The formatted configuration file.
|
|
334
|
+
|
|
335
|
+
Examples
|
|
336
|
+
--------
|
|
337
|
+
>>> custom_text = {
|
|
338
|
+
... "CustomSection1": ["CustomAttribute1", "CustomAttribute2"],
|
|
339
|
+
... "MetaData": ["CustomData\tCustomValue"],
|
|
340
|
+
... "AdditionalInfo": ["Info1", "Info2"],
|
|
341
|
+
... }
|
|
342
|
+
>>> print(data_configuration(user_text=custom_text))
|
|
343
|
+
[CustomSection1]
|
|
344
|
+
CustomAttribute1
|
|
345
|
+
CustomAttribute2
|
|
346
|
+
[MetaData]
|
|
347
|
+
CustomData CustomValue
|
|
348
|
+
[AdditionalInfo]
|
|
349
|
+
Info1
|
|
350
|
+
Info2
|
|
351
|
+
"""
|
|
352
|
+
# Default configuration
|
|
353
|
+
default_text = {
|
|
354
|
+
"WaveForms": [
|
|
355
|
+
"I\tLead_I_name",
|
|
356
|
+
"aVF\tLead_aVF_name",
|
|
357
|
+
],
|
|
358
|
+
"MedianBeats": [
|
|
359
|
+
"II\tLead_II_name",
|
|
360
|
+
],
|
|
361
|
+
"MetaData": [
|
|
362
|
+
"sampling frequency (original)\tSamplingFrequency",
|
|
363
|
+
"sampling number (waveforms)\tNumberOfWaveformSamples",
|
|
364
|
+
"Birthdate\tPatientBirthDate",
|
|
365
|
+
],
|
|
366
|
+
"OtherData": [
|
|
367
|
+
"ChannelSampleSkew\tChannelSampleSkew",
|
|
368
|
+
],
|
|
369
|
+
}
|
|
370
|
+
# Use default text if user_text is not provided
|
|
371
|
+
config = text or default_text
|
|
372
|
+
# Generate the formatted text
|
|
373
|
+
text_lines = []
|
|
374
|
+
for header, attributes in config.items():
|
|
375
|
+
text_lines.append(f"[{header}]")
|
|
376
|
+
text_lines.extend(attributes)
|
|
377
|
+
text = "\n".join(text_lines)
|
|
378
|
+
# If a file path is provided, write the text to the file
|
|
379
|
+
if path:
|
|
380
|
+
with open(path, "w", encoding="utf-8") as file:
|
|
381
|
+
file.write(text)
|
|
382
|
+
return text
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
386
|
+
@dataset
|
|
387
|
+
def parsed_config():
|
|
388
|
+
"""
|
|
389
|
+
Returns a dict of parsed and mapped config files.
|
|
390
|
+
"""
|
|
391
|
+
# #### XML config file
|
|
392
|
+
config_xml = {
|
|
393
|
+
"WaveForms": [
|
|
394
|
+
"I\tStripData.WaveformData_0.#text",
|
|
395
|
+
"II\tStripData.WaveformData_1.#text",
|
|
396
|
+
],
|
|
397
|
+
"MetaData": [
|
|
398
|
+
"unique identifier\tUID.DICOMStudyUID",
|
|
399
|
+
"number of leads\tRestingECGMeasurements.MedianSamples.NumberOfLeads",
|
|
400
|
+
"resolution unit (waveforms)\tStripData.Resolution.@units",
|
|
401
|
+
"resolution (waveforms)\tStripData.Resolution.#text",
|
|
402
|
+
"resolution unit (medianbeats)\tRestingECGMeasurements.MedianSamples.Resolution.@units",
|
|
403
|
+
"resolution (medianbeats)\tRestingECGMeasurements.MedianSamples.Resolution.#text",
|
|
404
|
+
"sampling frequency (original)\tRestingECGMeasurements.MedianSamples.SampleRate.#text",
|
|
405
|
+
"sampling frequency unit\tRestingECGMeasurements.MedianSamples.SampleRate.@units",
|
|
406
|
+
"sampling number (waveforms)\tStripData.ChannelSampleCountTotal",
|
|
407
|
+
"sampling number (medianbeats)\tRestingECGMeasurements.MedianSamples.ChannelSampleCountTotal",
|
|
408
|
+
"age\tPatientInfo.Age.#text",
|
|
409
|
+
"gender\tPatientInfo.Gender",
|
|
410
|
+
"birthday day\tPatientInfo.BirthDateTime.Day",
|
|
411
|
+
"birthday month\tPatientInfo.BirthDateTime.Month",
|
|
412
|
+
"birthday year\tPatientInfo.BirthDateTime.Year",
|
|
413
|
+
"sysbp unit\tPatientVisit.SysBP.@units",
|
|
414
|
+
"diabpb unit\tPatientVisit.DiaBP.@units",
|
|
415
|
+
"sysbp\tPatientVisit.SysBP.@text",
|
|
416
|
+
"diabpb\tPatientVisit.DiaBP.@text",
|
|
417
|
+
"pacemaker\tPatientInfo.PaceMaker",
|
|
418
|
+
]
|
|
419
|
+
}
|
|
420
|
+
with NamedTemporaryFile("w") as tmp_file:
|
|
421
|
+
_ = config_file(path=tmp_file.name, text=config_xml)
|
|
422
|
+
parser_xml = config_utils.ConfigParser(tmp_file.name)()
|
|
423
|
+
# adding the mapper
|
|
424
|
+
parser_xml.map(mapper=config_utils.DataMap())
|
|
425
|
+
|
|
426
|
+
# #### XML config file 2
|
|
427
|
+
config_xml2 = {
|
|
428
|
+
"WaveForms": [
|
|
429
|
+
"I\tStripData.WaveformData_0.#text",
|
|
430
|
+
"II\tStripData.WaveformData_1.#text",
|
|
431
|
+
],
|
|
432
|
+
"MetaData": [
|
|
433
|
+
"number of leads\tRestingECGMeasurements.MedianSamples.NumberOfLeads",
|
|
434
|
+
"resolution unit (waveforms)\tStripData.Resolution.@units",
|
|
435
|
+
"resolution (waveforms)\tStripData.Resolution.#text",
|
|
436
|
+
"resolution unit (medianbeats)\tRestingECGMeasurements.MedianSamples.Resolution.@units",
|
|
437
|
+
"resolution (medianbeats)\tRestingECGMeasurements.MedianSamples.Resolution.#text",
|
|
438
|
+
"sampling frequency (original)\tRestingECGMeasurements.MedianSamples.SampleRate.#text",
|
|
439
|
+
"sampling frequency unit\tRestingECGMeasurements.MedianSamples.SampleRate.@units",
|
|
440
|
+
"sampling number (waveforms)\tStripData.ChannelSampleCountTotal",
|
|
441
|
+
"sampling number (medianbeats)\tRestingECGMeasurements.MedianSamples.ChannelSampleCountTotal",
|
|
442
|
+
"age\tPatientInfo.Age.#text",
|
|
443
|
+
"gender\tPatientInfo.Gender",
|
|
444
|
+
"birthday day\tPatientInfo.BirthDateTime.Day",
|
|
445
|
+
"birthday month\tPatientInfo.BirthDateTime.Month",
|
|
446
|
+
"birthday year\tPatientInfo.BirthDateTime.Year",
|
|
447
|
+
"sysbp unit\tPatientVisit.SysBP.@units",
|
|
448
|
+
"diabpb unit\tPatientVisit.DiaBP.@units",
|
|
449
|
+
"sysbp\tPatientVisit.SysBP.@text",
|
|
450
|
+
"diabpb\tPatientVisit.DiaBP.@text",
|
|
451
|
+
"pacemaker\tPatientInfo.PaceMaker",
|
|
452
|
+
]
|
|
453
|
+
}
|
|
454
|
+
with NamedTemporaryFile("w") as tmp_file:
|
|
455
|
+
_ = config_file(path=tmp_file.name, text=config_xml2)
|
|
456
|
+
parser_xml2 = config_utils.ConfigParser(tmp_file.name)()
|
|
457
|
+
# adding the mapper
|
|
458
|
+
parser_xml2.map(mapper=config_utils.DataMap())
|
|
459
|
+
|
|
460
|
+
# #### DICOM config file
|
|
461
|
+
leads = [attr for attr in dir(CLeads) if not attr.startswith("__")]
|
|
462
|
+
config_dicom = {
|
|
463
|
+
"WaveForms": [
|
|
464
|
+
f"{getattr(CLeads, lead)}\tWaveformData.ECG_Leads_{idx}"
|
|
465
|
+
for idx, lead in enumerate(leads)],
|
|
466
|
+
"MedianBeats": [
|
|
467
|
+
f"{getattr(CLeads, lead)}\tWaveformData.Median_Beats_{idx}"
|
|
468
|
+
for idx, lead in enumerate(leads)],
|
|
469
|
+
"MetaData": [
|
|
470
|
+
"unique identifier\tSOP Instance UID",
|
|
471
|
+
"number of leads\tWaveform Sequence_0.Number of Waveform Channels",
|
|
472
|
+
"resolution unit (waveforms)\tWaveform Sequence_0.Channel Definition Sequence_0.Channel Sensitivity Units Sequence_0.Code Value",
|
|
473
|
+
"resolution (waveforms)\tWaveform Sequence_0.Channel Definition Sequence_0.Channel Sensitivity",
|
|
474
|
+
"resolution unit (medianbeats)\tWaveform Sequence_1.Channel Definition Sequence_0.Channel Sensitivity Units Sequence_0.Code Value",
|
|
475
|
+
"resolution (medianbeats)\tWaveform Sequence_1.Channel Definition Sequence_0.Channel Sensitivity",
|
|
476
|
+
"sampling frequency (original)\tWaveform Sequence_0.Sampling Frequency",
|
|
477
|
+
"sampling number (waveforms)\tWaveform Sequence_0.Number of Waveform Samples",
|
|
478
|
+
"sampling number (medianbeats)\tWaveform Sequence_1.Number of Waveform Samples",
|
|
479
|
+
"Softwave version\tSoftware Versions",
|
|
480
|
+
"Manufacturer\tManufacturer",
|
|
481
|
+
"Model name\tManufacturer's Model Name",
|
|
482
|
+
] +\
|
|
483
|
+
[
|
|
484
|
+
f"wave_channel_sens_{idx}\tWaveform Sequence_0.Channel Definition Sequence_{idx}.Channel Sensitivity" for idx, _ in enumerate(leads)] +\
|
|
485
|
+
[
|
|
486
|
+
f"wave_channel_correctionfactor_{idx}\tWaveform Sequence_0.Channel Definition Sequence_{idx}.Channel Sensitivity Correction Factor" for idx, _ in enumerate(leads)] +\
|
|
487
|
+
[
|
|
488
|
+
f"wave_channel_baseline_{idx}\tWaveform Sequence_0.Channel Definition Sequence_{idx}.Channel Baseline" for idx, _ in enumerate(leads)]
|
|
489
|
+
}
|
|
490
|
+
with NamedTemporaryFile("w") as tmp_file:
|
|
491
|
+
_ = config_file(path=tmp_file.name, text=config_dicom)
|
|
492
|
+
parser_dicom = config_utils.ConfigParser(tmp_file.name)()
|
|
493
|
+
# adding the mapper
|
|
494
|
+
parser_dicom.map(mapper=config_utils.DataMap())
|
|
495
|
+
|
|
496
|
+
# return
|
|
497
|
+
return {'parsed_dicom1': parser_dicom,
|
|
498
|
+
'parsed_xml1' : parser_xml,
|
|
499
|
+
'parsed_xml2' : parser_xml2,
|
|
500
|
+
}
|
|
501
|
+
|