boris-behav-obs 8.12__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 +28 -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 +141 -65
- boris/config_file.py +58 -67
- boris/connections.py +107 -61
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2373 -1786
- boris/core_qrc.py +15895 -10743
- boris/core_ui.py +943 -798
- boris/db_functions.py +17 -42
- boris/dev.py +109 -8
- boris/dialog.py +482 -236
- boris/duration_widget.py +9 -14
- boris/edit_event.py +61 -31
- boris/edit_event_ui.py +208 -97
- boris/event_operations.py +408 -293
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +184 -223
- boris/export_observation.py +74 -100
- boris/external_processes.py +123 -98
- boris/geometric_measurement.py +644 -290
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +4 -4
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +325 -0
- boris/irr.py +20 -57
- boris/latency.py +31 -24
- boris/measurement_widget.py +14 -18
- boris/media_file.py +17 -19
- 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 +533 -221
- boris/observation_operations.py +1025 -390
- boris/observation_ui.py +572 -362
- boris/observations_list.py +71 -53
- boris/otx_parser.py +74 -68
- 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 +25 -33
- 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 +684 -227
- boris/project.py +448 -293
- boris/project_functions.py +671 -238
- 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 -198
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +52 -35
- 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 +627 -236
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +95 -29
- 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.12.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/converters.ui +0 -289
- boris/core.qrc +0 -36
- boris/core.ui +0 -1556
- boris/edit_event.ui +0 -233
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -850
- boris/observation.ui +0 -814
- 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.12.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.12.dist-info/METADATA +0 -128
- boris_behav_obs-8.12.dist-info/RECORD +0 -108
- boris_behav_obs-8.12.dist-info/entry_points.txt +0 -3
- {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.12.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,26 +158,26 @@ 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:
|
|
@@ -192,7 +189,6 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
192
189
|
modifier_sets = []
|
|
193
190
|
|
|
194
191
|
if parent_name: # behavior
|
|
195
|
-
|
|
196
192
|
if (not key or len(key) > 1) and stop_key:
|
|
197
193
|
key = stop_key
|
|
198
194
|
|
|
@@ -211,7 +207,6 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
211
207
|
behaviors_list.append(behav_code)
|
|
212
208
|
|
|
213
209
|
else: # behavioral category
|
|
214
|
-
|
|
215
210
|
behav_category.append(behav_code)
|
|
216
211
|
|
|
217
212
|
behaviors_boris: dict = {}
|
|
@@ -241,7 +236,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
241
236
|
"description": modifiers[modif_key]["description"],
|
|
242
237
|
}
|
|
243
238
|
|
|
244
|
-
|
|
239
|
+
logging.debug(behaviors_boris)
|
|
245
240
|
|
|
246
241
|
# subjects
|
|
247
242
|
subjects = {}
|
|
@@ -251,11 +246,11 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
251
246
|
subject_name = re.sub("<[^>]*>", "", subject.getElementsByTagName("CDS_ELE_NAME")[0].toxml())
|
|
252
247
|
try:
|
|
253
248
|
key = re.sub("<[^>]*>", "", subject.getElementsByTagName("CDS_ELE_START_KEYCODE")[0].toxml())
|
|
254
|
-
except:
|
|
249
|
+
except Exception:
|
|
255
250
|
key = ""
|
|
256
251
|
try:
|
|
257
252
|
parent_name = re.sub("<[^>]*>", "", subject.getElementsByTagName("CDS_ELE_PARENT_NAME")[0].toxml())
|
|
258
|
-
except:
|
|
253
|
+
except Exception:
|
|
259
254
|
parent_name = ""
|
|
260
255
|
|
|
261
256
|
if parent_name:
|
|
@@ -268,7 +263,6 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
268
263
|
variables = {}
|
|
269
264
|
itemlist = xmldoc.getElementsByTagName("VL_VARIABLE")
|
|
270
265
|
for item in itemlist:
|
|
271
|
-
|
|
272
266
|
variable = minidom.parseString(item.toxml())
|
|
273
267
|
|
|
274
268
|
variable_label = re.sub("<[^>]*>", "", variable.getElementsByTagName("VL_LABEL")[0].toxml())
|
|
@@ -291,7 +285,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
291
285
|
|
|
292
286
|
try:
|
|
293
287
|
variable_description = re.sub("<[^>]*>", "", modif.getElementsByTagName("VL_DESCRIPTION")[0].toxml())
|
|
294
|
-
except:
|
|
288
|
+
except Exception:
|
|
295
289
|
variable_description = ""
|
|
296
290
|
|
|
297
291
|
try:
|
|
@@ -301,7 +295,7 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
301
295
|
values_list.append(re.sub("<[^>]*>", "", value.toxml()))
|
|
302
296
|
values_str = ",".join(values_list)
|
|
303
297
|
|
|
304
|
-
except:
|
|
298
|
+
except Exception:
|
|
305
299
|
values_str = ""
|
|
306
300
|
|
|
307
301
|
variables[variable_id] = {
|
|
@@ -355,24 +349,23 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
355
349
|
OBS_EVENT_LOGS = OBS_OBSERVATION.getElementsByTagName("OBS_EVENT_LOGS")[0]
|
|
356
350
|
|
|
357
351
|
for OBS_EVENT_LOG in OBS_EVENT_LOGS.getElementsByTagName("OBS_EVENT_LOG"):
|
|
358
|
-
|
|
359
352
|
CREATION_DATETIME = OBS_EVENT_LOG.getAttribute("CREATION_DATETIME")
|
|
360
353
|
|
|
361
|
-
CREATION_DATETIME = CREATION_DATETIME.replace(" ", "T").split(".")[0]
|
|
354
|
+
CREATION_DATETIME = CREATION_DATETIME.replace(" ", "T") # .split(".")[0]
|
|
362
355
|
|
|
363
|
-
|
|
356
|
+
logging.debug(f"{CREATION_DATETIME=}") # ex: 2022-05-18 10:04:09.474512"""
|
|
364
357
|
|
|
365
358
|
project[cfg.OBSERVATIONS][obs_id]["date"] = CREATION_DATETIME
|
|
366
359
|
|
|
367
360
|
for event in OBS_EVENT_LOG.getElementsByTagName("OBS_EVENT"):
|
|
368
|
-
|
|
369
361
|
OBS_EVENT_TIMESTAMP = event.getElementsByTagName("OBS_EVENT_TIMESTAMP")[0].childNodes[0].data
|
|
370
362
|
|
|
371
|
-
day_timestamp = dt.datetime.strptime(OBS_EVENT_TIMESTAMP.split(" ")[0], "%Y-%m-%d").timestamp()
|
|
372
|
-
|
|
373
363
|
full_timestamp = dt.datetime.strptime(OBS_EVENT_TIMESTAMP, "%Y-%m-%d %H:%M:%S.%f").timestamp()
|
|
364
|
+
logging.debug(f"{full_timestamp=}")
|
|
374
365
|
|
|
375
|
-
|
|
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"))
|
|
376
369
|
|
|
377
370
|
try:
|
|
378
371
|
OBS_EVENT_SUBJECT = event.getElementsByTagName("OBS_EVENT_SUBJECT")[0].getAttribute("NAME")
|
|
@@ -380,24 +373,34 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
380
373
|
OBS_EVENT_SUBJECT = ""
|
|
381
374
|
|
|
382
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
|
|
383
381
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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 = ""
|
|
390
392
|
|
|
393
|
+
# comment
|
|
391
394
|
try:
|
|
392
|
-
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
|
|
393
396
|
except Exception:
|
|
394
|
-
OBS_EVENT_COMMENT = ""
|
|
397
|
+
OBS_EVENT_COMMENT: str = ""
|
|
395
398
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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=}")
|
|
401
404
|
|
|
402
405
|
project[cfg.OBSERVATIONS][obs_id][cfg.EVENTS].append(
|
|
403
406
|
[
|
|
@@ -409,10 +412,6 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
409
412
|
]
|
|
410
413
|
)
|
|
411
414
|
|
|
412
|
-
# print(80 * "-")
|
|
413
|
-
|
|
414
|
-
# print(80 * "=")
|
|
415
|
-
|
|
416
415
|
project[cfg.PROJECT_NAME] = project_name
|
|
417
416
|
project[cfg.PROJECT_DATE] = project_creation_date.replace(" ", "T")
|
|
418
417
|
project[cfg.ETHOGRAM] = behaviors_boris
|
|
@@ -422,15 +421,22 @@ def otx_to_boris(file_path: str) -> dict:
|
|
|
422
421
|
project[cfg.INDEPENDENT_VARIABLES] = variables_boris
|
|
423
422
|
|
|
424
423
|
if flag_long_key:
|
|
425
|
-
|
|
424
|
+
error_list.append("The keys longer than one char were deleted.")
|
|
425
|
+
logging.debug("The keys longer than one char were deleted.")
|
|
426
426
|
|
|
427
|
-
return project
|
|
427
|
+
return project, error_list
|
|
428
428
|
|
|
429
429
|
|
|
430
430
|
if __name__ == "__main__":
|
|
431
431
|
import sys
|
|
432
432
|
import pprint
|
|
433
433
|
|
|
434
|
-
|
|
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])
|
|
435
440
|
|
|
436
|
-
|
|
441
|
+
pprint.pprint(project)
|
|
442
|
+
pprint.pprint(errors)
|