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/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,
|
|
@@ -424,12 +409,18 @@ def export_aggregated_events(self):
|
|
|
424
409
|
cfg.XLS,
|
|
425
410
|
cfg.HTML,
|
|
426
411
|
cfg.SDIS,
|
|
412
|
+
cfg.TBS,
|
|
427
413
|
cfg.SQL,
|
|
428
414
|
cfg.PANDAS_DF,
|
|
429
415
|
cfg.RDS,
|
|
430
416
|
)
|
|
431
417
|
|
|
432
|
-
|
|
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
|
+
)
|
|
433
424
|
|
|
434
425
|
if not fileName:
|
|
435
426
|
return
|
|
@@ -438,17 +429,11 @@ def export_aggregated_events(self):
|
|
|
438
429
|
if pl.Path(fileName).suffix != "." + outputFormat:
|
|
439
430
|
# check if file with new extension already exists
|
|
440
431
|
fileName = str(pl.Path(fileName)) + "." + outputFormat
|
|
441
|
-
if pl.Path(fileName).
|
|
442
|
-
if (
|
|
443
|
-
dialog.MessageDialog(
|
|
444
|
-
cfg.programName, f"The file {fileName} already exists.", [cfg.CANCEL, cfg.OVERWRITE]
|
|
445
|
-
)
|
|
446
|
-
== cfg.CANCEL
|
|
447
|
-
):
|
|
432
|
+
if pl.Path(fileName).exists():
|
|
433
|
+
if dialog.MessageDialog(cfg.programName, f"The file {fileName} already exists.", [cfg.CANCEL, cfg.OVERWRITE]) == cfg.CANCEL:
|
|
448
434
|
return
|
|
449
435
|
|
|
450
436
|
else: # not grouping
|
|
451
|
-
|
|
452
437
|
file_formats = (
|
|
453
438
|
cfg.TSV,
|
|
454
439
|
cfg.CSV,
|
|
@@ -473,7 +458,7 @@ def export_aggregated_events(self):
|
|
|
473
458
|
if not exportDir:
|
|
474
459
|
return
|
|
475
460
|
|
|
476
|
-
if outputFormat ==
|
|
461
|
+
if outputFormat == cfg.SQL_EXT:
|
|
477
462
|
_, _, conn = db_functions.load_aggregated_events_in_db(
|
|
478
463
|
self.pj, parameters[cfg.SELECTED_SUBJECTS], selected_observations, parameters[cfg.SELECTED_BEHAVIORS]
|
|
479
464
|
)
|
|
@@ -482,7 +467,6 @@ def export_aggregated_events(self):
|
|
|
482
467
|
for line in conn.iterdump():
|
|
483
468
|
f.write(f"{line}\n")
|
|
484
469
|
except Exception:
|
|
485
|
-
|
|
486
470
|
QMessageBox.critical(
|
|
487
471
|
None,
|
|
488
472
|
cfg.programName,
|
|
@@ -494,51 +478,50 @@ def export_aggregated_events(self):
|
|
|
494
478
|
return
|
|
495
479
|
|
|
496
480
|
# compute the maximum number of modifiers
|
|
497
|
-
tot_max_modifiers = 0
|
|
481
|
+
tot_max_modifiers: int = 0
|
|
498
482
|
for obs_id in selected_observations:
|
|
499
483
|
_, max_modifiers = export_observation.export_aggregated_events(self.pj, parameters, obs_id)
|
|
500
484
|
tot_max_modifiers = max(tot_max_modifiers, max_modifiers)
|
|
501
485
|
|
|
486
|
+
logging.debug(f"tot_max_modifiers: {tot_max_modifiers}")
|
|
487
|
+
|
|
502
488
|
data_grouped_obs = tablib.Dataset()
|
|
503
|
-
# sort by start time
|
|
504
|
-
start_idx = -9 # TODO: improve!
|
|
505
|
-
stop_idx = -8
|
|
506
489
|
|
|
507
|
-
mem_command = "" # remember user choice when file already exists
|
|
508
|
-
|
|
490
|
+
mem_command: str = "" # remember user choice when file already exists
|
|
491
|
+
header = list(fields_type(tot_max_modifiers).keys())
|
|
509
492
|
|
|
493
|
+
for obs_id in selected_observations:
|
|
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
|
-
index =
|
|
517
|
-
if
|
|
502
|
+
index = header.index("Start (s)")
|
|
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
|
-
index =
|
|
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
|
-
except:
|
|
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 (
|
|
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,18 +537,12 @@ def export_aggregated_events(self):
|
|
|
554
537
|
if mem_command in (cfg.SKIP, cfg.SKIP_ALL):
|
|
555
538
|
continue
|
|
556
539
|
|
|
557
|
-
|
|
558
|
-
if sys.version_info.minor >= 8:
|
|
559
|
-
print(f"{list(data_single_obs)=}")
|
|
560
|
-
print(f"{list(fields_type(max_modifiers).keys())=}")
|
|
561
|
-
"""
|
|
562
|
-
|
|
563
|
-
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))
|
|
564
541
|
if not r:
|
|
565
|
-
QMessageBox.warning(
|
|
566
|
-
None, cfg.programName, msg, QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton
|
|
567
|
-
)
|
|
542
|
+
QMessageBox.warning(None, cfg.programName, msg, QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
|
|
568
543
|
|
|
544
|
+
"""
|
|
545
|
+
# disabled after introduction of the force_number_modifiers parameter in export_aggregated_events function
|
|
569
546
|
if len(data_single_obs_sorted) and max_modifiers < tot_max_modifiers:
|
|
570
547
|
for i in range(tot_max_modifiers - max_modifiers):
|
|
571
548
|
data_single_obs_sorted.insert_col(
|
|
@@ -573,44 +550,33 @@ def export_aggregated_events(self):
|
|
|
573
550
|
col=[""] * (len(list(data_single_obs_sorted))),
|
|
574
551
|
header=f"Modif #{i}",
|
|
575
552
|
)
|
|
553
|
+
"""
|
|
554
|
+
|
|
576
555
|
data_grouped_obs.extend(data_single_obs_sorted)
|
|
577
556
|
|
|
578
|
-
"""
|
|
579
|
-
try:
|
|
580
|
-
# order by obs_id, start time
|
|
581
|
-
data_grouped_obs_sorted = tablib.Dataset(
|
|
582
|
-
*sorted(list(data_grouped_obs), key=lambda x: (x[obs_id_idx], float(x[start_idx]))),
|
|
583
|
-
headers=list(fields_type(tot_max_modifiers).keys()),
|
|
584
|
-
)
|
|
585
|
-
except:
|
|
586
|
-
# if error order by obs_id
|
|
587
|
-
data_grouped_obs_sorted = tablib.Dataset(
|
|
588
|
-
*sorted(list(data_grouped_obs), key=lambda x: x[obs_id_idx]),
|
|
589
|
-
headers=list(fields_type(tot_max_modifiers).keys()),
|
|
590
|
-
)
|
|
591
|
-
"""
|
|
592
557
|
data_grouped_obs_all = tablib.Dataset(headers=list(fields_type(tot_max_modifiers).keys()))
|
|
558
|
+
|
|
593
559
|
data_grouped_obs_all.extend(data_grouped_obs)
|
|
594
560
|
data_grouped_obs_all.title = "Aggregated events"
|
|
595
561
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
562
|
+
start_idx = header.index("Start (s)")
|
|
563
|
+
stop_idx = header.index("Stop (s)")
|
|
564
|
+
|
|
565
|
+
if outputFormat == cfg.TBS_EXT: # Timed behavioral sequences
|
|
566
|
+
out: str = ""
|
|
567
|
+
for obs_id in selected_observations:
|
|
600
568
|
# observation id
|
|
601
|
-
out += f"# {
|
|
569
|
+
out += f"# {obs_id}\n"
|
|
602
570
|
|
|
603
|
-
for event in list(
|
|
604
|
-
if event[0] ==
|
|
605
|
-
behavior = event[
|
|
571
|
+
for event in list(data_grouped_obs_all):
|
|
572
|
+
if event[0] == obs_id:
|
|
573
|
+
behavior = event[header.index("Behavior")]
|
|
574
|
+
subject = event[header.index("Subject")]
|
|
606
575
|
# replace various char by _
|
|
607
|
-
for char in
|
|
576
|
+
for char in (" ", "-", "/"):
|
|
608
577
|
behavior = behavior.replace(char, "_")
|
|
609
|
-
subject = event[-9]
|
|
610
|
-
# replace various char by _
|
|
611
|
-
for char in [" ", "-", "/"]:
|
|
612
578
|
subject = subject.replace(char, "_")
|
|
613
|
-
event_start = f"{float(event[start_idx]):.3f}" # start event
|
|
579
|
+
event_start = f"{float(event[start_idx]):.3f}" # start event
|
|
614
580
|
if not event[stop_idx]: # stop event (from end)
|
|
615
581
|
event_stop = f"{float(event[start_idx]) + 0.001:.3f}"
|
|
616
582
|
else:
|
|
@@ -622,7 +588,7 @@ def export_aggregated_events(self):
|
|
|
622
588
|
out += "\n"
|
|
623
589
|
|
|
624
590
|
if not flag_group:
|
|
625
|
-
fileName = f"{pl.Path(exportDir) / util.safeFileName(
|
|
591
|
+
fileName = f"{pl.Path(exportDir) / util.safeFileName(obs_id)}.{outputFormat}"
|
|
626
592
|
with open(fileName, "wb") as f:
|
|
627
593
|
f.write(str.encode(out))
|
|
628
594
|
out = ""
|
|
@@ -632,45 +598,34 @@ def export_aggregated_events(self):
|
|
|
632
598
|
f.write(str.encode(out))
|
|
633
599
|
return
|
|
634
600
|
|
|
635
|
-
if outputFormat ==
|
|
636
|
-
out =
|
|
637
|
-
|
|
638
|
-
)
|
|
639
|
-
for obsId in selected_observations:
|
|
601
|
+
if outputFormat == cfg.SDIS_EXT: # SDIS format
|
|
602
|
+
out: str = f"% SDIS file created by BORIS (www.boris.unito.it) at {util.datetime_iso8601(dt.datetime.now())}\nTimed <seconds>;\n"
|
|
603
|
+
for obs_id in selected_observations:
|
|
640
604
|
# observation id
|
|
641
|
-
out += "\n<{}>\n".format(
|
|
605
|
+
out += "\n<{}>\n".format(obs_id)
|
|
642
606
|
|
|
643
|
-
for event in list(
|
|
644
|
-
if event[0] ==
|
|
645
|
-
behavior = event[
|
|
607
|
+
for event in list(data_grouped_obs_all):
|
|
608
|
+
if event[0] == obs_id:
|
|
609
|
+
behavior = event[header.index("Behavior")]
|
|
610
|
+
subject = event[header.index("Subject")]
|
|
646
611
|
# replace various char by _
|
|
647
|
-
for char in
|
|
612
|
+
for char in (" ", "-", "/"):
|
|
648
613
|
behavior = behavior.replace(char, "_")
|
|
649
|
-
subject = event[-9]
|
|
650
|
-
# replace various char by _
|
|
651
|
-
for char in [" ", "-", "/"]:
|
|
652
614
|
subject = subject.replace(char, "_")
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
) # start event (from end for independent variables)
|
|
615
|
+
|
|
616
|
+
event_start = f"{float(event[start_idx]):.3f}" # start event
|
|
656
617
|
if not event[stop_idx]: # stop event (from end)
|
|
657
|
-
event_stop = "{
|
|
618
|
+
event_stop = f"{float(event[start_idx]) + 0.001:.3f}"
|
|
658
619
|
else:
|
|
659
|
-
event_stop = "{
|
|
620
|
+
event_stop = f"{float(event[stop_idx]):.3f}"
|
|
660
621
|
out += f"{subject}_{behavior},{event_start}-{event_stop} "
|
|
661
622
|
|
|
662
623
|
out += "/\n\n"
|
|
663
624
|
if not flag_group:
|
|
664
|
-
""
|
|
665
|
-
fileName = str(pathlib.Path(pathlib.Path(exportDir) / safeFileName(obsId)).with suffix("." + outputFormat))
|
|
666
|
-
"""
|
|
667
|
-
fileName = f"{pl.Path(exportDir) / util.safeFileName(obsId)}.{outputFormat}"
|
|
625
|
+
fileName = f"{pl.Path(exportDir) / util.safeFileName(obs_id)}.{outputFormat}"
|
|
668
626
|
with open(fileName, "wb") as f:
|
|
669
627
|
f.write(str.encode(out))
|
|
670
|
-
out = (
|
|
671
|
-
"% SDIS file created by BORIS (www.boris.unito.it) "
|
|
672
|
-
f"at {util.datetime_iso8601(dt.datetime.now())}\nTimed <seconds>;\n"
|
|
673
|
-
)
|
|
628
|
+
out = f"% SDIS file created by BORIS (www.boris.unito.it) at {util.datetime_iso8601(dt.datetime.now())}\nTimed <seconds>;\n"
|
|
674
629
|
|
|
675
630
|
if flag_group:
|
|
676
631
|
with open(fileName, "wb") as f:
|
|
@@ -678,7 +633,7 @@ def export_aggregated_events(self):
|
|
|
678
633
|
return
|
|
679
634
|
|
|
680
635
|
if flag_group:
|
|
681
|
-
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))
|
|
682
637
|
if not r:
|
|
683
638
|
QMessageBox.warning(None, cfg.programName, msg, QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton)
|
|
684
639
|
|
|
@@ -719,13 +674,19 @@ def export_events_as_textgrid(self) -> None:
|
|
|
719
674
|
|
|
720
675
|
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
721
676
|
|
|
677
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
678
|
+
|
|
722
679
|
parameters = select_subj_behav.choose_obs_subj_behav_category(
|
|
723
680
|
self,
|
|
724
681
|
selected_observations,
|
|
725
682
|
start_coding=start_coding,
|
|
726
683
|
end_coding=end_coding,
|
|
727
|
-
|
|
728
|
-
|
|
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,
|
|
729
690
|
maxTime=max_obs_length,
|
|
730
691
|
n_observations=len(selected_observations),
|
|
731
692
|
)
|
|
@@ -735,13 +696,13 @@ def export_events_as_textgrid(self) -> None:
|
|
|
735
696
|
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to export")
|
|
736
697
|
return
|
|
737
698
|
|
|
738
|
-
export_dir = QFileDialog
|
|
739
|
-
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
|
|
740
701
|
)
|
|
741
702
|
if not export_dir:
|
|
742
703
|
return
|
|
743
704
|
|
|
744
|
-
mem_command = ""
|
|
705
|
+
mem_command: str = ""
|
|
745
706
|
|
|
746
707
|
# see https://www.fon.hum.uva.nl/praat/manual/TextGrid_file_formats.html
|
|
747
708
|
|
|
@@ -754,12 +715,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
754
715
|
" intervals: size = {intervalsSize}\n"
|
|
755
716
|
)
|
|
756
717
|
|
|
757
|
-
interval_template =
|
|
758
|
-
" intervals [{count}]:\n"
|
|
759
|
-
" xmin = {xmin}\n"
|
|
760
|
-
" xmax = {xmax}\n"
|
|
761
|
-
' text = "{name}"\n'
|
|
762
|
-
)
|
|
718
|
+
interval_template = ' intervals [{count}]:\n xmin = {xmin}\n xmax = {xmax}\n text = "{name}"\n'
|
|
763
719
|
|
|
764
720
|
point_subject_header = (
|
|
765
721
|
" item [{subject_index}]:\n"
|
|
@@ -770,7 +726,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
770
726
|
" points: size = {intervalsSize}\n"
|
|
771
727
|
)
|
|
772
728
|
|
|
773
|
-
point_template =
|
|
729
|
+
point_template = ' points [{count}]:\n number = {number}\n mark = "{mark}"\n'
|
|
774
730
|
|
|
775
731
|
# widget for results
|
|
776
732
|
self.results = dialog.Results_dialog()
|
|
@@ -782,7 +738,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
782
738
|
)
|
|
783
739
|
|
|
784
740
|
if db_connector is None:
|
|
785
|
-
logging.critical(
|
|
741
|
+
logging.critical("Error when loading aggregated events in DB")
|
|
786
742
|
return
|
|
787
743
|
|
|
788
744
|
cursor = db_connector.cursor()
|
|
@@ -790,11 +746,8 @@ def export_events_as_textgrid(self) -> None:
|
|
|
790
746
|
file_count: int = 0
|
|
791
747
|
|
|
792
748
|
for obs_id in selected_observations:
|
|
793
|
-
|
|
794
749
|
if parameters["time"] == cfg.TIME_EVENTS:
|
|
795
|
-
start_coding, end_coding, coding_duration = observation_operations.coding_time(
|
|
796
|
-
self.pj[cfg.OBSERVATIONS], [obs_id]
|
|
797
|
-
)
|
|
750
|
+
start_coding, end_coding, coding_duration = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], [obs_id])
|
|
798
751
|
if start_coding is None and end_coding is None: # no events
|
|
799
752
|
self.results.ptText.appendHtml(f"The observation <b>{obs_id}</b> does not have events.")
|
|
800
753
|
QApplication.processEvents()
|
|
@@ -809,7 +762,6 @@ def export_events_as_textgrid(self) -> None:
|
|
|
809
762
|
max_time = float(end_coding)
|
|
810
763
|
|
|
811
764
|
if parameters["time"] == cfg.TIME_FULL_OBS:
|
|
812
|
-
|
|
813
765
|
if self.pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] == cfg.MEDIA:
|
|
814
766
|
max_media_duration, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], [obs_id])
|
|
815
767
|
min_time = float(0)
|
|
@@ -817,9 +769,7 @@ def export_events_as_textgrid(self) -> None:
|
|
|
817
769
|
coding_duration = max_media_duration
|
|
818
770
|
|
|
819
771
|
if self.pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in (cfg.LIVE, cfg.IMAGES):
|
|
820
|
-
start_coding, end_coding, coding_duration = observation_operations.coding_time(
|
|
821
|
-
self.pj[cfg.OBSERVATIONS], [obs_id]
|
|
822
|
-
)
|
|
772
|
+
start_coding, end_coding, coding_duration = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], [obs_id])
|
|
823
773
|
if start_coding is None and end_coding is None: # no events
|
|
824
774
|
self.results.ptText.appendHtml(f"The observation <b>{obs_id}</b> does not have events.")
|
|
825
775
|
QApplication.processEvents()
|
|
@@ -836,12 +786,16 @@ def export_events_as_textgrid(self) -> None:
|
|
|
836
786
|
min_time = float(parameters[cfg.START_TIME])
|
|
837
787
|
max_time = float(parameters[cfg.END_TIME])
|
|
838
788
|
|
|
839
|
-
""
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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)
|
|
843
796
|
|
|
844
797
|
# delete events outside time interval
|
|
798
|
+
|
|
845
799
|
cursor.execute(
|
|
846
800
|
"DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)",
|
|
847
801
|
(
|
|
@@ -885,37 +839,30 @@ def export_events_as_textgrid(self) -> None:
|
|
|
885
839
|
|
|
886
840
|
next_obs: bool = False
|
|
887
841
|
|
|
888
|
-
|
|
889
|
-
total_media_duration = round(
|
|
890
|
-
observation_operations.observation_total_length(self.pj[cfg.OBSERVATIONS][obs_id]), 3
|
|
891
|
-
)
|
|
892
|
-
"""
|
|
893
|
-
|
|
842
|
+
# number of items for size parameter
|
|
894
843
|
cursor.execute(
|
|
895
844
|
(
|
|
896
|
-
"SELECT COUNT(
|
|
897
|
-
"WHERE observation = ? AND subject IN ({})
|
|
898
|
-
",".join(["?"] * len(parameters[cfg.SELECTED_SUBJECTS]))
|
|
899
|
-
)
|
|
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) "
|
|
900
847
|
),
|
|
901
848
|
[obs_id] + parameters[cfg.SELECTED_SUBJECTS],
|
|
902
849
|
)
|
|
903
850
|
|
|
904
|
-
|
|
905
|
-
|
|
851
|
+
subjects_num = int(cursor.fetchone()[0])
|
|
852
|
+
subjects_max = max_time
|
|
906
853
|
|
|
907
854
|
out = (
|
|
908
855
|
'File type = "ooTextFile"\n'
|
|
909
856
|
'Object class = "TextGrid"\n'
|
|
910
857
|
"\n"
|
|
911
858
|
f"xmin = 0.0\n"
|
|
912
|
-
f"xmax = {
|
|
859
|
+
f"xmax = {subjects_max}\n"
|
|
913
860
|
"tiers? <exists>\n"
|
|
914
|
-
f"size = {
|
|
861
|
+
f"size = {subjects_num}\n"
|
|
915
862
|
"item []:\n"
|
|
916
863
|
)
|
|
917
864
|
|
|
918
|
-
subject_index = 0
|
|
865
|
+
subject_index: int = 0
|
|
919
866
|
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
920
867
|
if subject not in [
|
|
921
868
|
x[cfg.EVENT_SUBJECT_FIELD_IDX] if x[cfg.EVENT_SUBJECT_FIELD_IDX] else cfg.NO_FOCAL_SUBJECT
|
|
@@ -923,7 +870,8 @@ def export_events_as_textgrid(self) -> None:
|
|
|
923
870
|
]:
|
|
924
871
|
continue
|
|
925
872
|
|
|
926
|
-
intervalsMin
|
|
873
|
+
intervalsMin = min_time
|
|
874
|
+
intervalsMax = max_time
|
|
927
875
|
|
|
928
876
|
# STATE events
|
|
929
877
|
cursor.execute(
|
|
@@ -938,325 +886,22 @@ def export_events_as_textgrid(self) -> None:
|
|
|
938
886
|
{"start": util.float2decimal(r["start"]), "stop": util.float2decimal(r["stop"]), "code": r["behavior"]}
|
|
939
887
|
for r in cursor.fetchall()
|
|
940
888
|
]
|
|
941
|
-
if
|
|
942
|
-
|
|
889
|
+
if rows:
|
|
890
|
+
out += interval_subject_header
|
|
943
891
|
|
|
944
|
-
|
|
892
|
+
count = 0
|
|
945
893
|
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
# check if 1st behavior starts at the beginning
|
|
949
|
-
if rows[0]["start"] > 0:
|
|
950
|
-
count += 1
|
|
951
|
-
out += interval_template.format(count=count, name="null", xmin=0.0, xmax=rows[0]["start"])
|
|
952
|
-
|
|
953
|
-
for idx, row in enumerate(rows):
|
|
954
|
-
|
|
955
|
-
# check if events are overlapping
|
|
956
|
-
if (idx + 1 < len(rows)) and (row["stop"] > rows[idx + 1]["start"]):
|
|
957
|
-
|
|
958
|
-
self.results.ptText.appendHtml(
|
|
959
|
-
(
|
|
960
|
-
f"The events overlap for subject <b>{subject}</b> in the observation <b>{obs_id}</b>. "
|
|
961
|
-
"It is not possible to create the Praat TextGrid file."
|
|
962
|
-
)
|
|
963
|
-
)
|
|
964
|
-
QApplication.processEvents()
|
|
965
|
-
|
|
966
|
-
next_obs = True
|
|
967
|
-
break
|
|
968
|
-
|
|
969
|
-
count += 1
|
|
970
|
-
|
|
971
|
-
"""
|
|
972
|
-
print(f'{row["start"]=} {row["stop"]=}')
|
|
973
|
-
if idx + 1 < len(rows):
|
|
974
|
-
print(f'{rows[idx + 1]["start"]=}')
|
|
975
|
-
print()
|
|
976
|
-
"""
|
|
977
|
-
|
|
978
|
-
if (idx + 1 < len(rows)) and (
|
|
979
|
-
rows[idx + 1]["start"] - dec("0.001") <= row["stop"] < rows[idx + 1]["start"]
|
|
980
|
-
):
|
|
981
|
-
xmax = rows[idx + 1]["start"]
|
|
982
|
-
else:
|
|
983
|
-
xmax = row["stop"]
|
|
984
|
-
|
|
985
|
-
out += interval_template.format(count=count, name=row["code"], xmin=row["start"], xmax=xmax)
|
|
986
|
-
|
|
987
|
-
# check if no behavior
|
|
988
|
-
if (idx + 1 < len(rows)) and (row["stop"] < rows[idx + 1]["start"] - dec("0.001")):
|
|
894
|
+
# check if 1st behavior starts at the beginning
|
|
895
|
+
if rows[0]["start"] > 0:
|
|
989
896
|
count += 1
|
|
990
|
-
out += interval_template.format(
|
|
991
|
-
count=count,
|
|
992
|
-
name="null",
|
|
993
|
-
xmin=row["stop"],
|
|
994
|
-
xmax=rows[idx + 1]["start"],
|
|
995
|
-
)
|
|
996
|
-
|
|
997
|
-
if next_obs:
|
|
998
|
-
break
|
|
999
|
-
|
|
1000
|
-
# check if last event ends at the end of media file
|
|
1001
|
-
if rows[-1]["stop"] < max_time:
|
|
1002
|
-
count += 1
|
|
1003
|
-
out += interval_template.format(count=count, name="null", xmin=rows[-1]["stop"], xmax=max_time)
|
|
1004
|
-
|
|
1005
|
-
# add info
|
|
1006
|
-
subject_index += 1
|
|
1007
|
-
out = out.format(
|
|
1008
|
-
subject_index=subject_index,
|
|
1009
|
-
subject=subject,
|
|
1010
|
-
intervalsSize=count,
|
|
1011
|
-
intervalsMin=intervalsMin,
|
|
1012
|
-
intervalsMax=intervalsMax,
|
|
1013
|
-
)
|
|
1014
|
-
|
|
1015
|
-
# POINT events
|
|
1016
|
-
cursor.execute(
|
|
1017
|
-
(
|
|
1018
|
-
"SELECT start, behavior FROM aggregated_events "
|
|
1019
|
-
"WHERE observation = ? AND subject = ? AND type = 'POINT' ORDER BY start"
|
|
1020
|
-
),
|
|
1021
|
-
(obs_id, subject),
|
|
1022
|
-
)
|
|
1023
|
-
|
|
1024
|
-
rows = [{"start": util.float2decimal(r["start"]), "code": r["behavior"]} for r in cursor.fetchall()]
|
|
1025
|
-
if not rows:
|
|
1026
|
-
continue
|
|
1027
|
-
|
|
1028
|
-
out += point_subject_header
|
|
1029
|
-
|
|
1030
|
-
count = 0
|
|
1031
|
-
|
|
1032
|
-
for idx, row in enumerate(rows):
|
|
1033
|
-
|
|
1034
|
-
count += 1
|
|
1035
|
-
out += point_template.format(count=count, mark=row["code"], number=row["start"])
|
|
1036
|
-
|
|
1037
|
-
# add info
|
|
1038
|
-
subject_index += 1
|
|
1039
|
-
out = out.format(
|
|
1040
|
-
subject_index=subject_index,
|
|
1041
|
-
subject=subject,
|
|
1042
|
-
intervalsSize=count,
|
|
1043
|
-
intervalsMin=intervalsMin,
|
|
1044
|
-
intervalsMax=intervalsMax,
|
|
1045
|
-
)
|
|
1046
|
-
|
|
1047
|
-
if next_obs:
|
|
1048
|
-
continue
|
|
1049
|
-
|
|
1050
|
-
# check if file already exists
|
|
1051
|
-
if (
|
|
1052
|
-
mem_command != cfg.OVERWRITE_ALL
|
|
1053
|
-
and pl.Path(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.textGrid").is_file()
|
|
1054
|
-
):
|
|
1055
|
-
if mem_command == cfg.SKIP_ALL:
|
|
1056
|
-
continue
|
|
1057
|
-
mem_command = dialog.MessageDialog(
|
|
1058
|
-
cfg.programName,
|
|
1059
|
-
f"The file <b>{pl.Path(export_dir) / util.safeFileName(obs_id)}.textGrid</b> already exists.",
|
|
1060
|
-
[cfg.OVERWRITE, cfg.OVERWRITE_ALL, cfg.SKIP, cfg.SKIP_ALL, cfg.CANCEL],
|
|
1061
|
-
)
|
|
1062
|
-
if mem_command == cfg.CANCEL:
|
|
1063
|
-
return
|
|
1064
|
-
if mem_command in (cfg.SKIP, cfg.SKIP_ALL):
|
|
1065
|
-
continue
|
|
1066
|
-
|
|
1067
|
-
try:
|
|
1068
|
-
with open(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.textGrid", "w") as f:
|
|
1069
|
-
f.write(out)
|
|
1070
|
-
file_count += 1
|
|
1071
|
-
self.results.ptText.appendHtml(
|
|
1072
|
-
f"File {pl.Path(export_dir) / util.safeFileName(obs_id)}.textGrid was created."
|
|
1073
|
-
)
|
|
1074
|
-
QApplication.processEvents()
|
|
1075
|
-
except Exception:
|
|
1076
|
-
self.results.ptText.appendHtml(
|
|
1077
|
-
f"The file {pl.Path(export_dir) / util.safeFileName(obs_id)}.textGrid can not be created."
|
|
1078
|
-
)
|
|
1079
|
-
QApplication.processEvents()
|
|
1080
|
-
|
|
1081
|
-
self.results.ptText.appendHtml(f"Done. {file_count} file(s) were created in {export_dir}.")
|
|
1082
|
-
QApplication.processEvents()
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
def export_events_as_textgrid_old(self):
|
|
1086
|
-
"""
|
|
1087
|
-
export state events as Praat textgrid
|
|
1088
|
-
"""
|
|
1089
|
-
|
|
1090
|
-
_, selected_observations = select_observations.select_observations2(self, mode=cfg.MULTIPLE, windows_title="")
|
|
1091
|
-
|
|
1092
|
-
if not selected_observations:
|
|
1093
|
-
return
|
|
1094
|
-
|
|
1095
|
-
# check if coded behaviors are defined in ethogram
|
|
1096
|
-
if project_functions.check_coded_behaviors_in_obs_list(self.pj, selected_observations):
|
|
1097
|
-
return
|
|
1098
|
-
|
|
1099
|
-
# check if state events are paired
|
|
1100
|
-
not_ok, selected_observations = project_functions.check_state_events(self.pj, selected_observations)
|
|
1101
|
-
if not_ok or not selected_observations:
|
|
1102
|
-
return
|
|
1103
|
-
|
|
1104
|
-
max_obs_length, _ = observation_operations.observation_length(self.pj, selected_observations)
|
|
1105
|
-
|
|
1106
|
-
# exit with message if events do not have timestamp
|
|
1107
|
-
if max_obs_length.is_nan():
|
|
1108
|
-
QMessageBox.critical(
|
|
1109
|
-
None,
|
|
1110
|
-
cfg.programName,
|
|
1111
|
-
("This function is not available for observations with events that do not have timestamp"),
|
|
1112
|
-
QMessageBox.Ok | QMessageBox.Default,
|
|
1113
|
-
QMessageBox.NoButton,
|
|
1114
|
-
)
|
|
1115
|
-
return
|
|
1116
|
-
|
|
1117
|
-
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
1118
|
-
|
|
1119
|
-
parameters = select_subj_behav.choose_obs_subj_behav_category(
|
|
1120
|
-
self,
|
|
1121
|
-
selected_observations,
|
|
1122
|
-
start_coding=start_coding,
|
|
1123
|
-
end_coding=end_coding,
|
|
1124
|
-
flagShowIncludeModifiers=False,
|
|
1125
|
-
flagShowExcludeBehaviorsWoEvents=False,
|
|
1126
|
-
n_observations=len(selected_observations),
|
|
1127
|
-
)
|
|
1128
|
-
if parameters == {}:
|
|
1129
|
-
return
|
|
1130
|
-
if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
|
|
1131
|
-
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to export")
|
|
1132
|
-
return
|
|
1133
|
-
|
|
1134
|
-
exportDir = QFileDialog(self).getExistingDirectory(
|
|
1135
|
-
self, "Export events as Praat TextGrid", os.path.expanduser("~"), options=QFileDialog(self).ShowDirsOnly
|
|
1136
|
-
)
|
|
1137
|
-
if not exportDir:
|
|
1138
|
-
return
|
|
1139
|
-
|
|
1140
|
-
mem_command = ""
|
|
1141
|
-
|
|
1142
|
-
# see https://www.fon.hum.uva.nl/praat/manual/TextGrid_file_formats.html
|
|
1143
|
-
|
|
1144
|
-
interval_subject_header = (
|
|
1145
|
-
" item [{subject_index}]:\n"
|
|
1146
|
-
' class = "IntervalTier"\n'
|
|
1147
|
-
' name = "{subject}"\n'
|
|
1148
|
-
" xmin = {intervalsMin}\n"
|
|
1149
|
-
" xmax = {intervalsMax}\n"
|
|
1150
|
-
" intervals: size = {intervalsSize}\n"
|
|
1151
|
-
)
|
|
1152
|
-
|
|
1153
|
-
interval_template = (
|
|
1154
|
-
" intervals [{count}]:\n"
|
|
1155
|
-
" xmin = {xmin}\n"
|
|
1156
|
-
" xmax = {xmax}\n"
|
|
1157
|
-
' text = "{name}"\n'
|
|
1158
|
-
)
|
|
1159
|
-
|
|
1160
|
-
point_subject_header = (
|
|
1161
|
-
" item [{subject_index}]:\n"
|
|
1162
|
-
' class = "TextTier"\n'
|
|
1163
|
-
' name = "{subject}"\n'
|
|
1164
|
-
" xmin = {intervalsMin}\n"
|
|
1165
|
-
" xmax = {intervalsMax}\n"
|
|
1166
|
-
" points: size = {intervalsSize}\n"
|
|
1167
|
-
)
|
|
1168
|
-
|
|
1169
|
-
point_template = " points [{count}]:\n" " number = {number}\n" ' mark = "{mark}"\n'
|
|
1170
|
-
|
|
1171
|
-
# widget for results
|
|
1172
|
-
self.results = dialog.Results_dialog()
|
|
1173
|
-
self.results.setWindowTitle(f"{cfg.programName} - Export events as Praat TextGrid")
|
|
1174
|
-
self.results.show()
|
|
1175
|
-
|
|
1176
|
-
cursor = db_functions.load_events_in_db(
|
|
1177
|
-
self.pj,
|
|
1178
|
-
parameters[cfg.SELECTED_SUBJECTS],
|
|
1179
|
-
selected_observations,
|
|
1180
|
-
parameters[cfg.SELECTED_BEHAVIORS],
|
|
1181
|
-
)
|
|
1182
|
-
|
|
1183
|
-
file_count: int = 0
|
|
1184
|
-
|
|
1185
|
-
for obs_id in selected_observations:
|
|
1186
|
-
|
|
1187
|
-
next_obs: bool = False
|
|
1188
|
-
|
|
1189
|
-
total_media_duration = round(
|
|
1190
|
-
observation_operations.observation_total_length(self.pj[cfg.OBSERVATIONS][obs_id]), 3
|
|
1191
|
-
)
|
|
1192
|
-
|
|
1193
|
-
cursor.execute(
|
|
1194
|
-
(
|
|
1195
|
-
"SELECT COUNT(DISTINCT subject) FROM events "
|
|
1196
|
-
"WHERE observation = ? AND subject IN ({}) AND type = 'STATE' ".format(
|
|
1197
|
-
",".join(["?"] * len(parameters[cfg.SELECTED_SUBJECTS]))
|
|
1198
|
-
)
|
|
1199
|
-
),
|
|
1200
|
-
[obs_id] + parameters[cfg.SELECTED_SUBJECTS],
|
|
1201
|
-
)
|
|
1202
|
-
|
|
1203
|
-
subjectsNum = int(list(cursor.fetchall())[0][0])
|
|
1204
|
-
|
|
1205
|
-
subjectsMin, subjectsMax = 0, total_media_duration
|
|
1206
|
-
|
|
1207
|
-
out = (
|
|
1208
|
-
'File type = "ooTextFile"\n'
|
|
1209
|
-
'Object class = "TextGrid"\n'
|
|
1210
|
-
"\n"
|
|
1211
|
-
f"xmin = {subjectsMin}\n"
|
|
1212
|
-
f"xmax = {subjectsMax}\n"
|
|
1213
|
-
"tiers? <exists>\n"
|
|
1214
|
-
f"size = {subjectsNum}\n"
|
|
1215
|
-
"item []:\n"
|
|
1216
|
-
)
|
|
1217
|
-
|
|
1218
|
-
subject_index = 0
|
|
1219
|
-
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
1220
|
-
if subject not in [
|
|
1221
|
-
x[cfg.EVENT_SUBJECT_FIELD_IDX] if x[cfg.EVENT_SUBJECT_FIELD_IDX] else cfg.NO_FOCAL_SUBJECT
|
|
1222
|
-
for x in self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]
|
|
1223
|
-
]:
|
|
1224
|
-
continue
|
|
1225
|
-
|
|
1226
|
-
intervalsMin, intervalsMax = 0, total_media_duration
|
|
1227
|
-
|
|
1228
|
-
# STATE events
|
|
1229
|
-
cursor.execute(
|
|
1230
|
-
(
|
|
1231
|
-
"SELECT occurence, code FROM events "
|
|
1232
|
-
"WHERE observation = ? AND subject = ? AND type = 'STATE' ORDER BY occurence"
|
|
1233
|
-
),
|
|
1234
|
-
(obs_id, subject),
|
|
1235
|
-
)
|
|
1236
|
-
|
|
1237
|
-
rows = [{"occurence": util.float2decimal(r["occurence"]), "code": r["code"]} for r in cursor.fetchall()]
|
|
1238
|
-
if not rows:
|
|
1239
|
-
continue
|
|
1240
|
-
|
|
1241
|
-
out += interval_subject_header
|
|
1242
|
-
|
|
1243
|
-
count = 0
|
|
1244
|
-
|
|
1245
|
-
# check if 1st behavior starts at the beginning
|
|
1246
|
-
|
|
1247
|
-
if rows[0]["occurence"] > 0:
|
|
1248
|
-
count += 1
|
|
1249
|
-
out += interval_template.format(count=count, name="null", xmin=0.0, xmax=rows[0]["occurence"])
|
|
1250
|
-
|
|
1251
|
-
for idx, row in enumerate(rows):
|
|
1252
|
-
|
|
1253
|
-
if idx % 2 == 0:
|
|
1254
|
-
# check if events not interlacced
|
|
1255
|
-
if row["code"] != rows[idx + 1]["code"]:
|
|
897
|
+
out += interval_template.format(count=count, name="null", xmin=0.0, xmax=rows[0]["start"])
|
|
1256
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"]):
|
|
1257
902
|
self.results.ptText.appendHtml(
|
|
1258
903
|
(
|
|
1259
|
-
f"The events
|
|
904
|
+
f"The events overlap for subject <b>{subject}</b> in the observation <b>{obs_id}</b>. "
|
|
1260
905
|
"It is not possible to create the Praat TextGrid file."
|
|
1261
906
|
)
|
|
1262
907
|
)
|
|
@@ -1266,56 +911,49 @@ def export_events_as_textgrid_old(self):
|
|
|
1266
911
|
break
|
|
1267
912
|
|
|
1268
913
|
count += 1
|
|
1269
|
-
out += interval_template.format(
|
|
1270
|
-
count=count, name=row["code"], xmin=row["occurence"], xmax=rows[idx + 1]["occurence"]
|
|
1271
|
-
)
|
|
1272
|
-
|
|
1273
|
-
# check if difference is > 0.001
|
|
1274
|
-
if len(rows) > idx + 2:
|
|
1275
|
-
if rows[idx + 2]["occurence"] - rows[idx + 1]["occurence"] > 0.001:
|
|
1276
|
-
|
|
1277
|
-
out += interval_template.format(
|
|
1278
|
-
count=count + 1,
|
|
1279
|
-
name="null",
|
|
1280
|
-
xmin=rows[idx + 1]["occurence"],
|
|
1281
|
-
xmax=rows[idx + 2]["occurence"],
|
|
1282
|
-
)
|
|
1283
|
-
count += 1
|
|
1284
|
-
else:
|
|
1285
|
-
rows[idx + 2]["occurence"] = rows[idx + 1]["occurence"]
|
|
1286
914
|
|
|
1287
|
-
|
|
1288
|
-
|
|
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"],
|
|
930
|
+
)
|
|
1289
931
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
self.pj[cfg.OBSERVATIONS][obs_id]
|
|
1293
|
-
):
|
|
1294
|
-
count += 1
|
|
1295
|
-
out += interval_template.format(
|
|
1296
|
-
count=count, name="null", xmin=rows[-1]["occurence"], xmax=total_media_duration
|
|
1297
|
-
)
|
|
932
|
+
if next_obs:
|
|
933
|
+
break
|
|
1298
934
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
935
|
+
# check if last event ends at the end of media file
|
|
936
|
+
if rows[-1]["stop"] < max_time:
|
|
937
|
+
count += 1
|
|
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
|
+
)
|
|
1308
949
|
|
|
1309
950
|
# POINT events
|
|
1310
951
|
cursor.execute(
|
|
1311
|
-
(
|
|
1312
|
-
"SELECT occurence, code FROM events "
|
|
1313
|
-
"WHERE observation = ? AND subject = ? AND type = 'POINT' ORDER BY occurence"
|
|
1314
|
-
),
|
|
952
|
+
("SELECT start, behavior FROM aggregated_events WHERE observation = ? AND subject = ? AND type = 'POINT' ORDER BY start"),
|
|
1315
953
|
(obs_id, subject),
|
|
1316
954
|
)
|
|
1317
955
|
|
|
1318
|
-
rows = [{"
|
|
956
|
+
rows = [{"start": util.float2decimal(r["start"]), "code": r["behavior"]} for r in cursor.fetchall()]
|
|
1319
957
|
if not rows:
|
|
1320
958
|
continue
|
|
1321
959
|
|
|
@@ -1324,9 +962,8 @@ def export_events_as_textgrid_old(self):
|
|
|
1324
962
|
count = 0
|
|
1325
963
|
|
|
1326
964
|
for idx, row in enumerate(rows):
|
|
1327
|
-
|
|
1328
965
|
count += 1
|
|
1329
|
-
out += point_template.format(count=count, mark=row["code"], number=row["
|
|
966
|
+
out += point_template.format(count=count, mark=row["code"], number=row["start"])
|
|
1330
967
|
|
|
1331
968
|
# add info
|
|
1332
969
|
subject_index += 1
|
|
@@ -1342,15 +979,12 @@ def export_events_as_textgrid_old(self):
|
|
|
1342
979
|
continue
|
|
1343
980
|
|
|
1344
981
|
# check if file already exists
|
|
1345
|
-
if (
|
|
1346
|
-
mem_command != cfg.OVERWRITE_ALL
|
|
1347
|
-
and pl.Path(f"{pl.Path(exportDir) / util.safeFileName(obs_id)}.textGrid").is_file()
|
|
1348
|
-
):
|
|
982
|
+
if mem_command != cfg.OVERWRITE_ALL and pl.Path(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid").is_file():
|
|
1349
983
|
if mem_command == cfg.SKIP_ALL:
|
|
1350
984
|
continue
|
|
1351
985
|
mem_command = dialog.MessageDialog(
|
|
1352
986
|
cfg.programName,
|
|
1353
|
-
f"The file <b>{pl.Path(
|
|
987
|
+
f"The file <b>{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid</b> already exists.",
|
|
1354
988
|
[cfg.OVERWRITE, cfg.OVERWRITE_ALL, cfg.SKIP, cfg.SKIP_ALL, cfg.CANCEL],
|
|
1355
989
|
)
|
|
1356
990
|
if mem_command == cfg.CANCEL:
|
|
@@ -1359,18 +993,14 @@ def export_events_as_textgrid_old(self):
|
|
|
1359
993
|
continue
|
|
1360
994
|
|
|
1361
995
|
try:
|
|
1362
|
-
with open(f"{pl.Path(
|
|
996
|
+
with open(f"{pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid", "w") as f:
|
|
1363
997
|
f.write(out)
|
|
1364
998
|
file_count += 1
|
|
1365
|
-
self.results.ptText.appendHtml(
|
|
1366
|
-
f"File {pl.Path(exportDir) / util.safeFileName(obs_id)}.textGrid was created."
|
|
1367
|
-
)
|
|
999
|
+
self.results.ptText.appendHtml(f"File {pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid was created.")
|
|
1368
1000
|
QApplication.processEvents()
|
|
1369
1001
|
except Exception:
|
|
1370
|
-
self.results.ptText.appendHtml(
|
|
1371
|
-
f"The file {pl.Path(exportDir) / util.safeFileName(obs_id)}.textGrid can not be created."
|
|
1372
|
-
)
|
|
1002
|
+
self.results.ptText.appendHtml(f"The file {pl.Path(export_dir) / util.safeFileName(obs_id)}.TextGrid can not be created.")
|
|
1373
1003
|
QApplication.processEvents()
|
|
1374
1004
|
|
|
1375
|
-
self.results.ptText.appendHtml(f"Done. {file_count} file(s) were created in {
|
|
1005
|
+
self.results.ptText.appendHtml(f"Done. {file_count} file(s) were created in {export_dir}.")
|
|
1376
1006
|
QApplication.processEvents()
|