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/export_events.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
|
|
|
@@ -38,14 +38,14 @@ from . import project_functions
|
|
|
38
38
|
from . import dialog
|
|
39
39
|
from . import db_functions
|
|
40
40
|
|
|
41
|
-
from
|
|
41
|
+
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QInputDialog
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def export_events_as_behavioral_sequences(self, separated_subjects=False, timed=False):
|
|
45
45
|
"""
|
|
46
46
|
export events from selected observations by subject as behavioral sequences (plain text file)
|
|
47
47
|
behaviors are separated by character specified in self.behav_seq_separator (usually pipe |)
|
|
48
|
-
for use with Behatrix (see
|
|
48
|
+
for use with Behatrix (see https://www.boris.unito.it/pages/behatrix)
|
|
49
49
|
|
|
50
50
|
Args:
|
|
51
51
|
separated_subjects (bool):
|
|
@@ -70,24 +70,24 @@ def export_events_as_behavioral_sequences(self, separated_subjects=False, timed=
|
|
|
70
70
|
return
|
|
71
71
|
|
|
72
72
|
if len(selected_observations) == 1:
|
|
73
|
-
max_media_duration_all_obs, _ = observation_operations.media_duration(
|
|
74
|
-
|
|
75
|
-
)
|
|
76
|
-
start_coding, end_coding, _ = observation_operations.coding_time(
|
|
77
|
-
self.pj[cfg.OBSERVATIONS], selected_observations
|
|
78
|
-
)
|
|
73
|
+
max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
74
|
+
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
75
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
79
76
|
else:
|
|
80
77
|
max_media_duration_all_obs = None
|
|
81
78
|
start_coding, end_coding = dec("NaN"), dec("NaN")
|
|
79
|
+
start_interval, end_interval = None, None
|
|
82
80
|
|
|
83
81
|
parameters = select_subj_behav.choose_obs_subj_behav_category(
|
|
84
82
|
self,
|
|
85
83
|
selected_observations,
|
|
86
84
|
start_coding=start_coding,
|
|
87
85
|
end_coding=end_coding,
|
|
86
|
+
start_interval=start_interval,
|
|
87
|
+
end_interval=end_interval,
|
|
88
88
|
maxTime=max_media_duration_all_obs,
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
show_include_modifiers=True,
|
|
90
|
+
show_exclude_non_coded_behaviors=False,
|
|
91
91
|
n_observations=len(selected_observations),
|
|
92
92
|
)
|
|
93
93
|
|
|
@@ -97,30 +97,28 @@ def export_events_as_behavioral_sequences(self, separated_subjects=False, timed=
|
|
|
97
97
|
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
|
|
98
98
|
return
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
file_name, _ = QFileDialog.getSaveFileName(self, "Export events as behavioral sequences", "", "Text files (*.txt);;All files (*)")
|
|
101
|
+
|
|
102
|
+
if not file_name:
|
|
103
|
+
return
|
|
104
|
+
r, msg = export_observation.observation_to_behavioral_sequences(
|
|
105
|
+
pj=self.pj,
|
|
106
|
+
selected_observations=selected_observations,
|
|
107
|
+
parameters=parameters,
|
|
108
|
+
behaviors_separator=self.behav_seq_separator,
|
|
109
|
+
separated_subjects=separated_subjects,
|
|
110
|
+
timed=timed,
|
|
111
|
+
file_name=file_name,
|
|
102
112
|
)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
separated_subjects=separated_subjects,
|
|
112
|
-
timed=timed,
|
|
113
|
-
file_name=file_name,
|
|
113
|
+
if not r:
|
|
114
|
+
logging.critical(f"Error while exporting events as behavioral sequences: {msg}")
|
|
115
|
+
QMessageBox.critical(
|
|
116
|
+
None,
|
|
117
|
+
cfg.programName,
|
|
118
|
+
f"Error while exporting events as behavioral sequences:<br>{msg}",
|
|
119
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
120
|
+
QMessageBox.NoButton,
|
|
114
121
|
)
|
|
115
|
-
if not r:
|
|
116
|
-
logging.critical(f"Error while exporting events as behavioral sequences: {msg}")
|
|
117
|
-
QMessageBox.critical(
|
|
118
|
-
None,
|
|
119
|
-
cfg.programName,
|
|
120
|
-
f"Error while exporting events as behavioral sequences:<br>{msg}",
|
|
121
|
-
QMessageBox.Ok | QMessageBox.Default,
|
|
122
|
-
QMessageBox.NoButton,
|
|
123
|
-
)
|
|
124
122
|
|
|
125
123
|
|
|
126
124
|
def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
@@ -165,24 +163,24 @@ def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
|
165
163
|
return
|
|
166
164
|
|
|
167
165
|
if len(selected_observations) == 1:
|
|
168
|
-
max_media_duration_all_obs, _ = observation_operations.media_duration(
|
|
169
|
-
|
|
170
|
-
)
|
|
171
|
-
start_coding, end_coding, _ = observation_operations.coding_time(
|
|
172
|
-
self.pj[cfg.OBSERVATIONS], selected_observations
|
|
173
|
-
)
|
|
166
|
+
max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
167
|
+
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
168
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
174
169
|
else:
|
|
175
170
|
max_media_duration_all_obs = None
|
|
176
171
|
start_coding, end_coding = dec("NaN"), dec("NaN")
|
|
172
|
+
start_interval, end_interval = None, None
|
|
177
173
|
|
|
178
174
|
parameters = select_subj_behav.choose_obs_subj_behav_category(
|
|
179
175
|
self,
|
|
180
176
|
selected_observations,
|
|
181
177
|
start_coding=start_coding,
|
|
182
178
|
end_coding=end_coding,
|
|
179
|
+
start_interval=start_interval,
|
|
180
|
+
end_interval=end_interval,
|
|
183
181
|
maxTime=max_media_duration_all_obs,
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
show_include_modifiers=False,
|
|
183
|
+
show_exclude_non_coded_behaviors=False,
|
|
186
184
|
n_observations=len(selected_observations),
|
|
187
185
|
)
|
|
188
186
|
if parameters == {}:
|
|
@@ -203,7 +201,6 @@ def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
|
203
201
|
cfg.RDS,
|
|
204
202
|
)
|
|
205
203
|
if len(selected_observations) > 1: # choose directory for exporting observations
|
|
206
|
-
|
|
207
204
|
item, ok = QInputDialog.getItem(
|
|
208
205
|
self,
|
|
209
206
|
"Export events format",
|
|
@@ -226,8 +223,12 @@ def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
|
226
223
|
return
|
|
227
224
|
|
|
228
225
|
if len(selected_observations) == 1:
|
|
226
|
+
file_dialog_options = QFileDialog.Options()
|
|
227
|
+
file_dialog_options |= QFileDialog.DontConfirmOverwrite
|
|
229
228
|
|
|
230
|
-
file_name, filter_ = QFileDialog().getSaveFileName(
|
|
229
|
+
file_name, filter_ = QFileDialog().getSaveFileName(
|
|
230
|
+
self, "Export events", "", ";;".join(available_formats), options=file_dialog_options
|
|
231
|
+
)
|
|
231
232
|
if not file_name:
|
|
232
233
|
return
|
|
233
234
|
|
|
@@ -235,11 +236,9 @@ def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
|
235
236
|
if pl.Path(file_name).suffix != "." + output_format:
|
|
236
237
|
file_name = str(pl.Path(file_name)) + "." + output_format
|
|
237
238
|
# check if file with new extension already exists
|
|
238
|
-
if pl.Path(file_name).
|
|
239
|
+
if pl.Path(file_name).exists():
|
|
239
240
|
if (
|
|
240
|
-
dialog.MessageDialog(
|
|
241
|
-
cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE]
|
|
242
|
-
)
|
|
241
|
+
dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE])
|
|
243
242
|
== cfg.CANCEL
|
|
244
243
|
):
|
|
245
244
|
return
|
|
@@ -297,21 +296,23 @@ def export_aggregated_events(self):
|
|
|
297
296
|
- select subjects and behaviors
|
|
298
297
|
- export events in aggregated format
|
|
299
298
|
|
|
300
|
-
Formats can be SQL (sql), SDIS (sds)
|
|
299
|
+
Formats can be SQL (sql), SDIS (sds), Tabular format (tsv, csv, ods, xlsx, xls, html) or Pandas dataframe
|
|
301
300
|
"""
|
|
302
301
|
|
|
303
302
|
def fields_type(max_modif_number: int) -> dict:
|
|
304
|
-
|
|
305
303
|
fields_type_dict: dict = {
|
|
306
304
|
"Observation id": str,
|
|
307
305
|
"Observation date": dt.datetime,
|
|
308
306
|
"Description": str,
|
|
309
307
|
"Observation type": str,
|
|
310
308
|
"Source": str,
|
|
311
|
-
"
|
|
312
|
-
"
|
|
313
|
-
"
|
|
309
|
+
"Time offset (s)": str,
|
|
310
|
+
"Coding duration": float,
|
|
311
|
+
"Media duration (s)": str,
|
|
312
|
+
"FPS (frame/s)": str,
|
|
314
313
|
}
|
|
314
|
+
# TODO: "Media duration (s)" and "FPS (frame/s)" can be float for observation from 1 video
|
|
315
|
+
|
|
315
316
|
if cfg.INDEPENDENT_VARIABLES in self.pj:
|
|
316
317
|
for idx in util.sorted_keys(self.pj[cfg.INDEPENDENT_VARIABLES]):
|
|
317
318
|
if self.pj[cfg.INDEPENDENT_VARIABLES][idx]["type"] == "timestamp":
|
|
@@ -331,16 +332,6 @@ def export_aggregated_events(self):
|
|
|
331
332
|
)
|
|
332
333
|
|
|
333
334
|
# max number of modifiers
|
|
334
|
-
"""
|
|
335
|
-
max_modif_number = max(
|
|
336
|
-
[
|
|
337
|
-
len(self.pj[cfg.ETHOGRAM][idx][cfg.MODIFIERS])
|
|
338
|
-
for idx in self.pj[cfg.ETHOGRAM]
|
|
339
|
-
if self.pj[cfg.ETHOGRAM][idx][cfg.BEHAVIOR_CODE] in parameters[cfg.SELECTED_BEHAVIORS]
|
|
340
|
-
]
|
|
341
|
-
)
|
|
342
|
-
"""
|
|
343
|
-
|
|
344
335
|
for i in range(max_modif_number):
|
|
345
336
|
fields_type_dict[f"Modifier #{i + 1}"] = str
|
|
346
337
|
|
|
@@ -362,9 +353,7 @@ def export_aggregated_events(self):
|
|
|
362
353
|
|
|
363
354
|
return fields_type_dict
|
|
364
355
|
|
|
365
|
-
_, selected_observations = select_observations.select_observations2(
|
|
366
|
-
self, cfg.MULTIPLE, "Select observations for exporting events"
|
|
367
|
-
)
|
|
356
|
+
_, selected_observations = select_observations.select_observations2(self, cfg.MULTIPLE, "Select observations for exporting events")
|
|
368
357
|
if not selected_observations:
|
|
369
358
|
return
|
|
370
359
|
|
|
@@ -378,44 +367,40 @@ def export_aggregated_events(self):
|
|
|
378
367
|
return
|
|
379
368
|
|
|
380
369
|
if len(selected_observations) == 1:
|
|
381
|
-
max_media_duration_all_obs, _ = observation_operations.media_duration(
|
|
382
|
-
|
|
383
|
-
)
|
|
384
|
-
start_coding, end_coding, _ = observation_operations.coding_time(
|
|
385
|
-
self.pj[cfg.OBSERVATIONS], selected_observations
|
|
386
|
-
)
|
|
370
|
+
max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
371
|
+
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
372
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
387
373
|
else:
|
|
388
374
|
max_media_duration_all_obs = None
|
|
389
375
|
start_coding, end_coding = dec("NaN"), dec("NaN")
|
|
376
|
+
start_interval, end_interval = None, None
|
|
390
377
|
|
|
391
378
|
parameters = select_subj_behav.choose_obs_subj_behav_category(
|
|
392
379
|
self,
|
|
393
380
|
selected_observations,
|
|
394
381
|
start_coding=start_coding,
|
|
395
382
|
end_coding=end_coding,
|
|
383
|
+
start_interval=start_interval,
|
|
384
|
+
end_interval=end_interval,
|
|
396
385
|
maxTime=max_media_duration_all_obs,
|
|
397
|
-
|
|
398
|
-
|
|
386
|
+
show_include_modifiers=False,
|
|
387
|
+
show_exclude_non_coded_behaviors=False,
|
|
399
388
|
n_observations=len(selected_observations),
|
|
400
389
|
)
|
|
401
390
|
if parameters == {}:
|
|
402
391
|
return
|
|
403
392
|
if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
|
|
404
|
-
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to
|
|
393
|
+
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to export")
|
|
405
394
|
return
|
|
406
395
|
|
|
407
396
|
# check for grouping results
|
|
408
397
|
flag_group = True
|
|
409
398
|
if len(selected_observations) > 1:
|
|
410
399
|
flag_group = (
|
|
411
|
-
dialog.MessageDialog(
|
|
412
|
-
cfg.programName, "Group events from selected observations in one file?", [cfg.YES, cfg.NO]
|
|
413
|
-
)
|
|
414
|
-
== cfg.YES
|
|
400
|
+
dialog.MessageDialog(cfg.programName, "Group events from selected observations in one file?", [cfg.YES, cfg.NO]) == cfg.YES
|
|
415
401
|
)
|
|
416
402
|
|
|
417
403
|
if flag_group:
|
|
418
|
-
|
|
419
404
|
file_formats = (
|
|
420
405
|
cfg.TSV,
|
|
421
406
|
cfg.CSV,
|
|
@@ -430,7 +415,12 @@ def export_aggregated_events(self):
|
|
|
430
415
|
cfg.RDS,
|
|
431
416
|
)
|
|
432
417
|
|
|
433
|
-
|
|
418
|
+
file_dialog_options = QFileDialog.Options()
|
|
419
|
+
file_dialog_options |= QFileDialog.DontConfirmOverwrite
|
|
420
|
+
|
|
421
|
+
fileName, filter_ = QFileDialog().getSaveFileName(
|
|
422
|
+
self, "Export aggregated events", "", ";;".join(file_formats), options=file_dialog_options
|
|
423
|
+
)
|
|
434
424
|
|
|
435
425
|
if not fileName:
|
|
436
426
|
return
|
|
@@ -439,17 +429,11 @@ def export_aggregated_events(self):
|
|
|
439
429
|
if pl.Path(fileName).suffix != "." + outputFormat:
|
|
440
430
|
# check if file with new extension already exists
|
|
441
431
|
fileName = str(pl.Path(fileName)) + "." + outputFormat
|
|
442
|
-
if pl.Path(fileName).
|
|
443
|
-
if (
|
|
444
|
-
dialog.MessageDialog(
|
|
445
|
-
cfg.programName, f"The file {fileName} already exists.", [cfg.CANCEL, cfg.OVERWRITE]
|
|
446
|
-
)
|
|
447
|
-
== cfg.CANCEL
|
|
448
|
-
):
|
|
432
|
+
if pl.Path(fileName).exists():
|
|
433
|
+
if dialog.MessageDialog(cfg.programName, f"The file {fileName} already exists.", [cfg.CANCEL, cfg.OVERWRITE]) == cfg.CANCEL:
|
|
449
434
|
return
|
|
450
435
|
|
|
451
436
|
else: # not grouping
|
|
452
|
-
|
|
453
437
|
file_formats = (
|
|
454
438
|
cfg.TSV,
|
|
455
439
|
cfg.CSV,
|
|
@@ -474,7 +458,7 @@ def export_aggregated_events(self):
|
|
|
474
458
|
if not exportDir:
|
|
475
459
|
return
|
|
476
460
|
|
|
477
|
-
if outputFormat ==
|
|
461
|
+
if outputFormat == cfg.SQL_EXT:
|
|
478
462
|
_, _, conn = db_functions.load_aggregated_events_in_db(
|
|
479
463
|
self.pj, parameters[cfg.SELECTED_SUBJECTS], selected_observations, parameters[cfg.SELECTED_BEHAVIORS]
|
|
480
464
|
)
|
|
@@ -483,7 +467,6 @@ def export_aggregated_events(self):
|
|
|
483
467
|
for line in conn.iterdump():
|
|
484
468
|
f.write(f"{line}\n")
|
|
485
469
|
except Exception:
|
|
486
|
-
|
|
487
470
|
QMessageBox.critical(
|
|
488
471
|
None,
|
|
489
472
|
cfg.programName,
|
|
@@ -495,50 +478,50 @@ def export_aggregated_events(self):
|
|
|
495
478
|
return
|
|
496
479
|
|
|
497
480
|
# compute the maximum number of modifiers
|
|
498
|
-
tot_max_modifiers = 0
|
|
481
|
+
tot_max_modifiers: int = 0
|
|
499
482
|
for obs_id in selected_observations:
|
|
500
483
|
_, max_modifiers = export_observation.export_aggregated_events(self.pj, parameters, obs_id)
|
|
501
484
|
tot_max_modifiers = max(tot_max_modifiers, max_modifiers)
|
|
502
485
|
|
|
486
|
+
logging.debug(f"tot_max_modifiers: {tot_max_modifiers}")
|
|
487
|
+
|
|
503
488
|
data_grouped_obs = tablib.Dataset()
|
|
504
489
|
|
|
505
490
|
mem_command: str = "" # remember user choice when file already exists
|
|
506
491
|
header = list(fields_type(tot_max_modifiers).keys())
|
|
507
492
|
|
|
508
493
|
for obs_id in selected_observations:
|
|
509
|
-
|
|
510
494
|
logging.debug(f"Exporting aggregated events for obs Id: {obs_id}")
|
|
511
495
|
|
|
512
|
-
data_single_obs,
|
|
496
|
+
data_single_obs, _ = export_observation.export_aggregated_events(
|
|
497
|
+
self.pj, parameters, obs_id, force_number_modifiers=tot_max_modifiers
|
|
498
|
+
)
|
|
513
499
|
|
|
514
500
|
try:
|
|
515
501
|
# order by start time
|
|
516
502
|
index = header.index("Start (s)")
|
|
517
503
|
if cfg.NA not in [x[index] for x in list(data_single_obs)]:
|
|
518
504
|
data_single_obs_sorted = tablib.Dataset(
|
|
519
|
-
# *data.sort(col=index),
|
|
520
505
|
*sorted(list(data_single_obs), key=lambda x: float(x[index])),
|
|
521
|
-
headers=list(fields_type(
|
|
506
|
+
headers=list(fields_type(tot_max_modifiers).keys()),
|
|
522
507
|
)
|
|
523
508
|
else:
|
|
524
509
|
# order by image index
|
|
525
510
|
index = header.index("Image index start")
|
|
526
511
|
data_single_obs_sorted = tablib.Dataset(
|
|
527
|
-
# *data.sort(col=index),
|
|
528
512
|
*sorted(list(data_single_obs), key=lambda x: float(x[index])),
|
|
529
|
-
headers=list(fields_type(
|
|
513
|
+
headers=list(fields_type(tot_max_modifiers).keys()),
|
|
530
514
|
)
|
|
531
515
|
except Exception:
|
|
532
516
|
# if error no order
|
|
533
517
|
data_single_obs_sorted = tablib.Dataset(
|
|
534
|
-
# data.sort(col=0), # Observation id
|
|
535
518
|
*list(data_single_obs),
|
|
536
|
-
headers=list(fields_type(
|
|
519
|
+
headers=list(fields_type(tot_max_modifiers).keys()),
|
|
537
520
|
)
|
|
538
521
|
|
|
539
522
|
data_single_obs_sorted.title = obs_id
|
|
540
523
|
|
|
541
|
-
if (not flag_group) and (outputFormat not in (cfg.SDIS_EXT,
|
|
524
|
+
if (not flag_group) and (outputFormat not in (cfg.SDIS_EXT, cfg.TBS_EXT)):
|
|
542
525
|
fileName = f"{pl.Path(exportDir) / util.safeFileName(obs_id)}.{outputFormat}"
|
|
543
526
|
# check if file with new extension already exists
|
|
544
527
|
if mem_command != cfg.OVERWRITE_ALL and pl.Path(fileName).is_file():
|
|
@@ -554,12 +537,12 @@ def export_aggregated_events(self):
|
|
|
554
537
|
if mem_command in (cfg.SKIP, cfg.SKIP_ALL):
|
|
555
538
|
continue
|
|
556
539
|
|
|
557
|
-
r, msg = export_observation.dataset_write(data_single_obs_sorted, fileName, outputFormat, dtype=fields_type)
|
|
540
|
+
r, msg = export_observation.dataset_write(data_single_obs_sorted, fileName, outputFormat, dtype=fields_type(max_modifiers))
|
|
558
541
|
if not r:
|
|
559
|
-
QMessageBox.warning(
|
|
560
|
-
None, cfg.programName, msg, QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton
|
|
561
|
-
)
|
|
542
|
+
QMessageBox.warning(None, cfg.programName, msg, QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
|
|
562
543
|
|
|
544
|
+
"""
|
|
545
|
+
# disabled after introduction of the force_number_modifiers parameter in export_aggregated_events function
|
|
563
546
|
if len(data_single_obs_sorted) and max_modifiers < tot_max_modifiers:
|
|
564
547
|
for i in range(tot_max_modifiers - max_modifiers):
|
|
565
548
|
data_single_obs_sorted.insert_col(
|
|
@@ -567,9 +550,12 @@ def export_aggregated_events(self):
|
|
|
567
550
|
col=[""] * (len(list(data_single_obs_sorted))),
|
|
568
551
|
header=f"Modif #{i}",
|
|
569
552
|
)
|
|
553
|
+
"""
|
|
554
|
+
|
|
570
555
|
data_grouped_obs.extend(data_single_obs_sorted)
|
|
571
556
|
|
|
572
557
|
data_grouped_obs_all = tablib.Dataset(headers=list(fields_type(tot_max_modifiers).keys()))
|
|
558
|
+
|
|
573
559
|
data_grouped_obs_all.extend(data_grouped_obs)
|
|
574
560
|
data_grouped_obs_all.title = "Aggregated events"
|
|
575
561
|
|
|
@@ -613,10 +599,7 @@ def export_aggregated_events(self):
|
|
|
613
599
|
return
|
|
614
600
|
|
|
615
601
|
if outputFormat == cfg.SDIS_EXT: # SDIS format
|
|
616
|
-
out: str = (
|
|
617
|
-
"% SDIS file created by BORIS (www.boris.unito.it) "
|
|
618
|
-
f"at {util.datetime_iso8601(dt.datetime.now())}\nTimed <seconds>;\n"
|
|
619
|
-
)
|
|
602
|
+
out: str = f"% SDIS file created by BORIS (www.boris.unito.it) at {util.datetime_iso8601(dt.datetime.now())}\nTimed <seconds>;\n"
|
|
620
603
|
for obs_id in selected_observations:
|
|
621
604
|
# observation id
|
|
622
605
|
out += "\n<{}>\n".format(obs_id)
|
|
@@ -642,10 +625,7 @@ def export_aggregated_events(self):
|
|
|
642
625
|
fileName = f"{pl.Path(exportDir) / util.safeFileName(obs_id)}.{outputFormat}"
|
|
643
626
|
with open(fileName, "wb") as f:
|
|
644
627
|
f.write(str.encode(out))
|
|
645
|
-
out = (
|
|
646
|
-
"% SDIS file created by BORIS (www.boris.unito.it) "
|
|
647
|
-
f"at {util.datetime_iso8601(dt.datetime.now())}\nTimed <seconds>;\n"
|
|
648
|
-
)
|
|
628
|
+
out = f"% SDIS file created by BORIS (www.boris.unito.it) at {util.datetime_iso8601(dt.datetime.now())}\nTimed <seconds>;\n"
|
|
649
629
|
|
|
650
630
|
if flag_group:
|
|
651
631
|
with open(fileName, "wb") as f:
|
|
@@ -653,7 +633,7 @@ def export_aggregated_events(self):
|
|
|
653
633
|
return
|
|
654
634
|
|
|
655
635
|
if flag_group:
|
|
656
|
-
r, msg = export_observation.dataset_write(data_grouped_obs_all, fileName, outputFormat, dtype=fields_type)
|
|
636
|
+
r, msg = export_observation.dataset_write(data_grouped_obs_all, fileName, outputFormat, dtype=fields_type(max_modifiers))
|
|
657
637
|
if not r:
|
|
658
638
|
QMessageBox.warning(None, cfg.programName, msg, QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
|
|
659
639
|
|
|
@@ -694,13 +674,19 @@ def export_events_as_textgrid(self) -> None:
|
|
|
694
674
|
|
|
695
675
|
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
696
676
|
|
|
677
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
678
|
+
|
|
697
679
|
parameters = select_subj_behav.choose_obs_subj_behav_category(
|
|
698
680
|
self,
|
|
699
681
|
selected_observations,
|
|
700
682
|
start_coding=start_coding,
|
|
701
683
|
end_coding=end_coding,
|
|
702
|
-
|
|
703
|
-
|
|
684
|
+
# start_interval=start_interval,
|
|
685
|
+
# end_interval=end_interval,
|
|
686
|
+
start_interval=None,
|
|
687
|
+
end_interval=None,
|
|
688
|
+
show_include_modifiers=False,
|
|
689
|
+
show_exclude_non_coded_behaviors=False,
|
|
704
690
|
maxTime=max_obs_length,
|
|
705
691
|
n_observations=len(selected_observations),
|
|
706
692
|
)
|
|
@@ -710,13 +696,13 @@ def export_events_as_textgrid(self) -> None:
|
|
|
710
696
|
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to export")
|
|
711
697
|
return
|
|
712
698
|
|
|
713
|
-
export_dir = QFileDialog
|
|
714
|
-
self, "Export events as Praat TextGrid", os.path.expanduser("~"), options=QFileDialog
|
|
699
|
+
export_dir = QFileDialog.getExistingDirectory(
|
|
700
|
+
self, "Export events as Praat TextGrid", os.path.expanduser("~"), options=QFileDialog.ShowDirsOnly
|
|
715
701
|
)
|
|
716
702
|
if not export_dir:
|
|
717
703
|
return
|
|
718
704
|
|
|
719
|
-
mem_command = ""
|
|
705
|
+
mem_command: str = ""
|
|
720
706
|
|
|
721
707
|
# see https://www.fon.hum.uva.nl/praat/manual/TextGrid_file_formats.html
|
|
722
708
|
|
|
@@ -729,12 +715,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
729
715
|
" intervals: size = {intervalsSize}\n"
|
|
730
716
|
)
|
|
731
717
|
|
|
732
|
-
interval_template =
|
|
733
|
-
" intervals [{count}]:\n"
|
|
734
|
-
" xmin = {xmin}\n"
|
|
735
|
-
" xmax = {xmax}\n"
|
|
736
|
-
' text = "{name}"\n'
|
|
737
|
-
)
|
|
718
|
+
interval_template = ' intervals [{count}]:\n xmin = {xmin}\n xmax = {xmax}\n text = "{name}"\n'
|
|
738
719
|
|
|
739
720
|
point_subject_header = (
|
|
740
721
|
" item [{subject_index}]:\n"
|
|
@@ -745,7 +726,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
745
726
|
" points: size = {intervalsSize}\n"
|
|
746
727
|
)
|
|
747
728
|
|
|
748
|
-
point_template =
|
|
729
|
+
point_template = ' points [{count}]:\n number = {number}\n mark = "{mark}"\n'
|
|
749
730
|
|
|
750
731
|
# widget for results
|
|
751
732
|
self.results = dialog.Results_dialog()
|
|
@@ -757,7 +738,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
757
738
|
)
|
|
758
739
|
|
|
759
740
|
if db_connector is None:
|
|
760
|
-
logging.critical(
|
|
741
|
+
logging.critical("Error when loading aggregated events in DB")
|
|
761
742
|
return
|
|
762
743
|
|
|
763
744
|
cursor = db_connector.cursor()
|
|
@@ -765,11 +746,8 @@ def export_events_as_textgrid(self) -> None:
|
|
|
765
746
|
file_count: int = 0
|
|
766
747
|
|
|
767
748
|
for obs_id in selected_observations:
|
|
768
|
-
|
|
769
749
|
if parameters["time"] == cfg.TIME_EVENTS:
|
|
770
|
-
start_coding, end_coding, coding_duration = observation_operations.coding_time(
|
|
771
|
-
self.pj[cfg.OBSERVATIONS], [obs_id]
|
|
772
|
-
)
|
|
750
|
+
start_coding, end_coding, coding_duration = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], [obs_id])
|
|
773
751
|
if start_coding is None and end_coding is None: # no events
|
|
774
752
|
self.results.ptText.appendHtml(f"The observation <b>{obs_id}</b> does not have events.")
|
|
775
753
|
QApplication.processEvents()
|
|
@@ -784,7 +762,6 @@ def export_events_as_textgrid(self) -> None:
|
|
|
784
762
|
max_time = float(end_coding)
|
|
785
763
|
|
|
786
764
|
if parameters["time"] == cfg.TIME_FULL_OBS:
|
|
787
|
-
|
|
788
765
|
if self.pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] == cfg.MEDIA:
|
|
789
766
|
max_media_duration, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], [obs_id])
|
|
790
767
|
min_time = float(0)
|
|
@@ -792,9 +769,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
792
769
|
coding_duration = max_media_duration
|
|
793
770
|
|
|
794
771
|
if self.pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in (cfg.LIVE, cfg.IMAGES):
|
|
795
|
-
start_coding, end_coding, coding_duration = observation_operations.coding_time(
|
|
796
|
-
self.pj[cfg.OBSERVATIONS], [obs_id]
|
|
797
|
-
)
|
|
772
|
+
start_coding, end_coding, coding_duration = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], [obs_id])
|
|
798
773
|
if start_coding is None and end_coding is None: # no events
|
|
799
774
|
self.results.ptText.appendHtml(f"The observation <b>{obs_id}</b> does not have events.")
|
|
800
775
|
QApplication.processEvents()
|
|
@@ -811,7 +786,16 @@ def export_events_as_textgrid(self) -> None:
|
|
|
811
786
|
min_time = float(parameters[cfg.START_TIME])
|
|
812
787
|
max_time = float(parameters[cfg.END_TIME])
|
|
813
788
|
|
|
789
|
+
if parameters["time"] == cfg.TIME_OBS_INTERVAL:
|
|
790
|
+
max_media_duration, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], [obs_id])
|
|
791
|
+
obs_interval = self.pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
|
|
792
|
+
offset = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET])
|
|
793
|
+
min_time = float(obs_interval[0]) + offset
|
|
794
|
+
# Use max media duration for max time if no interval is defined (=0)
|
|
795
|
+
max_time = float(obs_interval[1]) + offset if obs_interval[1] != 0 else float(max_media_duration)
|
|
796
|
+
|
|
814
797
|
# delete events outside time interval
|
|
798
|
+
|
|
815
799
|
cursor.execute(
|
|
816
800
|
"DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)",
|
|
817
801
|
(
|
|
@@ -855,37 +839,30 @@ def export_events_as_textgrid(self) -> None:
|
|
|
855
839
|
|
|
856
840
|
next_obs: bool = False
|
|
857
841
|
|
|
858
|
-
|
|
859
|
-
total_media_duration = round(
|
|
860
|
-
observation_operations.observation_total_length(self.pj[cfg.OBSERVATIONS][obs_id]), 3
|
|
861
|
-
)
|
|
862
|
-
"""
|
|
863
|
-
|
|
842
|
+
# number of items for size parameter
|
|
864
843
|
cursor.execute(
|
|
865
844
|
(
|
|
866
|
-
"SELECT COUNT(
|
|
867
|
-
"WHERE observation = ? AND subject IN ({})
|
|
868
|
-
",".join(["?"] * len(parameters[cfg.SELECTED_SUBJECTS]))
|
|
869
|
-
)
|
|
845
|
+
"SELECT COUNT(*) FROM (SELECT * FROM aggregated_events "
|
|
846
|
+
f"WHERE observation = ? AND subject IN ({','.join(['?'] * len(parameters[cfg.SELECTED_SUBJECTS]))}) GROUP BY subject, behavior) "
|
|
870
847
|
),
|
|
871
848
|
[obs_id] + parameters[cfg.SELECTED_SUBJECTS],
|
|
872
849
|
)
|
|
873
850
|
|
|
874
|
-
|
|
875
|
-
|
|
851
|
+
subjects_num = int(cursor.fetchone()[0])
|
|
852
|
+
subjects_max = max_time
|
|
876
853
|
|
|
877
854
|
out = (
|
|
878
855
|
'File type = "ooTextFile"\n'
|
|
879
856
|
'Object class = "TextGrid"\n'
|
|
880
857
|
"\n"
|
|
881
858
|
f"xmin = 0.0\n"
|
|
882
|
-
f"xmax = {
|
|
859
|
+
f"xmax = {subjects_max}\n"
|
|
883
860
|
"tiers? <exists>\n"
|
|
884
|
-
f"size = {
|
|
861
|
+
f"size = {subjects_num}\n"
|
|
885
862
|
"item []:\n"
|
|
886
863
|
)
|
|
887
864
|
|
|
888
|
-
subject_index = 0
|
|
865
|
+
subject_index: int = 0
|
|
889
866
|
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
890
867
|
if subject not in [
|
|
891
868
|
x[cfg.EVENT_SUBJECT_FIELD_IDX] if x[cfg.EVENT_SUBJECT_FIELD_IDX] else cfg.NO_FOCAL_SUBJECT
|
|
@@ -893,7 +870,8 @@ def export_events_as_textgrid(self) -> None:
|
|
|
893
870
|
]:
|
|
894
871
|
continue
|
|
895
872
|
|
|
896
|
-
intervalsMin
|
|
873
|
+
intervalsMin = min_time
|
|
874
|
+
intervalsMax = max_time
|
|
897
875
|
|
|
898
876
|
# STATE events
|
|
899
877
|
cursor.execute(
|
|
@@ -908,79 +886,70 @@ def export_events_as_textgrid(self) -> None:
|
|
|
908
886
|
{"start": util.float2decimal(r["start"]), "stop": util.float2decimal(r["stop"]), "code": r["behavior"]}
|
|
909
887
|
for r in cursor.fetchall()
|
|
910
888
|
]
|
|
911
|
-
if
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
out += interval_subject_header
|
|
889
|
+
if rows:
|
|
890
|
+
out += interval_subject_header
|
|
915
891
|
|
|
916
|
-
|
|
892
|
+
count = 0
|
|
917
893
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
894
|
+
# check if 1st behavior starts at the beginning
|
|
895
|
+
if rows[0]["start"] > 0:
|
|
896
|
+
count += 1
|
|
897
|
+
out += interval_template.format(count=count, name="null", xmin=0.0, xmax=rows[0]["start"])
|
|
898
|
+
|
|
899
|
+
for idx, row in enumerate(rows):
|
|
900
|
+
# check if events are overlapping
|
|
901
|
+
if (idx + 1 < len(rows)) and (row["stop"] > rows[idx + 1]["start"]):
|
|
902
|
+
self.results.ptText.appendHtml(
|
|
903
|
+
(
|
|
904
|
+
f"The events overlap for subject <b>{subject}</b> in the observation <b>{obs_id}</b>. "
|
|
905
|
+
"It is not possible to create the Praat TextGrid file."
|
|
906
|
+
)
|
|
907
|
+
)
|
|
908
|
+
QApplication.processEvents()
|
|
922
909
|
|
|
923
|
-
|
|
910
|
+
next_obs = True
|
|
911
|
+
break
|
|
924
912
|
|
|
925
|
-
|
|
926
|
-
if (idx + 1 < len(rows)) and (row["stop"] > rows[idx + 1]["start"]):
|
|
913
|
+
count += 1
|
|
927
914
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
915
|
+
if (idx + 1 < len(rows)) and (rows[idx + 1]["start"] - dec("0.001") <= row["stop"] < rows[idx + 1]["start"]):
|
|
916
|
+
xmax = rows[idx + 1]["start"]
|
|
917
|
+
else:
|
|
918
|
+
xmax = row["stop"]
|
|
919
|
+
|
|
920
|
+
out += interval_template.format(count=count, name=row["code"], xmin=row["start"], xmax=xmax)
|
|
921
|
+
|
|
922
|
+
# check if no behavior
|
|
923
|
+
if (idx + 1 < len(rows)) and (row["stop"] < rows[idx + 1]["start"] - dec("0.001")):
|
|
924
|
+
count += 1
|
|
925
|
+
out += interval_template.format(
|
|
926
|
+
count=count,
|
|
927
|
+
name="null",
|
|
928
|
+
xmin=row["stop"],
|
|
929
|
+
xmax=rows[idx + 1]["start"],
|
|
932
930
|
)
|
|
933
|
-
)
|
|
934
|
-
QApplication.processEvents()
|
|
935
931
|
|
|
936
|
-
|
|
932
|
+
if next_obs:
|
|
937
933
|
break
|
|
938
934
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
if (idx + 1 < len(rows)) and (
|
|
942
|
-
rows[idx + 1]["start"] - dec("0.001") <= row["stop"] < rows[idx + 1]["start"]
|
|
943
|
-
):
|
|
944
|
-
xmax = rows[idx + 1]["start"]
|
|
945
|
-
else:
|
|
946
|
-
xmax = row["stop"]
|
|
947
|
-
|
|
948
|
-
out += interval_template.format(count=count, name=row["code"], xmin=row["start"], xmax=xmax)
|
|
949
|
-
|
|
950
|
-
# check if no behavior
|
|
951
|
-
if (idx + 1 < len(rows)) and (row["stop"] < rows[idx + 1]["start"] - dec("0.001")):
|
|
935
|
+
# check if last event ends at the end of media file
|
|
936
|
+
if rows[-1]["stop"] < max_time:
|
|
952
937
|
count += 1
|
|
953
|
-
out += interval_template.format(
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
if rows[-1]["stop"] < max_time:
|
|
965
|
-
count += 1
|
|
966
|
-
out += interval_template.format(count=count, name="null", xmin=rows[-1]["stop"], xmax=max_time)
|
|
967
|
-
|
|
968
|
-
# add info
|
|
969
|
-
subject_index += 1
|
|
970
|
-
out = out.format(
|
|
971
|
-
subject_index=subject_index,
|
|
972
|
-
subject=subject,
|
|
973
|
-
intervalsSize=count,
|
|
974
|
-
intervalsMin=intervalsMin,
|
|
975
|
-
intervalsMax=intervalsMax,
|
|
976
|
-
)
|
|
938
|
+
out += interval_template.format(count=count, name="null", xmin=rows[-1]["stop"], xmax=max_time)
|
|
939
|
+
|
|
940
|
+
# add info
|
|
941
|
+
subject_index += 1
|
|
942
|
+
out = out.format(
|
|
943
|
+
subject_index=subject_index,
|
|
944
|
+
subject=subject,
|
|
945
|
+
intervalsSize=count,
|
|
946
|
+
intervalsMin=intervalsMin,
|
|
947
|
+
intervalsMax=intervalsMax,
|
|
948
|
+
)
|
|
977
949
|
|
|
978
950
|
# POINT events
|
|
979
951
|
cursor.execute(
|
|
980
|
-
(
|
|
981
|
-
"SELECT start, behavior FROM aggregated_events "
|
|
982
|
-
"WHERE observation = ? AND subject = ? AND type = 'POINT' ORDER BY start"
|
|
983
|
-
),
|
|
952
|
+
("SELECT start, behavior FROM aggregated_events WHERE observation = ? AND subject = ? AND type = 'POINT' ORDER BY start"),
|
|
984
953
|
(obs_id, subject),
|
|
985
954
|
)
|
|
986
955
|
|
|
@@ -993,7 +962,6 @@ def export_events_as_textgrid(self) -> None:
|
|
|
993
962
|
count = 0
|
|
994
963
|
|
|
995
964
|
for idx, row in enumerate(rows):
|
|
996
|
-
|
|
997
965
|
count += 1
|
|
998
966
|
out += point_template.format(count=count, mark=row["code"], number=row["start"])
|
|
999
967
|
|
|
@@ -1011,15 +979,12 @@ def export_events_as_textgrid(self) -> None:
|
|
|
1011
979
|
continue
|
|
1012
980
|
|
|
1013
981
|
# check if file already exists
|
|
1014
|
-
if (
|
|
1015
|
-
mem_command != cfg.OVERWRITE_ALL
|
|
1016
|
-
and pl.Path(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.textGrid").is_file()
|
|
1017
|
-
):
|
|
982
|
+
if mem_command != cfg.OVERWRITE_ALL and pl.Path(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid").is_file():
|
|
1018
983
|
if mem_command == cfg.SKIP_ALL:
|
|
1019
984
|
continue
|
|
1020
985
|
mem_command = dialog.MessageDialog(
|
|
1021
986
|
cfg.programName,
|
|
1022
|
-
f"The file <b>{pl.Path(export_dir) / util.safeFileName(obs_id)}.
|
|
987
|
+
f"The file <b>{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid</b> already exists.",
|
|
1023
988
|
[cfg.OVERWRITE, cfg.OVERWRITE_ALL, cfg.SKIP, cfg.SKIP_ALL, cfg.CANCEL],
|
|
1024
989
|
)
|
|
1025
990
|
if mem_command == cfg.CANCEL:
|
|
@@ -1028,17 +993,13 @@ def export_events_as_textgrid(self) -> None:
|
|
|
1028
993
|
continue
|
|
1029
994
|
|
|
1030
995
|
try:
|
|
1031
|
-
with open(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.
|
|
996
|
+
with open(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid", "w") as f:
|
|
1032
997
|
f.write(out)
|
|
1033
998
|
file_count += 1
|
|
1034
|
-
self.results.ptText.appendHtml(
|
|
1035
|
-
f"File {pl.Path(export_dir) / util.safeFileName(obs_id)}.textGrid was created."
|
|
1036
|
-
)
|
|
999
|
+
self.results.ptText.appendHtml(f"File {pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid was created.")
|
|
1037
1000
|
QApplication.processEvents()
|
|
1038
1001
|
except Exception:
|
|
1039
|
-
self.results.ptText.appendHtml(
|
|
1040
|
-
f"The file {pl.Path(export_dir) / util.safeFileName(obs_id)}.textGrid can not be created."
|
|
1041
|
-
)
|
|
1002
|
+
self.results.ptText.appendHtml(f"The file {pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid can not be created.")
|
|
1042
1003
|
QApplication.processEvents()
|
|
1043
1004
|
|
|
1044
1005
|
self.results.ptText.appendHtml(f"Done. {file_count} file(s) were created in {export_dir}.")
|