boris-behav-obs 8.9.16__py3-none-any.whl → 9.7.6__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.
Potentially problematic release.
This version of boris-behav-obs might be problematic. Click here for more details.
- boris/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +36 -39
- boris/add_modifier.py +122 -109
- boris/add_modifier_ui.py +239 -135
- boris/advanced_event_filtering.py +81 -45
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/_latency.py +59 -0
- boris/analysis_plugins/irr_cohen_kappa.py +109 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
- boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
- boris/analysis_plugins/number_of_occurences.py +22 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
- boris/analysis_plugins/time_budget.py +61 -0
- boris/behav_coding_map_creator.py +228 -229
- boris/behavior_binary_table.py +33 -50
- boris/behaviors_coding_map.py +17 -18
- boris/boris_cli.py +6 -25
- boris/cmd_arguments.py +12 -1
- boris/coding_pad.py +42 -49
- boris/config.py +161 -77
- boris/config_file.py +63 -83
- boris/connections.py +112 -57
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2511 -1824
- boris/core_qrc.py +15895 -10185
- boris/core_ui.py +946 -792
- boris/db_functions.py +21 -41
- boris/dev.py +134 -0
- boris/dialog.py +505 -244
- boris/duration_widget.py +15 -20
- boris/edit_event.py +84 -28
- boris/edit_event_ui.py +214 -78
- boris/event_operations.py +517 -415
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +213 -583
- boris/export_observation.py +98 -611
- boris/external_processes.py +156 -97
- boris/geometric_measurement.py +652 -287
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +9 -9
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +325 -0
- boris/irr.py +26 -63
- boris/latency.py +34 -25
- boris/measurement_widget.py +14 -18
- boris/media_file.py +52 -84
- boris/menu_options.py +17 -6
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +7 -9
- boris/mpv.py +1 -0
- boris/mpv2.py +732 -705
- boris/observation.py +655 -310
- boris/observation_operations.py +1036 -404
- boris/observation_ui.py +584 -356
- boris/observations_list.py +71 -53
- boris/otx_parser.py +74 -80
- boris/param_panel.py +31 -16
- boris/param_panel_ui.py +254 -138
- boris/player_dock_widget.py +90 -60
- boris/plot_data_module.py +43 -46
- boris/plot_events.py +127 -90
- boris/plot_events_rt.py +17 -31
- boris/plot_spectrogram_rt.py +95 -30
- boris/plot_waveform_rt.py +32 -21
- boris/plugins.py +431 -0
- boris/portion/__init__.py +18 -8
- boris/portion/const.py +35 -18
- boris/portion/dict.py +5 -5
- boris/portion/func.py +2 -2
- boris/portion/interval.py +21 -41
- boris/portion/io.py +41 -32
- boris/preferences.py +306 -83
- boris/preferences_ui.py +685 -228
- boris/project.py +448 -293
- boris/project_functions.py +689 -254
- boris/project_import_export.py +213 -222
- boris/project_ui.py +674 -438
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +74 -48
- boris/select_observations.py +20 -199
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +53 -37
- boris/subjects_pad.py +6 -9
- boris/synthetic_time_budget.py +45 -28
- boris/time_budget_functions.py +171 -171
- boris/time_budget_widget.py +84 -114
- boris/transitions.py +41 -47
- boris/utilities.py +766 -266
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +125 -28
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +538 -0
- boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
- boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
- {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
- boris/README.TXT +0 -22
- boris/add_modifier.ui +0 -323
- boris/boris_ui.py +0 -886
- boris/converters.ui +0 -289
- boris/core.qrc +0 -35
- boris/core.ui +0 -1543
- boris/edit_event.ui +0 -175
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -850
- boris/observation.ui +0 -773
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -537
- boris/project.ui +0 -1069
- boris/project_server.py +0 -236
- boris/vlc.py +0 -10343
- boris/vlc_local.py +0 -90
- boris_behav_obs-8.9.16.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.9.16.dist-info/METADATA +0 -129
- boris_behav_obs-8.9.16.dist-info/RECORD +0 -108
- boris_behav_obs-8.9.16.dist-info/entry_points.txt +0 -2
- {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/top_level.txt +0 -0
boris/observations_list.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
This program is free software; you can redistribute it and/or modify
|
|
@@ -21,8 +21,8 @@ Copyright 2012-2023 Olivier Friard
|
|
|
21
21
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
from
|
|
25
|
-
from
|
|
24
|
+
from PySide6.QtGui import QColor
|
|
25
|
+
from PySide6.QtWidgets import (
|
|
26
26
|
QTableWidgetItem,
|
|
27
27
|
QLabel,
|
|
28
28
|
QLineEdit,
|
|
@@ -50,12 +50,21 @@ class MyTableWidgetItem(QTableWidgetItem):
|
|
|
50
50
|
|
|
51
51
|
# Qt uses a simple < check for sorting items, override this to use the sortKey
|
|
52
52
|
def __lt__(self, other):
|
|
53
|
-
|
|
53
|
+
if isinstance(self.sortKey, str) and isinstance(other.sortKey, str):
|
|
54
|
+
return self.sortKey.lower() < other.sortKey.lower()
|
|
55
|
+
else:
|
|
56
|
+
return self.sortKey < other.sortKey
|
|
54
57
|
|
|
55
58
|
|
|
56
59
|
class observationsList_widget(QDialog):
|
|
57
|
-
def __init__(
|
|
58
|
-
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
data: list,
|
|
63
|
+
header: list,
|
|
64
|
+
column_type: list,
|
|
65
|
+
not_paired: list = [],
|
|
66
|
+
parent=None,
|
|
67
|
+
):
|
|
59
68
|
super(observationsList_widget, self).__init__(parent)
|
|
60
69
|
|
|
61
70
|
self.data = data
|
|
@@ -78,7 +87,17 @@ class observationsList_widget(QDialog):
|
|
|
78
87
|
|
|
79
88
|
self.cbLogic = QComboBox(self)
|
|
80
89
|
self.cbLogic.addItems(
|
|
81
|
-
[
|
|
90
|
+
[
|
|
91
|
+
"contains",
|
|
92
|
+
"does not contain",
|
|
93
|
+
"=",
|
|
94
|
+
"!=",
|
|
95
|
+
">",
|
|
96
|
+
"<",
|
|
97
|
+
">=",
|
|
98
|
+
"<=",
|
|
99
|
+
"between (use and to separate terms)",
|
|
100
|
+
]
|
|
82
101
|
)
|
|
83
102
|
self.cbLogic.currentIndexChanged.connect(self.view_filter)
|
|
84
103
|
|
|
@@ -105,7 +124,7 @@ class observationsList_widget(QDialog):
|
|
|
105
124
|
self.pbUnSelectAll.clicked.connect(lambda: self.pbSelection_clicked("unselect"))
|
|
106
125
|
hbox2.addWidget(self.pbUnSelectAll)
|
|
107
126
|
|
|
108
|
-
self.pbCancel = QPushButton(
|
|
127
|
+
self.pbCancel = QPushButton(cfg.CANCEL, clicked=self.pbCancel_clicked)
|
|
109
128
|
hbox2.addWidget(self.pbCancel)
|
|
110
129
|
|
|
111
130
|
self.pbOpen = QPushButton("Start", clicked=self.pbOpen_clicked)
|
|
@@ -117,7 +136,7 @@ class observationsList_widget(QDialog):
|
|
|
117
136
|
self.pbEdit = QPushButton("Edit", clicked=self.pbEdit_clicked)
|
|
118
137
|
hbox2.addWidget(self.pbEdit)
|
|
119
138
|
|
|
120
|
-
self.pbOk = QPushButton(
|
|
139
|
+
self.pbOk = QPushButton(cfg.OK, clicked=self.pbOk_clicked)
|
|
121
140
|
hbox2.addWidget(self.pbOk)
|
|
122
141
|
|
|
123
142
|
self.gridLayout.addLayout(hbox2, 3, 0, 1, 3)
|
|
@@ -142,7 +161,6 @@ class observationsList_widget(QDialog):
|
|
|
142
161
|
self.label.setText(f"{self.view.rowCount()} observation{'s' * (self.view.rowCount() > 1)}")
|
|
143
162
|
|
|
144
163
|
def view_doubleClicked(self, index):
|
|
145
|
-
|
|
146
164
|
if self.mode == cfg.MULTIPLE:
|
|
147
165
|
return
|
|
148
166
|
|
|
@@ -155,7 +173,9 @@ class observationsList_widget(QDialog):
|
|
|
155
173
|
return
|
|
156
174
|
|
|
157
175
|
response = dialog.MessageDialog(
|
|
158
|
-
cfg.programName,
|
|
176
|
+
cfg.programName,
|
|
177
|
+
"What do you want to do with this observation?",
|
|
178
|
+
list(commands_index.keys()) + [cfg.CANCEL],
|
|
159
179
|
)
|
|
160
180
|
if response == cfg.CANCEL:
|
|
161
181
|
return
|
|
@@ -166,10 +186,10 @@ class observationsList_widget(QDialog):
|
|
|
166
186
|
"""
|
|
167
187
|
select or unselect all filtered observations
|
|
168
188
|
"""
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
189
|
+
if mode == "select":
|
|
190
|
+
self.view.selectAll()
|
|
191
|
+
if mode == "unselect":
|
|
192
|
+
self.view.clearSelection()
|
|
173
193
|
|
|
174
194
|
def pbCancel_clicked(self):
|
|
175
195
|
self.close()
|
|
@@ -187,7 +207,6 @@ class observationsList_widget(QDialog):
|
|
|
187
207
|
self.done(4)
|
|
188
208
|
|
|
189
209
|
def set_item(self, r, c):
|
|
190
|
-
|
|
191
210
|
if self.column_type[c] == cfg.NUMERIC:
|
|
192
211
|
try:
|
|
193
212
|
item = MyTableWidgetItem(self.data[r][c], float(self.data[r][c]))
|
|
@@ -208,7 +227,7 @@ class observationsList_widget(QDialog):
|
|
|
208
227
|
filter
|
|
209
228
|
"""
|
|
210
229
|
|
|
211
|
-
def str2float(s):
|
|
230
|
+
def str2float(s: str):
|
|
212
231
|
"""
|
|
213
232
|
convert str in float or return str
|
|
214
233
|
"""
|
|
@@ -223,60 +242,60 @@ class observationsList_widget(QDialog):
|
|
|
223
242
|
def not_in(s, lst):
|
|
224
243
|
return s not in lst
|
|
225
244
|
|
|
226
|
-
def equal(s,
|
|
227
|
-
|
|
228
|
-
if type(
|
|
229
|
-
return
|
|
245
|
+
def equal(s, x):
|
|
246
|
+
x_num, s_num = str2float(x), str2float(s)
|
|
247
|
+
if type(x_num) == type(s_num):
|
|
248
|
+
return x_num == s_num
|
|
230
249
|
else:
|
|
231
|
-
return
|
|
250
|
+
return x == s
|
|
232
251
|
|
|
233
|
-
def not_equal(s,
|
|
234
|
-
|
|
235
|
-
if type(
|
|
236
|
-
return
|
|
252
|
+
def not_equal(s, x):
|
|
253
|
+
x_num, s_num = str2float(x), str2float(s)
|
|
254
|
+
if type(x_num) == type(s_num):
|
|
255
|
+
return x_num != s_num
|
|
237
256
|
else:
|
|
238
|
-
return
|
|
257
|
+
return x != s
|
|
239
258
|
|
|
240
|
-
def gt(s,
|
|
241
|
-
|
|
242
|
-
if type(
|
|
243
|
-
return
|
|
259
|
+
def gt(s, x):
|
|
260
|
+
x_num, s_num = str2float(x), str2float(s)
|
|
261
|
+
if type(x_num) == type(s_num):
|
|
262
|
+
return x_num > s_num
|
|
244
263
|
else:
|
|
245
|
-
return
|
|
264
|
+
return x > s
|
|
246
265
|
|
|
247
|
-
def lt(s,
|
|
248
|
-
|
|
249
|
-
if type(
|
|
250
|
-
return
|
|
266
|
+
def lt(s, x):
|
|
267
|
+
x_num, s_num = str2float(x), str2float(s)
|
|
268
|
+
if type(x_num) == type(s_num):
|
|
269
|
+
return x_num < s_num
|
|
251
270
|
else:
|
|
252
|
-
return
|
|
271
|
+
return x < s
|
|
253
272
|
|
|
254
|
-
def gt_or_equal(s,
|
|
255
|
-
|
|
256
|
-
if type(
|
|
257
|
-
return
|
|
273
|
+
def gt_or_equal(s, x):
|
|
274
|
+
x_num, s_num = str2float(x), str2float(s)
|
|
275
|
+
if type(x_num) == type(s_num):
|
|
276
|
+
return x_num >= s_num
|
|
258
277
|
else:
|
|
259
|
-
return
|
|
278
|
+
return x >= s
|
|
260
279
|
|
|
261
|
-
def lt_or_equal(s,
|
|
262
|
-
|
|
263
|
-
if type(
|
|
264
|
-
return
|
|
280
|
+
def lt_or_equal(s, x):
|
|
281
|
+
x_num, s_num = str2float(x), str2float(s)
|
|
282
|
+
if type(x_num) == type(s_num):
|
|
283
|
+
return x_num <= s_num
|
|
265
284
|
else:
|
|
266
|
-
return
|
|
285
|
+
return x <= s
|
|
267
286
|
|
|
268
|
-
def between(s,
|
|
287
|
+
def between(s, x):
|
|
269
288
|
if len(s.split(" AND ")) != 2:
|
|
270
289
|
return None
|
|
271
290
|
s1, s2 = s.split(" AND ")
|
|
272
291
|
s1_num, s2_num = str2float(s1), str2float(s2)
|
|
273
292
|
if type(s1_num) != type(s2_num):
|
|
274
293
|
return None
|
|
275
|
-
|
|
276
|
-
if type(s1_num) == type(
|
|
277
|
-
return
|
|
294
|
+
x_num = str2float(x)
|
|
295
|
+
if type(s1_num) == type(x_num):
|
|
296
|
+
return s1_num <= x_num <= s2_num
|
|
278
297
|
else:
|
|
279
|
-
return
|
|
298
|
+
return s1 <= x <= s2
|
|
280
299
|
|
|
281
300
|
if not self.lineEdit.text():
|
|
282
301
|
self.view.setRowCount(len(self.data))
|
|
@@ -286,7 +305,6 @@ class observationsList_widget(QDialog):
|
|
|
286
305
|
self.view.setItem(r, c, self.set_item(r, c))
|
|
287
306
|
|
|
288
307
|
else:
|
|
289
|
-
|
|
290
308
|
if self.cbLogic.currentText() == "contains":
|
|
291
309
|
logic = in_
|
|
292
310
|
if self.cbLogic.currentText() == "does not contain":
|
boris/otx_parser.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -30,15 +30,16 @@ import re
|
|
|
30
30
|
import zipfile
|
|
31
31
|
import pathlib as pl
|
|
32
32
|
from xml.dom import minidom
|
|
33
|
-
import
|
|
33
|
+
import logging
|
|
34
|
+
from typing import Tuple
|
|
34
35
|
|
|
35
36
|
try:
|
|
36
37
|
from . import config as cfg
|
|
37
|
-
except:
|
|
38
|
+
except Exception:
|
|
38
39
|
import config as cfg
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
def otx_to_boris(file_path: str) -> dict:
|
|
42
|
+
def otx_to_boris(file_path: str) -> Tuple[dict, list]:
|
|
42
43
|
"""
|
|
43
44
|
convert otx/otb/odx file in a BORIS project
|
|
44
45
|
|
|
@@ -49,6 +50,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
49
50
|
|
|
50
51
|
Returns:
|
|
51
52
|
dict: BORIS project
|
|
53
|
+
list: list of errors during importation
|
|
52
54
|
"""
|
|
53
55
|
|
|
54
56
|
if pl.Path(file_path).suffix == ".otb":
|
|
@@ -57,26 +59,27 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
57
59
|
if files_list:
|
|
58
60
|
try:
|
|
59
61
|
file_zip.extract(files_list[0])
|
|
60
|
-
except:
|
|
61
|
-
return {"
|
|
62
|
+
except Exception:
|
|
63
|
+
return {"fatal": True}, ["Error when extracting file from OTB"]
|
|
62
64
|
else:
|
|
63
|
-
return {"
|
|
65
|
+
return {"fatal": True}, ["Error when extracting file"]
|
|
64
66
|
|
|
65
67
|
try:
|
|
66
68
|
xmldoc = minidom.parse(files_list[0])
|
|
67
|
-
except:
|
|
68
|
-
return {"
|
|
69
|
+
except Exception:
|
|
70
|
+
return {"fatal": True}, ["XML parsing error"]
|
|
69
71
|
|
|
70
72
|
elif pl.Path(file_path).suffix in (".odx", ".otx"):
|
|
71
73
|
try:
|
|
72
74
|
xmldoc = minidom.parse(file_path)
|
|
73
|
-
except:
|
|
74
|
-
return {"
|
|
75
|
+
except Exception:
|
|
76
|
+
return {"fatal": True}, ["XML parsing error"]
|
|
75
77
|
|
|
76
78
|
else:
|
|
77
|
-
return {"
|
|
79
|
+
return {"fatal": True}, ["The file must be in OTB, OTX or ODX format"]
|
|
78
80
|
|
|
79
|
-
flag_long_key = False
|
|
81
|
+
flag_long_key: bool = False
|
|
82
|
+
error_list: list = []
|
|
80
83
|
|
|
81
84
|
# metadata
|
|
82
85
|
for item in xmldoc.getElementsByTagName("MET_METADATA"):
|
|
@@ -86,22 +89,18 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
86
89
|
except Exception:
|
|
87
90
|
project_name = ""
|
|
88
91
|
try:
|
|
89
|
-
project_description = re.sub(
|
|
90
|
-
"<[^>]*>", "", metadata.getElementsByTagName("MET_PROJECT_DESCRIPTION")[0].toxml()
|
|
91
|
-
)
|
|
92
|
+
project_description = re.sub("<[^>]*>", "", metadata.getElementsByTagName("MET_PROJECT_DESCRIPTION")[0].toxml())
|
|
92
93
|
except Exception:
|
|
93
94
|
project_description = ""
|
|
94
95
|
|
|
95
96
|
try:
|
|
96
|
-
project_creation_date = re.sub(
|
|
97
|
-
"<[^>]*>", "", metadata.getElementsByTagName("MET_CREATION_DATETIME")[0].toxml()
|
|
98
|
-
)
|
|
97
|
+
project_creation_date = re.sub("<[^>]*>", "", metadata.getElementsByTagName("MET_CREATION_DATETIME")[0].toxml())
|
|
99
98
|
except Exception:
|
|
100
99
|
project_creation_date = ""
|
|
101
100
|
|
|
102
101
|
# modifiers
|
|
103
102
|
modifiers: dict = {}
|
|
104
|
-
modifiers_set = {}
|
|
103
|
+
# modifiers_set = {}
|
|
105
104
|
itemlist = xmldoc.getElementsByTagName("CDS_MODIFIER")
|
|
106
105
|
for item in itemlist:
|
|
107
106
|
modif = minidom.parseString(item.toxml())
|
|
@@ -112,16 +111,16 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
112
111
|
|
|
113
112
|
try:
|
|
114
113
|
modif_parent_id = re.sub("<[^>]*>", "", modif.getElementsByTagName("CDS_ELE_PARENT_ID")[0].toxml())
|
|
115
|
-
except:
|
|
114
|
+
except Exception:
|
|
116
115
|
modif_parent_id = ""
|
|
117
116
|
|
|
118
117
|
try:
|
|
119
118
|
description = re.sub("<[^>]*>", "", modif.getElementsByTagName("CDS_ELE_DESCRIPTION")[0].toxml())
|
|
120
|
-
except:
|
|
119
|
+
except Exception:
|
|
121
120
|
description = ""
|
|
122
121
|
try:
|
|
123
122
|
key = re.sub("<[^>]*>", "", modif.getElementsByTagName("CDS_ELE_START_KEYCODE")[0].toxml())
|
|
124
|
-
except:
|
|
123
|
+
except Exception:
|
|
125
124
|
key = ""
|
|
126
125
|
|
|
127
126
|
if modif_parent_id:
|
|
@@ -132,8 +131,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
132
131
|
flag_long_key = True
|
|
133
132
|
modifiers[modif_id] = {"set_name": modif_code, "key": key, "description": description, "values": []}
|
|
134
133
|
|
|
135
|
-
|
|
136
|
-
pprint.pprint(modifiers)
|
|
134
|
+
logging.debug(modifiers)
|
|
137
135
|
|
|
138
136
|
# connect modifiers to behaviors
|
|
139
137
|
connections: dict = {}
|
|
@@ -143,8 +141,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
143
141
|
connections[item.attributes["CDS_ELEMENT_ID"].value] = []
|
|
144
142
|
connections[item.attributes["CDS_ELEMENT_ID"].value].append(item.attributes["CDS_MODIFIER_ID"].value)
|
|
145
143
|
|
|
146
|
-
|
|
147
|
-
pprint.pprint(connections)
|
|
144
|
+
logging.debug(connections)
|
|
148
145
|
|
|
149
146
|
# behaviors
|
|
150
147
|
behaviors: dict = {}
|
|
@@ -161,40 +158,37 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
161
158
|
|
|
162
159
|
try:
|
|
163
160
|
description = re.sub("<[^>]*>", "", behav.getElementsByTagName("CDS_ELE_DESCRIPTION")[0].toxml())
|
|
164
|
-
except:
|
|
161
|
+
except Exception:
|
|
165
162
|
description = ""
|
|
166
163
|
try:
|
|
167
164
|
key = re.sub("<[^>]*>", "", behav.getElementsByTagName("CDS_ELE_START_KEYCODE")[0].toxml())
|
|
168
|
-
except:
|
|
165
|
+
except Exception:
|
|
169
166
|
key = ""
|
|
170
167
|
|
|
171
168
|
try:
|
|
172
169
|
stop_key = re.sub("<[^>]*>", "", behav.getElementsByTagName("CDS_ELE_STOP_KEYCODE")[0].toxml())
|
|
173
|
-
except:
|
|
170
|
+
except Exception:
|
|
174
171
|
stop_key = ""
|
|
175
172
|
|
|
176
173
|
try:
|
|
177
174
|
parent_name = re.sub("<[^>]*>", "", behav.getElementsByTagName("CDS_ELE_PARENT_NAME")[0].toxml())
|
|
178
|
-
except:
|
|
175
|
+
except Exception:
|
|
179
176
|
parent_name = ""
|
|
180
177
|
|
|
181
178
|
try:
|
|
182
179
|
mutually_exclusive = re.sub("<[^>]*>", "", behav.getElementsByTagName("CDS_ELE_MUT_EXCLUSIVE")[0].toxml())
|
|
183
|
-
except:
|
|
180
|
+
except Exception:
|
|
184
181
|
mutually_exclusive = ""
|
|
185
182
|
|
|
186
183
|
if mutually_exclusive == "Y" and parent_name:
|
|
187
184
|
mutually_exclusive_list.append(behav_code)
|
|
188
185
|
|
|
189
186
|
if behav_id in connections:
|
|
190
|
-
|
|
191
187
|
modifier_sets = [modifiers[modifier_set]["set_name"] for modifier_set in connections[behav_id]]
|
|
192
|
-
print(f"{modifier_sets=}")
|
|
193
188
|
else:
|
|
194
189
|
modifier_sets = []
|
|
195
190
|
|
|
196
191
|
if parent_name: # behavior
|
|
197
|
-
|
|
198
192
|
if (not key or len(key) > 1) and stop_key:
|
|
199
193
|
key = stop_key
|
|
200
194
|
|
|
@@ -213,7 +207,6 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
213
207
|
behaviors_list.append(behav_code)
|
|
214
208
|
|
|
215
209
|
else: # behavioral category
|
|
216
|
-
|
|
217
210
|
behav_category.append(behav_code)
|
|
218
211
|
|
|
219
212
|
behaviors_boris: dict = {}
|
|
@@ -243,7 +236,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
243
236
|
"description": modifiers[modif_key]["description"],
|
|
244
237
|
}
|
|
245
238
|
|
|
246
|
-
|
|
239
|
+
logging.debug(behaviors_boris)
|
|
247
240
|
|
|
248
241
|
# subjects
|
|
249
242
|
subjects = {}
|
|
@@ -253,11 +246,11 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
253
246
|
subject_name = re.sub("<[^>]*>", "", subject.getElementsByTagName("CDS_ELE_NAME")[0].toxml())
|
|
254
247
|
try:
|
|
255
248
|
key = re.sub("<[^>]*>", "", subject.getElementsByTagName("CDS_ELE_START_KEYCODE")[0].toxml())
|
|
256
|
-
except:
|
|
249
|
+
except Exception:
|
|
257
250
|
key = ""
|
|
258
251
|
try:
|
|
259
252
|
parent_name = re.sub("<[^>]*>", "", subject.getElementsByTagName("CDS_ELE_PARENT_NAME")[0].toxml())
|
|
260
|
-
except:
|
|
253
|
+
except Exception:
|
|
261
254
|
parent_name = ""
|
|
262
255
|
|
|
263
256
|
if parent_name:
|
|
@@ -270,7 +263,6 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
270
263
|
variables = {}
|
|
271
264
|
itemlist = xmldoc.getElementsByTagName("VL_VARIABLE")
|
|
272
265
|
for item in itemlist:
|
|
273
|
-
|
|
274
266
|
variable = minidom.parseString(item.toxml())
|
|
275
267
|
|
|
276
268
|
variable_label = re.sub("<[^>]*>", "", variable.getElementsByTagName("VL_LABEL")[0].toxml())
|
|
@@ -293,7 +285,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
293
285
|
|
|
294
286
|
try:
|
|
295
287
|
variable_description = re.sub("<[^>]*>", "", modif.getElementsByTagName("VL_DESCRIPTION")[0].toxml())
|
|
296
|
-
except:
|
|
288
|
+
except Exception:
|
|
297
289
|
variable_description = ""
|
|
298
290
|
|
|
299
291
|
try:
|
|
@@ -303,7 +295,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
303
295
|
values_list.append(re.sub("<[^>]*>", "", value.toxml()))
|
|
304
296
|
values_str = ",".join(values_list)
|
|
305
297
|
|
|
306
|
-
except:
|
|
298
|
+
except Exception:
|
|
307
299
|
values_str = ""
|
|
308
300
|
|
|
309
301
|
variables[variable_id] = {
|
|
@@ -331,16 +323,10 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
331
323
|
|
|
332
324
|
observations = xmldoc.getElementsByTagName("OBS_OBSERVATION")
|
|
333
325
|
|
|
334
|
-
"""
|
|
335
|
-
print(f"{len(observations)=}")
|
|
336
|
-
print()
|
|
337
|
-
"""
|
|
338
|
-
|
|
339
326
|
for OBS_OBSERVATION in observations:
|
|
340
327
|
# OBS_OBSERVATION = minidom.parseString(OBS_OBSERVATION.toxml())
|
|
341
328
|
|
|
342
329
|
obs_id = OBS_OBSERVATION.getAttribute("NAME")
|
|
343
|
-
"""print(f"{obs_id=}")"""
|
|
344
330
|
|
|
345
331
|
project[cfg.OBSERVATIONS][obs_id] = dict(
|
|
346
332
|
{
|
|
@@ -362,27 +348,24 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
362
348
|
|
|
363
349
|
OBS_EVENT_LOGS = OBS_OBSERVATION.getElementsByTagName("OBS_EVENT_LOGS")[0]
|
|
364
350
|
|
|
365
|
-
# print(f"{OBS_EVENT_LOGS=}")
|
|
366
|
-
|
|
367
351
|
for OBS_EVENT_LOG in OBS_EVENT_LOGS.getElementsByTagName("OBS_EVENT_LOG"):
|
|
368
|
-
|
|
369
352
|
CREATION_DATETIME = OBS_EVENT_LOG.getAttribute("CREATION_DATETIME")
|
|
370
353
|
|
|
371
|
-
CREATION_DATETIME = CREATION_DATETIME.replace(" ", "T").split(".")[0]
|
|
354
|
+
CREATION_DATETIME = CREATION_DATETIME.replace(" ", "T") # .split(".")[0]
|
|
372
355
|
|
|
373
|
-
|
|
356
|
+
logging.debug(f"{CREATION_DATETIME=}") # ex: 2022-05-18 10:04:09.474512"""
|
|
374
357
|
|
|
375
358
|
project[cfg.OBSERVATIONS][obs_id]["date"] = CREATION_DATETIME
|
|
376
359
|
|
|
377
360
|
for event in OBS_EVENT_LOG.getElementsByTagName("OBS_EVENT"):
|
|
378
|
-
|
|
379
361
|
OBS_EVENT_TIMESTAMP = event.getElementsByTagName("OBS_EVENT_TIMESTAMP")[0].childNodes[0].data
|
|
380
362
|
|
|
381
|
-
day_timestamp = dt.datetime.strptime(OBS_EVENT_TIMESTAMP.split(" ")[0], "%Y-%m-%d").timestamp()
|
|
382
|
-
|
|
383
363
|
full_timestamp = dt.datetime.strptime(OBS_EVENT_TIMESTAMP, "%Y-%m-%d %H:%M:%S.%f").timestamp()
|
|
364
|
+
logging.debug(f"{full_timestamp=}")
|
|
384
365
|
|
|
385
|
-
|
|
366
|
+
# day_timestamp = dt.datetime.strptime(OBS_EVENT_TIMESTAMP.split(" ")[0], "%Y-%m-%d").timestamp()
|
|
367
|
+
# timestamp = dec(str(round(full_timestamp - day_timestamp, 3)))
|
|
368
|
+
timestamp = dec(full_timestamp).quantize(dec(".001"))
|
|
386
369
|
|
|
387
370
|
try:
|
|
388
371
|
OBS_EVENT_SUBJECT = event.getElementsByTagName("OBS_EVENT_SUBJECT")[0].getAttribute("NAME")
|
|
@@ -390,26 +373,34 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
390
373
|
OBS_EVENT_SUBJECT = ""
|
|
391
374
|
|
|
392
375
|
OBS_EVENT_BEHAVIOR = event.getElementsByTagName("OBS_EVENT_BEHAVIOR")[0].getAttribute("NAME")
|
|
376
|
+
logging.debug(f"{OBS_EVENT_BEHAVIOR=}")
|
|
377
|
+
if not OBS_EVENT_BEHAVIOR:
|
|
378
|
+
logging.warning(f"Behavior missing in observation {obs_id} at {timestamp}")
|
|
379
|
+
error_list.append(f"Behavior missing in observation {obs_id} at {timestamp}")
|
|
380
|
+
continue
|
|
393
381
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
382
|
+
# modifier
|
|
383
|
+
try:
|
|
384
|
+
OBS_EVENT_BEHAVIOR_MODIFIER = (
|
|
385
|
+
event.getElementsByTagName("OBS_EVENT_BEHAVIOR")[0]
|
|
386
|
+
.getElementsByTagName("OBS_EVENT_BEHAVIOR_MODIFIER")[0]
|
|
387
|
+
.childNodes[0]
|
|
388
|
+
.data
|
|
389
|
+
)
|
|
390
|
+
except Exception:
|
|
391
|
+
OBS_EVENT_BEHAVIOR_MODIFIER: str = ""
|
|
400
392
|
|
|
393
|
+
# comment
|
|
401
394
|
try:
|
|
402
|
-
OBS_EVENT_COMMENT = event.getElementsByTagName("OBS_EVENT_COMMENT")[0].childNodes[0].data
|
|
395
|
+
OBS_EVENT_COMMENT: str = event.getElementsByTagName("OBS_EVENT_COMMENT")[0].childNodes[0].data
|
|
403
396
|
except Exception:
|
|
404
|
-
OBS_EVENT_COMMENT = ""
|
|
397
|
+
OBS_EVENT_COMMENT: str = ""
|
|
405
398
|
|
|
406
|
-
""
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
print(f"{OBS_EVENT_COMMENT=}")
|
|
412
|
-
"""
|
|
399
|
+
logging.debug(f"{timestamp=}")
|
|
400
|
+
logging.debug(f"{OBS_EVENT_SUBJECT=}")
|
|
401
|
+
logging.debug(f"{OBS_EVENT_BEHAVIOR=}")
|
|
402
|
+
logging.debug(f"{OBS_EVENT_BEHAVIOR_MODIFIER=}")
|
|
403
|
+
logging.debug(f"{OBS_EVENT_COMMENT=}")
|
|
413
404
|
|
|
414
405
|
project[cfg.OBSERVATIONS][obs_id][cfg.EVENTS].append(
|
|
415
406
|
[
|
|
@@ -421,10 +412,6 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
421
412
|
]
|
|
422
413
|
)
|
|
423
414
|
|
|
424
|
-
# print(80 * "-")
|
|
425
|
-
|
|
426
|
-
# print(80 * "=")
|
|
427
|
-
|
|
428
415
|
project[cfg.PROJECT_NAME] = project_name
|
|
429
416
|
project[cfg.PROJECT_DATE] = project_creation_date.replace(" ", "T")
|
|
430
417
|
project[cfg.ETHOGRAM] = behaviors_boris
|
|
@@ -434,15 +421,22 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
434
421
|
project[cfg.INDEPENDENT_VARIABLES] = variables_boris
|
|
435
422
|
|
|
436
423
|
if flag_long_key:
|
|
437
|
-
|
|
424
|
+
error_list.append("The keys longer than one char were deleted.")
|
|
425
|
+
logging.debug("The keys longer than one char were deleted.")
|
|
438
426
|
|
|
439
|
-
return project
|
|
427
|
+
return project, error_list
|
|
440
428
|
|
|
441
429
|
|
|
442
430
|
if __name__ == "__main__":
|
|
443
431
|
import sys
|
|
444
432
|
import pprint
|
|
445
433
|
|
|
446
|
-
|
|
434
|
+
logging.basicConfig(
|
|
435
|
+
format="%(asctime)s,%(msecs)d %(module)s l.%(lineno)d %(levelname)s %(message)s",
|
|
436
|
+
datefmt="%H:%M:%S",
|
|
437
|
+
level=logging.DEBUG,
|
|
438
|
+
)
|
|
439
|
+
project, errors = otx_to_boris(sys.argv[1])
|
|
447
440
|
|
|
448
|
-
|
|
441
|
+
pprint.pprint(project)
|
|
442
|
+
pprint.pprint(errors)
|