pyPhasesRecordloaderProfusion 0.1.0__tar.gz
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.
- pyphasesrecordloaderprofusion-0.1.0/.gitlab-ci.yml +50 -0
- pyphasesrecordloaderprofusion-0.1.0/LICENSE +21 -0
- pyphasesrecordloaderprofusion-0.1.0/MANIFEST.in +3 -0
- pyphasesrecordloaderprofusion-0.1.0/PKG-INFO +67 -0
- pyphasesrecordloaderprofusion-0.1.0/README.md +39 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion/Plugin.py +13 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion/__init__.py +2 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion/config.yaml +93 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion/recordLoaders/ProfusionAnnotationLoader.py +149 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion/recordLoaders/RecordLoaderProfusion.py +92 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion/recordLoaders/__init__.py +0 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion.egg-info/PKG-INFO +67 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion.egg-info/SOURCES.txt +17 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion.egg-info/dependency_links.txt +1 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion.egg-info/requires.txt +3 -0
- pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion.egg-info/top_level.txt +2 -0
- pyphasesrecordloaderprofusion-0.1.0/setup.cfg +4 -0
- pyphasesrecordloaderprofusion-0.1.0/setup.py +25 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
image: python:3.13-alpine
|
|
2
|
+
|
|
3
|
+
variables:
|
|
4
|
+
PROJECTNAME: $CI_PROJECT_NAME
|
|
5
|
+
|
|
6
|
+
stages:
|
|
7
|
+
- test
|
|
8
|
+
- release
|
|
9
|
+
|
|
10
|
+
test:
|
|
11
|
+
stage: test
|
|
12
|
+
cache: {}
|
|
13
|
+
before_script:
|
|
14
|
+
- apk update
|
|
15
|
+
- apk add build-base
|
|
16
|
+
script:
|
|
17
|
+
- pip install -U -r requirements.txt
|
|
18
|
+
- pip install -U coverage
|
|
19
|
+
- python -m coverage run -m unittest discover -s tests -p "test_*.py"
|
|
20
|
+
- python -m coverage report --data-file=.coverage
|
|
21
|
+
- python -m coverage xml --data-file=.coverage -o cov/coverage.xml
|
|
22
|
+
- python -m coverage html --data-file=.coverage -d cov/htmlcov
|
|
23
|
+
only:
|
|
24
|
+
- pushes
|
|
25
|
+
coverage: '/TOTAL.*\s+(\d+\%)/'
|
|
26
|
+
artifacts:
|
|
27
|
+
paths:
|
|
28
|
+
- :./cov/
|
|
29
|
+
reports:
|
|
30
|
+
coverage_report:
|
|
31
|
+
coverage_format: cobertura
|
|
32
|
+
path: cov/coverage.xml
|
|
33
|
+
pypi:
|
|
34
|
+
stage: release
|
|
35
|
+
before_script:
|
|
36
|
+
- apk update
|
|
37
|
+
- apk add build-base
|
|
38
|
+
cache: {}
|
|
39
|
+
variables:
|
|
40
|
+
APP_VERSION: $CI_COMMIT_TAG
|
|
41
|
+
TWINE_NON_INTERACTIVE: "1"
|
|
42
|
+
TWINE_REPOSITORY_URL: "https://upload.pypi.org/legacy/"
|
|
43
|
+
script:
|
|
44
|
+
- sed -i "s/v0.0.0/${APP_VERSION}/g" setup.py
|
|
45
|
+
- pip install -U -r requirements.txt
|
|
46
|
+
- pip install -U 'twine<5.0' pip setuptools
|
|
47
|
+
- python setup.py sdist
|
|
48
|
+
- twine upload dist/*
|
|
49
|
+
rules:
|
|
50
|
+
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 pyPhases
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyPhasesRecordloaderProfusion
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Adds a record loaders to the pyPhases package
|
|
5
|
+
Home-page: https://gitlab.com/tud.ibmt.public/pyphases/pyPhasesRecordloaderProfusion/
|
|
6
|
+
Author: Franz Ehrlich
|
|
7
|
+
Author-email: fehrlichd@gmail.com
|
|
8
|
+
License: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.5
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: pyPhases
|
|
15
|
+
Requires-Dist: pyPhasesRecordloader
|
|
16
|
+
Requires-Dist: pyedflib
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
Dynamic: requires-dist
|
|
26
|
+
Dynamic: requires-python
|
|
27
|
+
Dynamic: summary
|
|
28
|
+
|
|
29
|
+
# Extension for pyPhasesRecordloader
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
In a phase you can access the data through the `RecordLoader`:
|
|
36
|
+
|
|
37
|
+
Add the plugins and config values to your project.yaml::
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
name: ProfusionProject
|
|
41
|
+
plugins:
|
|
42
|
+
- pyPhasesML
|
|
43
|
+
- pyPhasesRecordloaderProfusion
|
|
44
|
+
- pyPhasesRecordloader
|
|
45
|
+
|
|
46
|
+
phases:
|
|
47
|
+
- name: MyPhase
|
|
48
|
+
|
|
49
|
+
config:
|
|
50
|
+
profusion-path: C:/datasets/recordings
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
In a phase (`phases/MyPhase.py`) you can access the records using the `RecordLoader`:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from pyPhasesRecordloader import RecordLoader
|
|
58
|
+
from pyPhases import Phase
|
|
59
|
+
|
|
60
|
+
class MyPhase(Phase):
|
|
61
|
+
def run(self):
|
|
62
|
+
recordIds = recordLoader.getRecordList()
|
|
63
|
+
for recordId in recordIds:
|
|
64
|
+
record = recordLoader.getRecord(recordId)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Run your project with `python -m phases run MyPhase`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Extension for pyPhasesRecordloader
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
In a phase you can access the data through the `RecordLoader`:
|
|
8
|
+
|
|
9
|
+
Add the plugins and config values to your project.yaml::
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
name: ProfusionProject
|
|
13
|
+
plugins:
|
|
14
|
+
- pyPhasesML
|
|
15
|
+
- pyPhasesRecordloaderProfusion
|
|
16
|
+
- pyPhasesRecordloader
|
|
17
|
+
|
|
18
|
+
phases:
|
|
19
|
+
- name: MyPhase
|
|
20
|
+
|
|
21
|
+
config:
|
|
22
|
+
profusion-path: C:/datasets/recordings
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
In a phase (`phases/MyPhase.py`) you can access the records using the `RecordLoader`:
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from pyPhasesRecordloader import RecordLoader
|
|
30
|
+
from pyPhases import Phase
|
|
31
|
+
|
|
32
|
+
class MyPhase(Phase):
|
|
33
|
+
def run(self):
|
|
34
|
+
recordIds = recordLoader.getRecordList()
|
|
35
|
+
for recordId in recordIds:
|
|
36
|
+
record = recordLoader.getRecord(recordId)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Run your project with `python -m phases run MyPhase`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from pyPhases import PluginAdapter
|
|
2
|
+
from pyPhasesRecordloader import RecordLoader
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Plugin(PluginAdapter):
|
|
6
|
+
def initPlugin(self):
|
|
7
|
+
# self.project.loadConfig(self.project.loadConfig(pathlib.Path(__file__).parent.absolute().joinpath("config.yaml")))
|
|
8
|
+
module = "pyPhasesRecordloaderProfusion"
|
|
9
|
+
rlPath = f"{module}.recordLoaders"
|
|
10
|
+
RecordLoader.registerRecordLoader("RecordLoaderProfusion", rlPath)
|
|
11
|
+
RecordLoader.registerRecordLoader("ProfusionAnnotationLoader", rlPath)
|
|
12
|
+
profusionPath = self.getConfig("profusion-path")
|
|
13
|
+
self.project.setConfig("loader.profusion.filePath", profusionPath)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
loader:
|
|
2
|
+
profusion:
|
|
3
|
+
dataBase: Profusion
|
|
4
|
+
dataBaseVersion: 1.0.0 # needs to be raised every time new data is added
|
|
5
|
+
|
|
6
|
+
filePath: ./datasets/recordings
|
|
7
|
+
copyRawDataToLocal: True
|
|
8
|
+
dataIsFinal: False
|
|
9
|
+
|
|
10
|
+
dataset:
|
|
11
|
+
loaderName: RecordLoaderProfusion
|
|
12
|
+
fromFiles: True # if the records are read from files or sql-database
|
|
13
|
+
dataHandler:
|
|
14
|
+
type: folders
|
|
15
|
+
listFilter: null
|
|
16
|
+
canReadRemote: True
|
|
17
|
+
basePath: .
|
|
18
|
+
extensions: [.edf, -nsrr.xml]
|
|
19
|
+
basePathExtensionwise:
|
|
20
|
+
[
|
|
21
|
+
Profusion\polysomnography\edfs\Profusion,
|
|
22
|
+
Profusion\polysomnography\annotations-events-nsrr\Profusion,
|
|
23
|
+
]
|
|
24
|
+
force: False
|
|
25
|
+
idPattern: .*/(.*).edf
|
|
26
|
+
signal-path: "{recordId}/{recordId}.edf"
|
|
27
|
+
annotation-path: "{recordId}/{recordId}.xml"
|
|
28
|
+
metadata-path: "{recordId}/{recordId}.txt"
|
|
29
|
+
filePattern: "*.edf"
|
|
30
|
+
|
|
31
|
+
# the channels that should be extracted from the edf files
|
|
32
|
+
sourceChannels:
|
|
33
|
+
- name: EEG F3-M2 # harmonized label name should be based on EDF+
|
|
34
|
+
type: eeg
|
|
35
|
+
aliases: [F3-M2] # actual names in the different edfs
|
|
36
|
+
- name: EEG C3-M2
|
|
37
|
+
type: eeg
|
|
38
|
+
aliases: [C3-M2]
|
|
39
|
+
- name: EEG O1-M2
|
|
40
|
+
type: eeg
|
|
41
|
+
aliases: [O1-M2]
|
|
42
|
+
- name: EEG F4-M1
|
|
43
|
+
type: eeg
|
|
44
|
+
aliases: [F4-M1]
|
|
45
|
+
- name: EEG C4-M1
|
|
46
|
+
type: eeg
|
|
47
|
+
aliases: [C4-M1]
|
|
48
|
+
- name: EEG O2-M1
|
|
49
|
+
type: eeg
|
|
50
|
+
aliases: [O2-M1]
|
|
51
|
+
- name: EOG ROC-M2
|
|
52
|
+
type: eog
|
|
53
|
+
aliases: [ROC-M2]
|
|
54
|
+
- name: EOG LOC-M2
|
|
55
|
+
type: eog
|
|
56
|
+
aliases: [LOC-M2]
|
|
57
|
+
- name: EMG EMG1-EMG2
|
|
58
|
+
type: emg
|
|
59
|
+
aliases: [EMG1-EMG2]
|
|
60
|
+
- name: ECG
|
|
61
|
+
type: ecg
|
|
62
|
+
- name: EMG Leg/l
|
|
63
|
+
type: emg
|
|
64
|
+
aliases: [Leg/l]
|
|
65
|
+
- name: EMG Leg/r
|
|
66
|
+
type: emg
|
|
67
|
+
aliases: [Leg/r]
|
|
68
|
+
- name: MIC
|
|
69
|
+
type: mic
|
|
70
|
+
- name: Airflow
|
|
71
|
+
type: flow
|
|
72
|
+
- name: Thor
|
|
73
|
+
type: effort
|
|
74
|
+
- name: Abdo
|
|
75
|
+
type: effort
|
|
76
|
+
- name: SpO2
|
|
77
|
+
type: sao2
|
|
78
|
+
- name: Pressure
|
|
79
|
+
type: pressure
|
|
80
|
+
optional: true
|
|
81
|
+
# - name: Pulse
|
|
82
|
+
# type: pulse
|
|
83
|
+
- name: Position
|
|
84
|
+
type: position
|
|
85
|
+
# - name: Light
|
|
86
|
+
# type: light
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
combineChannels: []
|
|
90
|
+
|
|
91
|
+
useLoader: profusion
|
|
92
|
+
profusion-path: datasets/Profusion
|
|
93
|
+
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from pyPhases.util.Logger import classLogger
|
|
4
|
+
from pyPhasesRecordloader import AnnotationInvalid, AnnotationNotFound, Event
|
|
5
|
+
from pyPhasesRecordloader.recordLoaders.XMLAnnotationLoader import XMLAnnotationLoader
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@classLogger
|
|
9
|
+
class ProfusionAnnotationLoader(XMLAnnotationLoader):
|
|
10
|
+
|
|
11
|
+
mapSleep = {
|
|
12
|
+
'9': 'undefined',
|
|
13
|
+
'0': 'W',
|
|
14
|
+
'1': 'N1',
|
|
15
|
+
'2': 'N2',
|
|
16
|
+
'3': 'N3',
|
|
17
|
+
'5': 'R',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
mapEvents = {
|
|
21
|
+
'Central Apnea': 'resp_centralapnea',
|
|
22
|
+
'Obstructive Apnea': 'resp_obstructiveapnea',
|
|
23
|
+
'Mixed Apnea': 'resp_mixedapnea',
|
|
24
|
+
'Hypopnea': 'resp_hypopnea',
|
|
25
|
+
|
|
26
|
+
'Obstructive Hypopnea': 'resp_hypopnea_obstructive',
|
|
27
|
+
'Central Hypopnea': 'resp_hypopnea_central',
|
|
28
|
+
'Mixed Hypopnea': 'hypopnea',
|
|
29
|
+
|
|
30
|
+
'Arousal ()': 'arousal',
|
|
31
|
+
'Arousal (ARO RES)': 'arousal_respiratory',
|
|
32
|
+
'Arousal (ARO Limb)': 'arousal_limb',
|
|
33
|
+
'Arousal (ARO SPONT)': 'arousal_spontaneous',
|
|
34
|
+
|
|
35
|
+
'RERA': 'arousal_rera',
|
|
36
|
+
|
|
37
|
+
'Limb Movement (Left)': 'LegMovement-Left',
|
|
38
|
+
'Limb Movement (Right)': 'egMovement-Right',
|
|
39
|
+
'PLM (Left)': 'PLM-Left',
|
|
40
|
+
'PLM (Right)': 'PLM-Right',
|
|
41
|
+
|
|
42
|
+
'Cheyne Stokes Breathing': 'resp_cheynestokesbreath',
|
|
43
|
+
|
|
44
|
+
'SpO2 desaturation': 'spo2_desaturation',
|
|
45
|
+
|
|
46
|
+
# 'Unsure': '',
|
|
47
|
+
# 'Bradycardia': '',
|
|
48
|
+
# 'Tachycardia': '',
|
|
49
|
+
# 'TcCO2 artifact': '',
|
|
50
|
+
# 'EtCO2 artifact': '',
|
|
51
|
+
# 'Distal pH artifact': '',
|
|
52
|
+
# 'Distal pH': '',
|
|
53
|
+
# 'Proximal pH artifact': '',
|
|
54
|
+
# 'Blood pressure artifact': '',
|
|
55
|
+
# 'Body temperature artifact': '',
|
|
56
|
+
# 'Respiratory Paradox': '',
|
|
57
|
+
# 'Periodic Breathing': '',
|
|
58
|
+
# 'Respiratory artifact': '',
|
|
59
|
+
# 'SpO2 artifact': '',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def getPath(self, xml, path):
|
|
64
|
+
path = "./" + path
|
|
65
|
+
return xml.findall(path)
|
|
66
|
+
|
|
67
|
+
def loadEvents(
|
|
68
|
+
self,
|
|
69
|
+
path,
|
|
70
|
+
eventMap,
|
|
71
|
+
durationChild="Duration",
|
|
72
|
+
startChild="Start",
|
|
73
|
+
typeChild="EventType",
|
|
74
|
+
conceptChild="EventConcept",
|
|
75
|
+
defaultState="ignore",
|
|
76
|
+
minDuration=0,
|
|
77
|
+
replaceName=None,
|
|
78
|
+
):
|
|
79
|
+
tags = self.getPath(self.metaXML, path)
|
|
80
|
+
if tags is None:
|
|
81
|
+
raise AnnotationNotFound(path)
|
|
82
|
+
|
|
83
|
+
events = []
|
|
84
|
+
|
|
85
|
+
lastDefaultEvent = None
|
|
86
|
+
for tag in tags:
|
|
87
|
+
name = tag.find(conceptChild).text
|
|
88
|
+
startValue = float(tag.find(startChild).text)
|
|
89
|
+
if startValue is None:
|
|
90
|
+
raise AnnotationInvalid(path + [startChild])
|
|
91
|
+
|
|
92
|
+
startInSeconds = float(startValue)
|
|
93
|
+
# if the name is in the eventMap it will be added to the annotations
|
|
94
|
+
if name in eventMap:
|
|
95
|
+
event = Event()
|
|
96
|
+
event.start = startInSeconds
|
|
97
|
+
event.manual = True
|
|
98
|
+
eventName = replaceName(tag) if replaceName else eventMap[name]
|
|
99
|
+
|
|
100
|
+
event.name = eventName
|
|
101
|
+
|
|
102
|
+
if durationChild is not None:
|
|
103
|
+
# if there is a duration the event will be saved as as 2 events:
|
|
104
|
+
# startTime, "(eventName"
|
|
105
|
+
# endTime, "eventName)"
|
|
106
|
+
durationValue = float(tag.find(durationChild).text)
|
|
107
|
+
if durationValue is None:
|
|
108
|
+
raise AnnotationInvalid(path + [durationChild])
|
|
109
|
+
|
|
110
|
+
durationInSeconds = float(durationValue)
|
|
111
|
+
if durationInSeconds > minDuration:
|
|
112
|
+
event.duration = durationInSeconds
|
|
113
|
+
events.append(event)
|
|
114
|
+
else:
|
|
115
|
+
# if its without a duration, it is considered a permanent state change
|
|
116
|
+
# that will persist until it is changed again
|
|
117
|
+
if lastDefaultEvent is not None:
|
|
118
|
+
lastDefaultEvent.duration = event.start - lastDefaultEvent.start
|
|
119
|
+
events.append(event)
|
|
120
|
+
lastDefaultEvent = event
|
|
121
|
+
# else:
|
|
122
|
+
# self.logWarning("Event " + name + " not in EventMap.")
|
|
123
|
+
|
|
124
|
+
if lastDefaultEvent is not None and self.lightOn is not None:
|
|
125
|
+
lastDefaultEvent.duration = self.lightOn - lastDefaultEvent.start
|
|
126
|
+
|
|
127
|
+
return events
|
|
128
|
+
|
|
129
|
+
def loadAnnotation(self, xmlFile) -> List[Event]:
|
|
130
|
+
self.loadXmlFile(xmlFile)
|
|
131
|
+
|
|
132
|
+
allEvents = []
|
|
133
|
+
allEvents += self.loadEvents(
|
|
134
|
+
"SleepStages/SleepStage",
|
|
135
|
+
self.mapSleep,
|
|
136
|
+
)
|
|
137
|
+
allEvents += self.loadEvents(
|
|
138
|
+
"ScoredEvents/ScoredEvent",
|
|
139
|
+
self.mapEvents,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# no light annotations ?
|
|
143
|
+
self.lightOff = 0
|
|
144
|
+
self.lightOn = None
|
|
145
|
+
|
|
146
|
+
return allEvents
|
|
147
|
+
|
|
148
|
+
def fillRecord(self, record, xmlFile):
|
|
149
|
+
pass
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from .ProfusionAnnotationLoader import ProfusionAnnotationLoader
|
|
4
|
+
from pyPhasesRecordloader.recordLoaders.EDFRecordLoader import EDFRecordLoader
|
|
5
|
+
from pyPhasesRecordloader.recordLoaders.CSVMetaLoader import CSVMetaLoader
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RecordLoaderProfusion(EDFRecordLoader):
|
|
9
|
+
|
|
10
|
+
def loadAnnotation(self, recordId, fileName, valueMap=None):
|
|
11
|
+
filePath = self.getFilePathAnnotation(recordId)
|
|
12
|
+
annotationLoader = ProfusionAnnotationLoader.load(filePath, valueMap, self.annotationFrequency)
|
|
13
|
+
|
|
14
|
+
return annotationLoader.events
|
|
15
|
+
|
|
16
|
+
def getEventList(self, recordName, targetFrequency=1):
|
|
17
|
+
metaXML = self.getFilePathAnnotation(recordName)
|
|
18
|
+
xmlLoader = ProfusionAnnotationLoader()
|
|
19
|
+
|
|
20
|
+
eventArray = xmlLoader.loadAnnotation(metaXML)
|
|
21
|
+
self.lightOff = xmlLoader.lightOff
|
|
22
|
+
self.lightOn = xmlLoader.lightOn
|
|
23
|
+
|
|
24
|
+
if targetFrequency != 1:
|
|
25
|
+
eventArray = self.updateFrequencyForEventList(eventArray, targetFrequency)
|
|
26
|
+
|
|
27
|
+
return eventArray
|
|
28
|
+
|
|
29
|
+
def getRelevantCols(self):
|
|
30
|
+
return {
|
|
31
|
+
"gender": lambda row: "male" if row["gender"] == 1 else "female",
|
|
32
|
+
"age": "age_s1",
|
|
33
|
+
"bmi": "bmi_s1",
|
|
34
|
+
"tst": "slpprdp", # slptime in v.15
|
|
35
|
+
"sLatency": "slplatp",
|
|
36
|
+
"rLatency": "remlaiip",
|
|
37
|
+
"waso": "waso",
|
|
38
|
+
"sEfficiency": "slpeffp",
|
|
39
|
+
"indexArousal": "ai_all",
|
|
40
|
+
# countArousal
|
|
41
|
+
"ArREMBP": "arrembp",
|
|
42
|
+
"ArREMOP": "arremop",
|
|
43
|
+
"ArNREMBP": "arnrembp",
|
|
44
|
+
"ArNREMOP": "arnremop",
|
|
45
|
+
"ahi": "ahi_a0h4a",
|
|
46
|
+
"bp_diastolic": "diasbp",
|
|
47
|
+
"bp_systolic": "systbp",
|
|
48
|
+
"race": "race",
|
|
49
|
+
# % N1, N2, N3, R
|
|
50
|
+
# therapy / diagnostics
|
|
51
|
+
# Diagnosis
|
|
52
|
+
# PLMSI
|
|
53
|
+
# PLMSIArI
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def getMetaData2(self, recordName, loadMetadataFromCSV=True):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
def getMetaData(self, recordName, loadMetadataFromCSV=True):
|
|
60
|
+
metaData = super().getMetaData(recordName)
|
|
61
|
+
metaData["recordId"] = recordName
|
|
62
|
+
|
|
63
|
+
return metaData
|
|
64
|
+
|
|
65
|
+
def getAllMetaData(self, visit=1):
|
|
66
|
+
relevantCols = self.getRelevantCols()
|
|
67
|
+
csvLoader = CSVMetaLoader(
|
|
68
|
+
f"{self.filePath}/datasets/Profusion{visit}-dataset-0.20.0.csv", idColumn="nsrrid", relevantRows=relevantCols
|
|
69
|
+
)
|
|
70
|
+
csvMetaData = csvLoader.getAllMetaData()
|
|
71
|
+
csvMetaData["countArousal"] = csvMetaData["ArREMBP"] + csvMetaData["ArREMOP"] + csvMetaData["ArNREMBP"] + csvMetaData["ArNREMOP"]
|
|
72
|
+
|
|
73
|
+
# append Profusion1 to recordId
|
|
74
|
+
csvMetaData["recordId"] = f"Profusion{visit}-" + csvMetaData["recordId"].astype(str)
|
|
75
|
+
|
|
76
|
+
return csvMetaData
|
|
77
|
+
|
|
78
|
+
def getSubjectId(self, recordId):
|
|
79
|
+
return recordId
|
|
80
|
+
|
|
81
|
+
def getSessionId(self, recordId):
|
|
82
|
+
return "1"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Compumedics P-series Sleep Monitoring system, version 3
|
|
86
|
+
def getDICOMMetadata(self, recordId):
|
|
87
|
+
return {
|
|
88
|
+
"Manufacturer": "Compumedics",
|
|
89
|
+
"ManufacturerModelName": "Profusion 5",
|
|
90
|
+
"DeviceSerialNumber": "TBD",
|
|
91
|
+
"SoftwareVersions": "Profusion 5",
|
|
92
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyPhasesRecordloaderProfusion
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Adds a record loaders to the pyPhases package
|
|
5
|
+
Home-page: https://gitlab.com/tud.ibmt.public/pyphases/pyPhasesRecordloaderProfusion/
|
|
6
|
+
Author: Franz Ehrlich
|
|
7
|
+
Author-email: fehrlichd@gmail.com
|
|
8
|
+
License: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.5
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: pyPhases
|
|
15
|
+
Requires-Dist: pyPhasesRecordloader
|
|
16
|
+
Requires-Dist: pyedflib
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
Dynamic: requires-dist
|
|
26
|
+
Dynamic: requires-python
|
|
27
|
+
Dynamic: summary
|
|
28
|
+
|
|
29
|
+
# Extension for pyPhasesRecordloader
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
In a phase you can access the data through the `RecordLoader`:
|
|
36
|
+
|
|
37
|
+
Add the plugins and config values to your project.yaml::
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
name: ProfusionProject
|
|
41
|
+
plugins:
|
|
42
|
+
- pyPhasesML
|
|
43
|
+
- pyPhasesRecordloaderProfusion
|
|
44
|
+
- pyPhasesRecordloader
|
|
45
|
+
|
|
46
|
+
phases:
|
|
47
|
+
- name: MyPhase
|
|
48
|
+
|
|
49
|
+
config:
|
|
50
|
+
profusion-path: C:/datasets/recordings
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
In a phase (`phases/MyPhase.py`) you can access the records using the `RecordLoader`:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from pyPhasesRecordloader import RecordLoader
|
|
58
|
+
from pyPhases import Phase
|
|
59
|
+
|
|
60
|
+
class MyPhase(Phase):
|
|
61
|
+
def run(self):
|
|
62
|
+
recordIds = recordLoader.getRecordList()
|
|
63
|
+
for recordId in recordIds:
|
|
64
|
+
record = recordLoader.getRecord(recordId)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Run your project with `python -m phases run MyPhase`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
setup.py
|
|
5
|
+
./.gitlab-ci.yml
|
|
6
|
+
./pyPhasesRecordloaderProfusion/config.yaml
|
|
7
|
+
pyPhasesRecordloaderProfusion/Plugin.py
|
|
8
|
+
pyPhasesRecordloaderProfusion/__init__.py
|
|
9
|
+
pyPhasesRecordloaderProfusion/config.yaml
|
|
10
|
+
pyPhasesRecordloaderProfusion.egg-info/PKG-INFO
|
|
11
|
+
pyPhasesRecordloaderProfusion.egg-info/SOURCES.txt
|
|
12
|
+
pyPhasesRecordloaderProfusion.egg-info/dependency_links.txt
|
|
13
|
+
pyPhasesRecordloaderProfusion.egg-info/requires.txt
|
|
14
|
+
pyPhasesRecordloaderProfusion.egg-info/top_level.txt
|
|
15
|
+
pyPhasesRecordloaderProfusion/recordLoaders/ProfusionAnnotationLoader.py
|
|
16
|
+
pyPhasesRecordloaderProfusion/recordLoaders/RecordLoaderProfusion.py
|
|
17
|
+
pyPhasesRecordloaderProfusion/recordLoaders/__init__.py
|
pyphasesrecordloaderprofusion-0.1.0/pyPhasesRecordloaderProfusion.egg-info/dependency_links.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import setuptools
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r") as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
|
|
6
|
+
setuptools.setup(
|
|
7
|
+
name="pyPhasesRecordloaderProfusion",
|
|
8
|
+
version="v0.1.0"[1:],
|
|
9
|
+
author="Franz Ehrlich",
|
|
10
|
+
author_email="fehrlichd@gmail.com",
|
|
11
|
+
description="Adds a record loaders to the pyPhases package",
|
|
12
|
+
long_description=long_description,
|
|
13
|
+
long_description_content_type="text/markdown",
|
|
14
|
+
url="https://gitlab.com/tud.ibmt.public/pyphases/pyPhasesRecordloaderProfusion/",
|
|
15
|
+
packages=setuptools.find_packages(),
|
|
16
|
+
include_package_data=True,
|
|
17
|
+
package_data={"pyPhasesRecordloaderProfusion": ["**/*.yaml"]},
|
|
18
|
+
classifiers=[
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
],
|
|
22
|
+
install_requires=["pyPhases", "pyPhasesRecordloader", "pyedflib"],
|
|
23
|
+
python_requires=">=3.5",
|
|
24
|
+
license="License :: OSI Approved :: MIT License"
|
|
25
|
+
)
|