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_observation.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 program is free software; you can redistribute it and/or modify
|
|
7
7
|
it under the terms of the GNU General Public License as published by
|
|
@@ -29,7 +29,7 @@ import math
|
|
|
29
29
|
import pathlib
|
|
30
30
|
from io import StringIO
|
|
31
31
|
import pandas as pd
|
|
32
|
-
from typing import
|
|
32
|
+
from typing import Tuple
|
|
33
33
|
|
|
34
34
|
try:
|
|
35
35
|
import pyreadr
|
|
@@ -66,7 +66,6 @@ def export_events_jwatcher(
|
|
|
66
66
|
str: error message
|
|
67
67
|
"""
|
|
68
68
|
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
69
|
-
|
|
70
69
|
# select events for current subject
|
|
71
70
|
events = []
|
|
72
71
|
for event in observation[cfg.EVENTS]:
|
|
@@ -80,9 +79,7 @@ def export_events_jwatcher(
|
|
|
80
79
|
|
|
81
80
|
total_length = 0 # in seconds
|
|
82
81
|
if observation[cfg.EVENTS]:
|
|
83
|
-
total_length =
|
|
84
|
-
observation[cfg.EVENTS][-1][0] - observation[cfg.EVENTS][0][0]
|
|
85
|
-
) # last event time - first event time
|
|
82
|
+
total_length = observation[cfg.EVENTS][-1][0] - observation[cfg.EVENTS][0][0] # last event time - first event time
|
|
86
83
|
|
|
87
84
|
file_name_subject = str(pathlib.Path(file_name).parent / pathlib.Path(file_name).stem) + "_" + subject + ".dat"
|
|
88
85
|
|
|
@@ -120,14 +117,13 @@ def export_events_jwatcher(
|
|
|
120
117
|
behav_code = event[cfg.EVENT_BEHAVIOR_FIELD_IDX]
|
|
121
118
|
|
|
122
119
|
try:
|
|
123
|
-
behavior_key = [
|
|
124
|
-
ethogram[k][cfg.BEHAVIOR_KEY] for k in ethogram if ethogram[k][cfg.BEHAVIOR_CODE] == behav_code
|
|
125
|
-
][0]
|
|
120
|
+
behavior_key = [ethogram[k][cfg.BEHAVIOR_KEY] for k in ethogram if ethogram[k][cfg.BEHAVIOR_CODE] == behav_code][0]
|
|
126
121
|
except Exception:
|
|
127
122
|
# coded behavior not defined in ethogram
|
|
128
123
|
continue
|
|
129
|
-
if [ethogram[k][cfg.TYPE] for k in ethogram if ethogram[k][cfg.BEHAVIOR_CODE] == behav_code]
|
|
130
|
-
cfg.STATE_EVENT
|
|
124
|
+
if [ethogram[k][cfg.TYPE] for k in ethogram if ethogram[k][cfg.BEHAVIOR_CODE] == behav_code] in [
|
|
125
|
+
[cfg.STATE_EVENT],
|
|
126
|
+
[cfg.STATE_EVENT_WITH_CODING_MAP],
|
|
131
127
|
]:
|
|
132
128
|
if behav_code in mem_number_of_state_events:
|
|
133
129
|
mem_number_of_state_events[behav_code] += 1
|
|
@@ -155,7 +151,7 @@ def export_events_jwatcher(
|
|
|
155
151
|
if fmf_file_path.exists():
|
|
156
152
|
fmf_creation_answer = dialog.MessageDialog(
|
|
157
153
|
cfg.programName,
|
|
158
|
-
(f"The {fmf_file_path} file already exists.<br>
|
|
154
|
+
(f"The {fmf_file_path} file already exists.<br>What do you want to do?"),
|
|
159
155
|
[cfg.OVERWRITE, "Skip file creation", cfg.CANCEL],
|
|
160
156
|
)
|
|
161
157
|
|
|
@@ -168,11 +164,9 @@ def export_events_jwatcher(
|
|
|
168
164
|
rows.append("# Format: Focal Master File 1.0")
|
|
169
165
|
rows.append(f"# Updated: {dt.datetime.now().isoformat()}")
|
|
170
166
|
rows.append("#-----------------------------------------------------------")
|
|
171
|
-
for
|
|
167
|
+
for behav, key in all_observed_behaviors:
|
|
172
168
|
rows.append(f"Behaviour.name.{key}={behav}")
|
|
173
|
-
behav_description = [
|
|
174
|
-
ethogram[k][cfg.DESCRIPTION] for k in ethogram if ethogram[k][cfg.BEHAVIOR_CODE] == behav
|
|
175
|
-
][0]
|
|
169
|
+
behav_description = [ethogram[k][cfg.DESCRIPTION] for k in ethogram if ethogram[k][cfg.BEHAVIOR_CODE] == behav][0]
|
|
176
170
|
rows.append(f"Behaviour.description.{key}={behav_description}")
|
|
177
171
|
|
|
178
172
|
rows.append(f"DurationMilliseconds={int(float(total_length) * 1000)}")
|
|
@@ -199,7 +193,7 @@ def export_events_jwatcher(
|
|
|
199
193
|
if faf_file_path.exists():
|
|
200
194
|
faf_creation_answer = dialog.MessageDialog(
|
|
201
195
|
cfg.programName,
|
|
202
|
-
(f"The {faf_file_path} file already exists.<br>
|
|
196
|
+
(f"The {faf_file_path} file already exists.<br>What do you want to do?"),
|
|
203
197
|
[cfg.OVERWRITE, "Skip file creation", cfg.CANCEL],
|
|
204
198
|
)
|
|
205
199
|
if faf_creation_answer == cfg.CANCEL:
|
|
@@ -276,7 +270,7 @@ def export_events_jwatcher(
|
|
|
276
270
|
rows.append("AllCodesMutuallyExclusive=true")
|
|
277
271
|
rows.append("")
|
|
278
272
|
|
|
279
|
-
for
|
|
273
|
+
for behav, key in all_observed_behaviors:
|
|
280
274
|
rows.append(f"Behavior.isModified.{key}=false")
|
|
281
275
|
rows.append(f"Behavior.isSubtracted.{key}=false")
|
|
282
276
|
rows.append(f"Behavior.isIgnored.{key}=false")
|
|
@@ -319,13 +313,13 @@ def export_tabular_events(
|
|
|
319
313
|
interval = parameters["time"]
|
|
320
314
|
|
|
321
315
|
start_coding, end_coding, coding_duration = observation_operations.coding_time(pj[cfg.OBSERVATIONS], [obs_id])
|
|
316
|
+
start_interval, end_interval = observation_operations.time_intervals_range(pj[cfg.OBSERVATIONS], [obs_id])
|
|
322
317
|
|
|
323
318
|
if interval == cfg.TIME_EVENTS:
|
|
324
319
|
min_time = start_coding
|
|
325
320
|
max_time = end_coding
|
|
326
321
|
|
|
327
322
|
if interval == cfg.TIME_FULL_OBS:
|
|
328
|
-
|
|
329
323
|
if observation[cfg.TYPE] == cfg.MEDIA:
|
|
330
324
|
max_media_duration, _ = observation_operations.media_duration(pj[cfg.OBSERVATIONS], [obs_id])
|
|
331
325
|
min_time = dec("0")
|
|
@@ -340,6 +334,12 @@ def export_tabular_events(
|
|
|
340
334
|
min_time = parameters[cfg.START_TIME]
|
|
341
335
|
max_time = parameters[cfg.END_TIME]
|
|
342
336
|
|
|
337
|
+
if interval == cfg.TIME_OBS_INTERVAL:
|
|
338
|
+
max_media_duration, _ = observation_operations.media_duration(pj[cfg.OBSERVATIONS], [obs_id])
|
|
339
|
+
min_time = start_interval
|
|
340
|
+
# Use max media duration for max time if no interval is defined (=0)
|
|
341
|
+
max_time = end_interval if end_interval != 0 else max_media_duration
|
|
342
|
+
|
|
343
343
|
logging.debug(f"min_time: {min_time} max_time: {max_time}")
|
|
344
344
|
|
|
345
345
|
events_with_status = project_functions.events_start_stop(ethogram, observation[cfg.EVENTS], observation[cfg.TYPE])
|
|
@@ -356,10 +356,7 @@ def export_tabular_events(
|
|
|
356
356
|
max_modifiers = max(max_modifiers, len(event[cfg.EVENT_MODIFIER_FIELD_IDX].split("|")))
|
|
357
357
|
|
|
358
358
|
# media file number
|
|
359
|
-
|
|
360
|
-
if observation[cfg.TYPE] == cfg.MEDIA:
|
|
361
|
-
for player in observation[cfg.FILE]:
|
|
362
|
-
mediaNb += len(observation[cfg.FILE][player])
|
|
359
|
+
media_nb = util.count_media_file(observation[cfg.FILE])
|
|
363
360
|
|
|
364
361
|
rows: list = []
|
|
365
362
|
|
|
@@ -371,9 +368,14 @@ def export_tabular_events(
|
|
|
371
368
|
"Observation duration": float,
|
|
372
369
|
"Observation type": str,
|
|
373
370
|
"Source": str,
|
|
374
|
-
"
|
|
375
|
-
"FPS": float,
|
|
371
|
+
"Time offset (s)": str,
|
|
376
372
|
}
|
|
373
|
+
if media_nb == 1:
|
|
374
|
+
fields_type["Media duration (s)"] = float
|
|
375
|
+
fields_type["FPS"] = float
|
|
376
|
+
else:
|
|
377
|
+
fields_type["Media duration (s)"] = str
|
|
378
|
+
fields_type["FPS"] = str
|
|
377
379
|
|
|
378
380
|
# independent variables
|
|
379
381
|
if cfg.INDEPENDENT_VARIABLES in observation:
|
|
@@ -418,7 +420,6 @@ def export_tabular_events(
|
|
|
418
420
|
(event[cfg.EVENT_SUBJECT_FIELD_IDX] in parameters[cfg.SELECTED_SUBJECTS])
|
|
419
421
|
or (event[cfg.EVENT_SUBJECT_FIELD_IDX] == "" and cfg.NO_FOCAL_SUBJECT in parameters[cfg.SELECTED_SUBJECTS])
|
|
420
422
|
) and (event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in parameters[cfg.SELECTED_BEHAVIORS]):
|
|
421
|
-
|
|
422
423
|
fields: list = []
|
|
423
424
|
fields.append(obs_id)
|
|
424
425
|
fields.append(observation.get("date", "").replace("T", " "))
|
|
@@ -436,9 +437,7 @@ def export_tabular_events(
|
|
|
436
437
|
for media_file in observation[cfg.FILE][player]:
|
|
437
438
|
media_file_lst.append(media_file)
|
|
438
439
|
fps_lst.append(f"{observation[cfg.MEDIA_INFO][cfg.FPS].get(media_file, cfg.NA):.3f}")
|
|
439
|
-
media_durations_lst.append(
|
|
440
|
-
f"{observation[cfg.MEDIA_INFO][cfg.LENGTH].get(media_file, cfg.NA):.3f}"
|
|
441
|
-
)
|
|
440
|
+
media_durations_lst.append(f"{observation[cfg.MEDIA_INFO][cfg.LENGTH].get(media_file, cfg.NA):.3f}")
|
|
442
441
|
if player > "1":
|
|
443
442
|
media_file_str += "|"
|
|
444
443
|
fps_str += "|"
|
|
@@ -488,6 +487,9 @@ def export_tabular_events(
|
|
|
488
487
|
else:
|
|
489
488
|
fields.append("")
|
|
490
489
|
|
|
490
|
+
# time offset
|
|
491
|
+
fields.append(observation[cfg.TIME_OFFSET])
|
|
492
|
+
|
|
491
493
|
# media duration
|
|
492
494
|
fields.append(media_durations_str)
|
|
493
495
|
|
|
@@ -526,9 +528,7 @@ def export_tabular_events(
|
|
|
526
528
|
|
|
527
529
|
# check video file name containing the event
|
|
528
530
|
if observation[cfg.TYPE] == cfg.MEDIA:
|
|
529
|
-
video_file_name = observation_operations.event2media_file_name(
|
|
530
|
-
observation, event[cfg.EVENT_TIME_FIELD_IDX]
|
|
531
|
-
)
|
|
531
|
+
video_file_name = observation_operations.event2media_file_name(observation, event[cfg.EVENT_TIME_FIELD_IDX])
|
|
532
532
|
if video_file_name is None:
|
|
533
533
|
video_file_name = "Not found"
|
|
534
534
|
|
|
@@ -573,9 +573,7 @@ def export_tabular_events(
|
|
|
573
573
|
return r, msg
|
|
574
574
|
|
|
575
575
|
|
|
576
|
-
def dataset_write(
|
|
577
|
-
dataset: tablib.Dataset, file_name: str, output_format: str, dtype: dict = {}
|
|
578
|
-
) -> tuple: # -> tuple[bool, str]:
|
|
576
|
+
def dataset_write(dataset: tablib.Dataset, file_name: str, output_format: str, dtype: dict = {}) -> tuple: # -> tuple[bool, str]:
|
|
579
577
|
"""
|
|
580
578
|
write a tablib dataset with aggregated events or tabular events to file in specified format (output_format)
|
|
581
579
|
|
|
@@ -583,6 +581,7 @@ def dataset_write(
|
|
|
583
581
|
dataset (tablib.dataset): dataset to write
|
|
584
582
|
file_name (str): file name
|
|
585
583
|
output_format (str): format of output
|
|
584
|
+
dtype (dict): type of field
|
|
586
585
|
|
|
587
586
|
Returns:
|
|
588
587
|
bool: result. True if OK else False
|
|
@@ -592,11 +591,9 @@ def dataset_write(
|
|
|
592
591
|
logging.debug("function: dataset_write")
|
|
593
592
|
|
|
594
593
|
try:
|
|
595
|
-
|
|
596
|
-
if output_format in ("pkl", "rds"):
|
|
597
|
-
|
|
594
|
+
if output_format in (cfg.PANDAS_DF_EXT, cfg.RDS_EXT):
|
|
598
595
|
# build pandas dataframe from the tsv export of tablib dataset
|
|
599
|
-
date_type = []
|
|
596
|
+
date_type: list = []
|
|
600
597
|
for field_name in dtype:
|
|
601
598
|
if dtype[field_name] == dt.datetime:
|
|
602
599
|
date_type.append(field_name)
|
|
@@ -605,27 +602,26 @@ def dataset_write(
|
|
|
605
602
|
del dtype[field_name]
|
|
606
603
|
|
|
607
604
|
df = pd.read_csv(
|
|
608
|
-
StringIO(dataset.export(
|
|
605
|
+
StringIO(dataset.export(cfg.TSV_EXT)),
|
|
609
606
|
sep="\t",
|
|
610
607
|
dtype=dtype,
|
|
611
608
|
parse_dates=date_type,
|
|
612
609
|
)
|
|
613
610
|
|
|
614
|
-
if output_format ==
|
|
611
|
+
if output_format == cfg.PANDAS_DF_EXT:
|
|
615
612
|
df.to_pickle(file_name)
|
|
616
613
|
|
|
617
|
-
if
|
|
614
|
+
if output_format == cfg.RDS_EXT and flag_pyreadr_loaded:
|
|
618
615
|
pyreadr.write_rds(file_name, df)
|
|
619
616
|
|
|
620
617
|
return True, ""
|
|
621
618
|
|
|
622
|
-
if output_format in (
|
|
619
|
+
if output_format in (cfg.CSV_EXT, cfg.TSV_EXT, cfg.HTML_EXT):
|
|
623
620
|
with open(file_name, "wb") as f:
|
|
624
621
|
f.write(str.encode(dataset.export(output_format)))
|
|
625
622
|
return True, ""
|
|
626
623
|
|
|
627
|
-
if output_format in (
|
|
628
|
-
|
|
624
|
+
if output_format in (cfg.ODS_EXT, cfg.XLS_EXT, cfg.XLSX_EXT):
|
|
629
625
|
dataset.title = util.safe_xl_worksheet_title(dataset.title, output_format)
|
|
630
626
|
|
|
631
627
|
with open(file_name, "wb") as f:
|
|
@@ -638,7 +634,7 @@ def dataset_write(
|
|
|
638
634
|
return False, str(sys.exc_info()[1])
|
|
639
635
|
|
|
640
636
|
|
|
641
|
-
def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[tablib.Dataset, int]:
|
|
637
|
+
def export_aggregated_events(pj: dict, parameters: dict, obsId: str, force_number_modifiers: int = 0) -> Tuple[tablib.Dataset, int]:
|
|
642
638
|
"""
|
|
643
639
|
export aggregated events of one observation
|
|
644
640
|
|
|
@@ -646,6 +642,7 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
646
642
|
pj (dict): BORIS project
|
|
647
643
|
parameters (dict): subjects, behaviors
|
|
648
644
|
obsId (str): observation id
|
|
645
|
+
force_number_modifiers (int): force the number of modifiers to return
|
|
649
646
|
|
|
650
647
|
Returns:
|
|
651
648
|
tablib.Dataset:
|
|
@@ -660,6 +657,8 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
660
657
|
data = tablib.Dataset()
|
|
661
658
|
|
|
662
659
|
start_coding, end_coding, coding_duration = observation_operations.coding_time(pj[cfg.OBSERVATIONS], [obsId])
|
|
660
|
+
start_interval, end_interval = observation_operations.time_intervals_range(pj[cfg.OBSERVATIONS], [obsId])
|
|
661
|
+
|
|
663
662
|
if start_coding is None and end_coding is None: # no events
|
|
664
663
|
return data, 0
|
|
665
664
|
|
|
@@ -681,21 +680,22 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
681
680
|
min_time = float(parameters[cfg.START_TIME])
|
|
682
681
|
max_time = float(parameters[cfg.END_TIME])
|
|
683
682
|
|
|
683
|
+
if interval == cfg.TIME_OBS_INTERVAL:
|
|
684
|
+
max_media_duration, _ = observation_operations.media_duration(pj[cfg.OBSERVATIONS], [obsId])
|
|
685
|
+
min_time = float(start_interval)
|
|
686
|
+
# Use max media duration for max time if no interval is defined (=0)
|
|
687
|
+
max_time = float(end_interval) if end_interval != 0 else float(max_media_duration)
|
|
688
|
+
|
|
684
689
|
logging.debug(f"min_time: {min_time} max_time: {max_time}")
|
|
685
690
|
|
|
686
691
|
# obs description
|
|
687
692
|
obs_description = util.eol2space(observation[cfg.DESCRIPTION])
|
|
688
693
|
|
|
689
|
-
"""
|
|
690
|
-
obs_length = observation_operations.observation_total_length(pj[cfg.OBSERVATIONS][obsId])
|
|
691
|
-
logging.debug(f"obs_length: {obs_length}")
|
|
692
|
-
"""
|
|
693
|
-
|
|
694
694
|
_, _, connector = db_functions.load_aggregated_events_in_db(
|
|
695
695
|
pj, parameters[cfg.SELECTED_SUBJECTS], [obsId], parameters[cfg.SELECTED_BEHAVIORS]
|
|
696
696
|
)
|
|
697
697
|
if connector is None:
|
|
698
|
-
logging.critical(
|
|
698
|
+
logging.critical("error when loading aggregated events in DB")
|
|
699
699
|
return data, 0
|
|
700
700
|
|
|
701
701
|
cursor = connector.cursor()
|
|
@@ -749,13 +749,16 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
749
749
|
behavioral_category = project_functions.behavior_category(pj[cfg.ETHOGRAM])
|
|
750
750
|
|
|
751
751
|
cursor.execute("SELECT DISTINCT modifiers FROM aggregated_events")
|
|
752
|
-
max_modifiers = 0
|
|
753
|
-
for row in cursor.fetchall():
|
|
754
|
-
if row["modifiers"]:
|
|
755
|
-
max_modifiers = max(max_modifiers, row["modifiers"].count("|") + 1)
|
|
756
752
|
|
|
757
|
-
|
|
753
|
+
if not force_number_modifiers:
|
|
754
|
+
max_modifiers: int = 0
|
|
755
|
+
for row in cursor.fetchall():
|
|
756
|
+
if row["modifiers"]:
|
|
757
|
+
max_modifiers = max(max_modifiers, row["modifiers"].count("|") + 1)
|
|
758
|
+
else:
|
|
759
|
+
max_modifiers = force_number_modifiers
|
|
758
760
|
|
|
761
|
+
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
759
762
|
# calculate observation duration by subject (by obs)
|
|
760
763
|
cursor.execute(("SELECT SUM(stop - start) AS duration FROM aggregated_events WHERE subject = ? "), (subject,))
|
|
761
764
|
duration_by_subject_by_obs = cursor.fetchone()["duration"]
|
|
@@ -763,7 +766,6 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
763
766
|
duration_by_subject_by_obs = round(duration_by_subject_by_obs, 3)
|
|
764
767
|
|
|
765
768
|
for behavior in parameters[cfg.SELECTED_BEHAVIORS]:
|
|
766
|
-
|
|
767
769
|
cursor.execute(
|
|
768
770
|
"SELECT DISTINCT modifiers FROM aggregated_events WHERE subject=? AND behavior=? ORDER BY modifiers",
|
|
769
771
|
(
|
|
@@ -775,7 +777,6 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
775
777
|
rows_distinct_modifiers = list(x[0] for x in cursor.fetchall())
|
|
776
778
|
|
|
777
779
|
for distinct_modifiers in rows_distinct_modifiers:
|
|
778
|
-
|
|
779
780
|
cursor.execute(
|
|
780
781
|
(
|
|
781
782
|
"SELECT start, stop, type, modifiers, comment, comment_stop, "
|
|
@@ -787,12 +788,12 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
787
788
|
)
|
|
788
789
|
|
|
789
790
|
for row in cursor.fetchall():
|
|
790
|
-
|
|
791
791
|
media_file_name = cfg.NA
|
|
792
792
|
|
|
793
793
|
if observation[cfg.TYPE] == cfg.MEDIA:
|
|
794
794
|
observation_type = "Media file"
|
|
795
795
|
|
|
796
|
+
# get the media file name of the start of event
|
|
796
797
|
media_file_name = observation_operations.event2media_file_name(observation, row["start"])
|
|
797
798
|
if media_file_name is None:
|
|
798
799
|
media_file_name = "Not found"
|
|
@@ -804,12 +805,8 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
804
805
|
if observation[cfg.FILE][player]:
|
|
805
806
|
for media_file in observation[cfg.FILE][player]:
|
|
806
807
|
media_file_lst.append(media_file)
|
|
807
|
-
fps_lst.append(
|
|
808
|
-
|
|
809
|
-
)
|
|
810
|
-
media_durations_lst.append(
|
|
811
|
-
f"{observation[cfg.MEDIA_INFO][cfg.LENGTH].get(media_file, cfg.NA):.3f}"
|
|
812
|
-
)
|
|
808
|
+
fps_lst.append(f"{observation[cfg.MEDIA_INFO][cfg.FPS].get(media_file, cfg.NA):.3f}")
|
|
809
|
+
media_durations_lst.append(f"{observation[cfg.MEDIA_INFO][cfg.LENGTH].get(media_file, cfg.NA):.3f}")
|
|
813
810
|
if player > "1":
|
|
814
811
|
media_file_str += "|"
|
|
815
812
|
fps_str += "|"
|
|
@@ -841,7 +838,8 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
841
838
|
observation["date"].replace("T", " "),
|
|
842
839
|
obs_description,
|
|
843
840
|
observation_type,
|
|
844
|
-
media_file_str,
|
|
841
|
+
media_file_str, # list of media used in observation
|
|
842
|
+
pj[cfg.OBSERVATIONS][obsId][cfg.TIME_OFFSET],
|
|
845
843
|
f"{coding_duration:.3f}" if not coding_duration.is_nan() else cfg.NA,
|
|
846
844
|
media_durations_str,
|
|
847
845
|
fps_str,
|
|
@@ -851,13 +849,8 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
851
849
|
# independent variables
|
|
852
850
|
if cfg.INDEPENDENT_VARIABLES in pj:
|
|
853
851
|
for idx_var in util.sorted_keys(pj[cfg.INDEPENDENT_VARIABLES]):
|
|
854
|
-
if
|
|
855
|
-
pj[cfg.INDEPENDENT_VARIABLES][idx_var]["label"]
|
|
856
|
-
in observation[cfg.INDEPENDENT_VARIABLES]
|
|
857
|
-
):
|
|
858
|
-
var_value = observation[cfg.INDEPENDENT_VARIABLES][
|
|
859
|
-
pj[cfg.INDEPENDENT_VARIABLES][idx_var]["label"]
|
|
860
|
-
]
|
|
852
|
+
if pj[cfg.INDEPENDENT_VARIABLES][idx_var]["label"] in observation[cfg.INDEPENDENT_VARIABLES]:
|
|
853
|
+
var_value = observation[cfg.INDEPENDENT_VARIABLES][pj[cfg.INDEPENDENT_VARIABLES][idx_var]["label"]]
|
|
861
854
|
if pj[cfg.INDEPENDENT_VARIABLES][idx_var]["type"] == "timestamp":
|
|
862
855
|
var_value = var_value.replace("T", " ")
|
|
863
856
|
|
|
@@ -900,9 +893,7 @@ def export_aggregated_events(pj: dict, parameters: dict, obsId: str) -> Tuple[ta
|
|
|
900
893
|
f"{row['start']:.3f}" if row["start"] is not None else cfg.NA,
|
|
901
894
|
f"{row['stop']:.3f}" if row["stop"] is not None else cfg.NA,
|
|
902
895
|
# duration
|
|
903
|
-
f"{row['stop'] - row['start']:.3f}"
|
|
904
|
-
if (row["stop"] is not None) and (row["start"] is not None)
|
|
905
|
-
else cfg.NA,
|
|
896
|
+
f"{row['stop'] - row['start']:.3f}" if (row["stop"] is not None) and (row["start"] is not None) else cfg.NA,
|
|
906
897
|
media_file_name, # Media file name
|
|
907
898
|
]
|
|
908
899
|
)
|
|
@@ -951,10 +942,7 @@ def events_to_behavioral_sequences(pj, obs_id: str, subj: str, parameters: dict,
|
|
|
951
942
|
if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] not in parameters[cfg.SELECTED_BEHAVIORS]:
|
|
952
943
|
continue
|
|
953
944
|
|
|
954
|
-
if event[cfg.EVENT_SUBJECT_FIELD_IDX] == subj or (
|
|
955
|
-
subj == cfg.NO_FOCAL_SUBJECT and event[cfg.EVENT_SUBJECT_FIELD_IDX] == ""
|
|
956
|
-
):
|
|
957
|
-
|
|
945
|
+
if event[cfg.EVENT_SUBJECT_FIELD_IDX] == subj or (subj == cfg.NO_FOCAL_SUBJECT and event[cfg.EVENT_SUBJECT_FIELD_IDX] == ""):
|
|
958
946
|
# if event[cfg.EVENT_STATUS_FIELD_IDX] == cfg.POINT:
|
|
959
947
|
if event[-1] == cfg.POINT: # status is last element
|
|
960
948
|
if current_states:
|
|
@@ -986,7 +974,6 @@ def events_to_behavioral_sequences(pj, obs_id: str, subj: str, parameters: dict,
|
|
|
986
974
|
|
|
987
975
|
# if event[cfg.EVENT_STATUS_FIELD_IDX] == cfg.STOP:
|
|
988
976
|
if event[-1] == cfg.STOP:
|
|
989
|
-
|
|
990
977
|
if parameters[cfg.INCLUDE_MODIFIERS]:
|
|
991
978
|
behav_modif = (
|
|
992
979
|
f"{event[cfg.EVENT_BEHAVIOR_FIELD_IDX]}"
|
|
@@ -1010,9 +997,7 @@ def events_to_behavioral_sequences(pj, obs_id: str, subj: str, parameters: dict,
|
|
|
1010
997
|
return out
|
|
1011
998
|
|
|
1012
999
|
|
|
1013
|
-
def events_to_behavioral_sequences_all_subj(
|
|
1014
|
-
pj, obs_id: str, subjects_list: list, parameters: dict, behav_seq_separator: str
|
|
1015
|
-
) -> str:
|
|
1000
|
+
def events_to_behavioral_sequences_all_subj(pj, obs_id: str, subjects_list: list, parameters: dict, behav_seq_separator: str) -> str:
|
|
1016
1001
|
"""
|
|
1017
1002
|
return the behavioral sequences for all selected subjects in obs_id
|
|
1018
1003
|
|
|
@@ -1041,14 +1026,11 @@ def events_to_behavioral_sequences_all_subj(
|
|
|
1041
1026
|
if (event[cfg.EVENT_SUBJECT_FIELD_IDX] in subjects_list) or (
|
|
1042
1027
|
event[cfg.EVENT_SUBJECT_FIELD_IDX] == "" and cfg.NO_FOCAL_SUBJECT in subjects_list
|
|
1043
1028
|
):
|
|
1044
|
-
|
|
1045
1029
|
subject = event[cfg.EVENT_SUBJECT_FIELD_IDX] if event[cfg.EVENT_SUBJECT_FIELD_IDX] else cfg.NO_FOCAL_SUBJECT
|
|
1046
1030
|
|
|
1047
1031
|
if event[-1] == cfg.POINT:
|
|
1048
1032
|
if current_states[subject]:
|
|
1049
|
-
out += (
|
|
1050
|
-
f"[{subject}]" + "+".join(current_states[subject]) + "+" + event[cfg.EVENT_BEHAVIOR_FIELD_IDX]
|
|
1051
|
-
)
|
|
1033
|
+
out += f"[{subject}]" + "+".join(current_states[subject]) + "+" + event[cfg.EVENT_BEHAVIOR_FIELD_IDX]
|
|
1052
1034
|
else:
|
|
1053
1035
|
out += f"[{subject}]" + event[cfg.EVENT_BEHAVIOR_FIELD_IDX]
|
|
1054
1036
|
|
|
@@ -1074,7 +1056,6 @@ def events_to_behavioral_sequences_all_subj(
|
|
|
1074
1056
|
out += behav_seq_separator
|
|
1075
1057
|
|
|
1076
1058
|
if event[-1] == cfg.STOP:
|
|
1077
|
-
|
|
1078
1059
|
if parameters[cfg.INCLUDE_MODIFIERS]:
|
|
1079
1060
|
behav_modif = (
|
|
1080
1061
|
f"{event[cfg.EVENT_BEHAVIOR_FIELD_IDX]}"
|
|
@@ -1151,10 +1132,7 @@ def events_to_timed_behavioral_sequences(
|
|
|
1151
1132
|
return out
|
|
1152
1133
|
|
|
1153
1134
|
|
|
1154
|
-
def observation_to_behavioral_sequences(
|
|
1155
|
-
pj, selected_observations, parameters, behaviors_separator, separated_subjects, timed, file_name
|
|
1156
|
-
):
|
|
1157
|
-
|
|
1135
|
+
def observation_to_behavioral_sequences(pj, selected_observations, parameters, behaviors_separator, separated_subjects, timed, file_name):
|
|
1158
1136
|
try:
|
|
1159
1137
|
with open(file_name, "w", encoding="utf-8") as out_file:
|
|
1160
1138
|
for obs_id in selected_observations:
|
|
@@ -1192,9 +1170,7 @@ def observation_to_behavioral_sequences(
|
|
|
1192
1170
|
out_file.write("# Independent variables\n")
|
|
1193
1171
|
|
|
1194
1172
|
for variable in pj[cfg.OBSERVATIONS][obs_id][cfg.INDEPENDENT_VARIABLES]:
|
|
1195
|
-
out_file.write(
|
|
1196
|
-
f"# {variable}: {pj[cfg.OBSERVATIONS][obs_id][cfg.INDEPENDENT_VARIABLES][variable]}\n"
|
|
1197
|
-
)
|
|
1173
|
+
out_file.write(f"# {variable}: {pj[cfg.OBSERVATIONS][obs_id][cfg.INDEPENDENT_VARIABLES][variable]}\n")
|
|
1198
1174
|
out_file.write("\n")
|
|
1199
1175
|
|
|
1200
1176
|
# one sequence for all subjects
|
|
@@ -1215,9 +1191,7 @@ def observation_to_behavioral_sequences(
|
|
|
1215
1191
|
out = events_to_behavioral_sequences(pj, obs_id, subject, parameters, behaviors_separator)
|
|
1216
1192
|
|
|
1217
1193
|
if timed:
|
|
1218
|
-
out = events_to_timed_behavioral_sequences(
|
|
1219
|
-
pj, obs_id, subject, parameters, 0.001, behaviors_separator
|
|
1220
|
-
)
|
|
1194
|
+
out = events_to_timed_behavioral_sequences(pj, obs_id, subject, parameters, 0.001, behaviors_separator)
|
|
1221
1195
|
|
|
1222
1196
|
if out:
|
|
1223
1197
|
out_file.write(out + "\n")
|