boris-behav-obs 9.1.1__tar.gz → 9.2.3__tar.gz

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.
Files changed (126) hide show
  1. boris_behav_obs-9.2.3/PKG-INFO +33 -0
  2. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/advanced_event_filtering.py +5 -3
  3. boris_behav_obs-9.2.3/boris/analysis_plugins/number_of_occurences.py +22 -0
  4. boris_behav_obs-9.2.3/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +34 -0
  5. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/analysis_plugins/time_budget.py +9 -47
  6. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/behavior_binary_table.py +5 -5
  7. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/connections.py +3 -1
  8. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/cooccurence.py +6 -3
  9. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/core.py +46 -122
  10. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/export_events.py +8 -6
  11. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/export_observation.py +4 -1
  12. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/gui_utilities.py +19 -11
  13. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/observation_operations.py +16 -9
  14. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/param_panel.py +6 -0
  15. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/player_dock_widget.py +19 -4
  16. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/plot_events.py +1 -1
  17. boris_behav_obs-9.2.3/boris/plugins.py +274 -0
  18. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/project_functions.py +29 -22
  19. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/select_subj_behav.py +4 -0
  20. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/synthetic_time_budget.py +11 -7
  21. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/time_budget_functions.py +1 -1
  22. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/time_budget_widget.py +6 -4
  23. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/utilities.py +0 -2
  24. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/version.py +2 -2
  25. boris_behav_obs-9.2.3/boris_behav_obs.egg-info/PKG-INFO +33 -0
  26. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris_behav_obs.egg-info/SOURCES.txt +2 -1
  27. boris_behav_obs-9.2.3/boris_behav_obs.egg-info/entry_points.txt +2 -0
  28. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris_behav_obs.egg-info/requires.txt +0 -1
  29. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/pyproject.toml +12 -13
  30. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_utilities.py +7 -0
  31. boris_behav_obs-9.2.3/tests/test_utilities2.py +124 -0
  32. boris_behav_obs-9.1.1/PKG-INFO +0 -711
  33. boris_behav_obs-9.1.1/boris/analysis_plugins/number_of_occurences.py +0 -62
  34. boris_behav_obs-9.1.1/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -74
  35. boris_behav_obs-9.1.1/boris/plugins.py +0 -79
  36. boris_behav_obs-9.1.1/boris_behav_obs.egg-info/PKG-INFO +0 -711
  37. boris_behav_obs-9.1.1/boris_behav_obs.egg-info/entry_points.txt +0 -2
  38. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/LICENSE.TXT +0 -0
  39. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/MANIFEST.in +0 -0
  40. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/README.TXT +0 -0
  41. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/README.md +0 -0
  42. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/__init__.py +0 -0
  43. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/__main__.py +0 -0
  44. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/about.py +0 -0
  45. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/add_modifier.py +0 -0
  46. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/add_modifier_ui.py +0 -0
  47. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/analysis_plugins/__init__.py +0 -0
  48. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/behav_coding_map_creator.py +0 -0
  49. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/behaviors_coding_map.py +0 -0
  50. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/boris_cli.py +0 -0
  51. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/cmd_arguments.py +0 -0
  52. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/coding_pad.py +0 -0
  53. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/config.py +0 -0
  54. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/config_file.py +0 -0
  55. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/converters.py +0 -0
  56. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/converters_ui.py +0 -0
  57. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/core_qrc.py +0 -0
  58. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/core_ui.py +0 -0
  59. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/db_functions.py +0 -0
  60. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/dev.py +0 -0
  61. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/dialog.py +0 -0
  62. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/duration_widget.py +0 -0
  63. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/edit_event.py +0 -0
  64. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/edit_event_ui.py +0 -0
  65. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/event_operations.py +0 -0
  66. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/events_cursor.py +0 -0
  67. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/events_snapshots.py +0 -0
  68. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/exclusion_matrix.py +0 -0
  69. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/external_processes.py +0 -0
  70. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/geometric_measurement.py +0 -0
  71. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/image_overlay.py +0 -0
  72. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/import_observations.py +0 -0
  73. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/irr.py +0 -0
  74. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/latency.py +0 -0
  75. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/map_creator.py +0 -0
  76. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/measurement_widget.py +0 -0
  77. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/media_file.py +0 -0
  78. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/menu_options.py +0 -0
  79. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/modifiers_coding_map.py +0 -0
  80. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/mpv-1.0.3.py +0 -0
  81. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/mpv.py +0 -0
  82. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/mpv2.py +0 -0
  83. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/observation.py +0 -0
  84. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/observation_ui.py +0 -0
  85. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/observations_list.py +0 -0
  86. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/otx_parser.py +0 -0
  87. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/param_panel_ui.py +0 -0
  88. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/plot_data_module.py +0 -0
  89. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/plot_events_rt.py +0 -0
  90. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/plot_spectrogram_rt.py +0 -0
  91. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/plot_waveform_rt.py +0 -0
  92. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/portion/__init__.py +0 -0
  93. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/portion/const.py +0 -0
  94. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/portion/dict.py +0 -0
  95. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/portion/func.py +0 -0
  96. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/portion/interval.py +0 -0
  97. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/portion/io.py +0 -0
  98. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/preferences.py +0 -0
  99. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/preferences_ui.py +0 -0
  100. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/project.py +0 -0
  101. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/project_import_export.py +0 -0
  102. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/project_ui.py +0 -0
  103. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/qrc_boris.py +0 -0
  104. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/qrc_boris5.py +0 -0
  105. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/select_modifiers.py +0 -0
  106. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/select_observations.py +0 -0
  107. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/state_events.py +0 -0
  108. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/subjects_pad.py +0 -0
  109. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/transitions.py +0 -0
  110. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/video_equalizer.py +0 -0
  111. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/video_equalizer_ui.py +0 -0
  112. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/video_operations.py +0 -0
  113. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/view_df.py +0 -0
  114. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/view_df_ui.py +0 -0
  115. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris/write_event.py +0 -0
  116. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
  117. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/boris_behav_obs.egg-info/top_level.txt +0 -0
  118. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/setup.cfg +0 -0
  119. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_db_functions.py +0 -0
  120. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_export_observation.py +0 -0
  121. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_irr.py +0 -0
  122. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_observation_gui.py +0 -0
  123. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_otx_parser.py +0 -0
  124. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_preferences_gui.py +0 -0
  125. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_project_functions.py +0 -0
  126. {boris_behav_obs-9.1.1 → boris_behav_obs-9.2.3}/tests/test_time_budget.py +0 -0
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: boris-behav-obs
3
+ Version: 9.2.3
4
+ Summary: BORIS - Behavioral Observation Research Interactive Software
5
+ Author-email: Olivier Friard <olivier.friard@unito.it>
6
+ License-Expression: GPL-3.0-only
7
+ Project-URL: Homepage, http://www.boris.unito.it
8
+ Project-URL: Documentation, https://boris.readthedocs.io/en/latest/
9
+ Project-URL: Change_log, https://github.com/olivierfriard/BORIS/wiki/BORIS-change-log-v.8
10
+ Project-URL: Source_code, https://github.com/olivierfriard/BORIS
11
+ Project-URL: Issues, https://github.com/olivierfriard/BORIS/issues
12
+ Classifier: Topic :: Scientific/Engineering
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/x-rst
20
+ License-File: LICENSE.TXT
21
+ Requires-Dist: exifread>=3.0.0
22
+ Requires-Dist: numpy>=1.26.4
23
+ Requires-Dist: matplotlib>=3.3.3
24
+ Requires-Dist: pandas>=2.2.2
25
+ Requires-Dist: tablib[cli,html,ods,pandas,xls,xlsx]>=3
26
+ Requires-Dist: pyreadr
27
+ Requires-Dist: pyside6==6.8.0.2
28
+ Requires-Dist: hachoir>=3.3.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: ruff; extra == "dev"
31
+ Requires-Dist: pytest; extra == "dev"
32
+ Requires-Dist: pytest-cov; extra == "dev"
33
+ Dynamic: license-file
@@ -359,8 +359,10 @@ def event_filtering(self):
359
359
  selected_observations,
360
360
  start_coding=start_coding,
361
361
  end_coding=end_coding,
362
- start_interval=start_interval,
363
- end_interval=end_interval,
362
+ # start_interval=start_interval,
363
+ # end_interval=end_interval,
364
+ start_interval=None,
365
+ end_interval=None,
364
366
  maxTime=max_media_duration_all_obs,
365
367
  show_include_modifiers=False,
366
368
  show_exclude_non_coded_behaviors=False,
@@ -368,7 +370,7 @@ def event_filtering(self):
368
370
  n_observations=len(selected_observations),
369
371
  )
370
372
 
371
- if parameters == {}:
373
+ if not parameters:
372
374
  return
373
375
 
374
376
  if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
@@ -0,0 +1,22 @@
1
+ """
2
+ BORIS plugin
3
+
4
+ number of occurences of behaviors
5
+ """
6
+
7
+ import pandas as pd
8
+
9
+ __version__ = "0.3.0"
10
+ __version_date__ = "2025-03-17"
11
+ __plugin_name__ = "Number of occurences of behaviors"
12
+ __author__ = "Olivier Friard - University of Torino - Italy"
13
+
14
+
15
+ def run(df: pd.DataFrame):
16
+ """
17
+ Calculate the number of occurrences of behaviors by subject.
18
+ """
19
+
20
+ df_results: pd.DataFrame = df.groupby(["Subject", "Behavior"])["Behavior"].count().reset_index(name="number of occurences")
21
+
22
+ return df_results
@@ -0,0 +1,34 @@
1
+ """
2
+ BORIS plugin
3
+
4
+ number of occurences of behaviors by independent_variable
5
+ """
6
+
7
+ import pandas as pd
8
+
9
+ __version__ = "0.3.0"
10
+ __version_date__ = "2025-03-17"
11
+ __plugin_name__ = "Number of occurences of behaviors by subject by independent_variable"
12
+ __author__ = "Olivier Friard - University of Torino - Italy"
13
+
14
+
15
+ def run(df: pd.DataFrame):
16
+ """
17
+ Calculate the number of occurrences of behaviors by subject and by independent_variable.
18
+
19
+ This plugin returns a Pandas dataframe
20
+ """
21
+
22
+ df_results: df.DataFrame = (
23
+ df.groupby(
24
+ [
25
+ "independent variable 'Weather'",
26
+ "Subject",
27
+ "Behavior",
28
+ ]
29
+ )["Behavior"]
30
+ .count()
31
+ .reset_index(name="number of occurences")
32
+ )
33
+
34
+ return df_results
@@ -7,8 +7,8 @@ Time budget
7
7
  import pandas as pd
8
8
  import numpy as np
9
9
 
10
- __version__ = "0.2.0"
11
- __version_date__ = "2025-01-25"
10
+ __version__ = "0.3.0"
11
+ __version_date__ = "2025-03-17"
12
12
  __plugin_name__ = "Time budget"
13
13
  __author__ = "Olivier Friard - University of Torino - Italy"
14
14
 
@@ -18,7 +18,7 @@ def run(df: pd.DataFrame):
18
18
  Calculate the following values:
19
19
 
20
20
  - Total number of occurences of behavior
21
- - Total duration of behavior (in seconds)
21
+ - Total duration of behavior (in seconds) (pandas.DataFrame.sum() ignore NaN values when computing the sum. Use min_count=1)
22
22
  - Duration mean of behavior (in seconds)
23
23
  - Standard deviation of behavior duration (in seconds)
24
24
  - Inter-event intervals mean (in seconds)
@@ -26,11 +26,15 @@ def run(df: pd.DataFrame):
26
26
  - % of total subject observation duration
27
27
  """
28
28
 
29
+ print("running time budget plugin")
30
+
31
+ print(df)
32
+
29
33
  group_by = ["Subject", "Behavior"]
30
34
 
31
35
  dfs = [
32
36
  df.groupby(group_by)["Behavior"].count().reset_index(name="number of occurences"),
33
- df.groupby(group_by)["Duration (s)"].sum().reset_index(name="total duration"),
37
+ df.groupby(group_by)["Duration (s)"].sum(min_count=1).reset_index(name="total duration"),
34
38
  df.groupby(group_by)["Duration (s)"].mean().astype(float).round(3).reset_index(name="duration mean"),
35
39
  df.groupby(group_by)["Duration (s)"].std().astype(float).round(3).reset_index(name="duration std dev"),
36
40
  ]
@@ -48,7 +52,7 @@ def run(df: pd.DataFrame):
48
52
  interval = (df.groupby(["Subject"])["Stop (s)"].max() - df.groupby(["Subject"])["Start (s)"].min()).replace(0, np.nan)
49
53
 
50
54
  dfs.append(
51
- (100 * df.groupby(group_by)["Duration (s)"].sum() / interval)
55
+ (100 * df.groupby(group_by)["Duration (s)"].sum(min_count=1) / interval)
52
56
  .astype(float)
53
57
  .round(3)
54
58
  .reset_index(name="% of total subject observation duration")
@@ -59,45 +63,3 @@ def run(df: pd.DataFrame):
59
63
  merged_df = pd.merge(merged_df, df, on=group_by)
60
64
 
61
65
  return merged_df
62
-
63
-
64
- def main(df: pd.DataFrame, observations_list: list = [], parameters: dict = {}) -> pd.DataFrame:
65
- """
66
- filter by selected observations.
67
- filter by selected subjects.
68
- filter by selected behaviors.
69
- filter by time interval.
70
- """
71
-
72
- # filter selected observations
73
- if observations_list:
74
- df = df[df["Observation id"].isin(observations_list)]
75
-
76
- if parameters:
77
- # filter selected subjects
78
- df = df[df["Subject"].isin(parameters["selected subjects"])]
79
-
80
- # filter selected behaviors
81
- df = df[df["Behavior"].isin(parameters["selected behaviors"])]
82
-
83
- # filter selected time interval
84
- if parameters["start time"] is not None and parameters["end time"] is not None:
85
- MIN_TIME = parameters["start time"]
86
- MAX_TIME = parameters["end time"]
87
-
88
- df_interval = df[
89
- (
90
- ((df["Start (s)"] >= MIN_TIME) & (df["Start (s)"] <= MAX_TIME))
91
- | ((df["Stop (s)"] >= MIN_TIME) & (df["Stop (s)"] <= MAX_TIME))
92
- )
93
- | ((df["Start (s)"] < MIN_TIME) & (df["Stop (s)"] > MAX_TIME))
94
- ]
95
-
96
- df_interval.loc[df["Start (s)"] < MIN_TIME, "Start (s)"] = MIN_TIME
97
- df_interval.loc[df["Stop (s)"] > MAX_TIME, "Stop (s)"] = MAX_TIME
98
-
99
- df_interval.loc[:, "Duration (s)"] = df_interval["Stop (s)"] - df_interval["Start (s)"]
100
-
101
- df = df_interval
102
-
103
- return run(df)
@@ -80,12 +80,12 @@ def create_behavior_binary_table(pj: dict, selected_observations: list, paramete
80
80
  end_time = dec(max_obs_length)
81
81
 
82
82
  if parameters_obs["time"] == cfg.TIME_OBS_INTERVAL:
83
- obs_interval = pj[cfg.OBSERVATIONS][obs_id][cfg.OBSERVATION_TIME_INTERVAL]
83
+ obs_interval = pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
84
84
  offset = pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET]
85
85
  start_time = dec(obs_interval[0]) + offset
86
86
  # Use max observation length for end time if no interval is defined (=0)
87
87
  max_obs_length, _ = observation_operations.observation_length(pj, [obs_id])
88
- end_time = dec(obs_interval[1]) + offset if obs_interval[1] != 0 else dec(max_obs_length)
88
+ end_time = dec(obs_interval[1]) + offset if obs_interval[1] not in (0, None) else dec(max_obs_length)
89
89
 
90
90
  if obs_id not in results_df:
91
91
  results_df[obs_id] = {}
@@ -166,8 +166,8 @@ def behavior_binary_table(self):
166
166
  None,
167
167
  cfg.programName,
168
168
  (
169
- "Depending of the length of your observations "
170
- "the execution of this function may be very long.<br>"
169
+ "Depending on the length of yours observations "
170
+ "the execution of this function may take a long time.<br>"
171
171
  "The program interface may freeze, be patient. <br>"
172
172
  ),
173
173
  )
@@ -224,7 +224,7 @@ def behavior_binary_table(self):
224
224
  by_category=False,
225
225
  n_observations=len(selected_observations),
226
226
  )
227
- if parameters == {}:
227
+ if not parameters:
228
228
  return
229
229
  if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
230
230
  QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
@@ -271,7 +271,9 @@ def connections(self):
271
271
  self.actionViewBehavior.triggered.connect(self.view_behavior)
272
272
  self.twEthogram.addAction(self.actionViewBehavior)
273
273
 
274
- self.actionFilterBehaviors.triggered.connect(lambda: self.filter_behaviors(table=cfg.ETHOGRAM))
274
+ self.actionFilterBehaviors.triggered.connect(
275
+ lambda: self.filter_behaviors(table=cfg.ETHOGRAM, behavior_type=cfg.STATE_EVENT_TYPES + cfg.POINT_EVENT_TYPES)
276
+ )
275
277
  self.twEthogram.addAction(self.actionFilterBehaviors)
276
278
 
277
279
  self.actionShowAllBehaviors.triggered.connect(self.show_all_behaviors)
@@ -98,6 +98,7 @@ def get_cooccurence(self):
98
98
 
99
99
  start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
100
100
 
101
+ # loop on choose subjects /behaviors until parameters are OK
101
102
  while True:
102
103
  flag_ok: bool = True
103
104
  parameters = select_subj_behav.choose_obs_subj_behav_category(
@@ -105,15 +106,17 @@ def get_cooccurence(self):
105
106
  selected_observations,
106
107
  start_coding=start_coding,
107
108
  end_coding=end_coding,
108
- start_interval=start_interval,
109
- end_interval=end_interval,
109
+ # start_interval=start_interval,
110
+ # end_interval=end_interval,
111
+ start_interval=None,
112
+ end_interval=None,
110
113
  maxTime=max_media_duration_all_obs,
111
114
  n_observations=len(selected_observations),
112
115
  show_include_modifiers=False,
113
116
  show_exclude_non_coded_behaviors=True,
114
117
  )
115
118
 
116
- if parameters == {}: # cancel button pressed
119
+ if not parameters: # cancel button pressed
117
120
  return
118
121
 
119
122
  if not parameters[cfg.SELECTED_SUBJECTS]:
@@ -34,7 +34,6 @@ import json
34
34
  import logging
35
35
  import pathlib as pl
36
36
  import platform
37
- import importlib
38
37
  import re
39
38
  import PIL.Image
40
39
  import PIL.ImageEnhance
@@ -52,7 +51,6 @@ from decimal import Decimal as dec
52
51
  from decimal import ROUND_DOWN
53
52
  import gzip
54
53
  from collections import deque
55
- import pandas as pd
56
54
  import matplotlib
57
55
  import zipfile
58
56
  import shutil
@@ -129,7 +127,6 @@ from . import config_file
129
127
  from . import select_subj_behav
130
128
  from . import observation_operations
131
129
  from . import write_event
132
- from . import view_df
133
130
 
134
131
 
135
132
  # matplotlib.pyplot.switch_backend("Qt5Agg")
@@ -797,10 +794,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
797
794
 
798
795
  def filter_behaviors(
799
796
  self,
800
- title="Select the behaviors to show in the ethogram table",
801
- text="Behaviors to show in ethogram list",
802
- table=cfg.ETHOGRAM,
803
- behavior_type=cfg.STATE_EVENT_TYPES,
797
+ title: str = "Select the behaviors to show in the ethogram table",
798
+ text: str = "Behaviors to show in ethogram list",
799
+ table: str = cfg.ETHOGRAM,
800
+ behavior_type: list = cfg.STATE_EVENT_TYPES,
804
801
  ) -> Tuple[bool, list]:
805
802
  """
806
803
  allow user to:
@@ -812,6 +809,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
812
809
  title (str): title of dialog box
813
810
  text (str): text of dialog box
814
811
  table (str): table where behaviors will be filtered
812
+ behavior_type
815
813
 
816
814
  Returns:
817
815
  (None if table = ETHOGRAM)
@@ -861,21 +859,22 @@ class MainWindow(QMainWindow, Ui_MainWindow):
861
859
  categories = ["###no category###"]
862
860
 
863
861
  for category in categories:
864
- if category != "###no category###":
865
- if category == "":
866
- paramPanelWindow.item = QListWidgetItem("No category")
867
- paramPanelWindow.item.setData(34, "No category")
868
- else:
869
- paramPanelWindow.item = QListWidgetItem(category)
870
- paramPanelWindow.item.setData(34, category)
862
+ if category == "###no category###":
863
+ continue
864
+ if category == "":
865
+ paramPanelWindow.item = QListWidgetItem("No category")
866
+ paramPanelWindow.item.setData(34, "No category")
867
+ else:
868
+ paramPanelWindow.item = QListWidgetItem(category)
869
+ paramPanelWindow.item.setData(34, category)
871
870
 
872
- font = QFont()
873
- font.setBold(True)
874
- paramPanelWindow.item.setFont(font)
875
- paramPanelWindow.item.setData(33, "category")
876
- paramPanelWindow.item.setData(35, False)
871
+ font = QFont()
872
+ font.setBold(True)
873
+ paramPanelWindow.item.setFont(font)
874
+ paramPanelWindow.item.setData(33, "category")
875
+ paramPanelWindow.item.setData(35, False)
877
876
 
878
- paramPanelWindow.lwBehaviors.addItem(paramPanelWindow.item)
877
+ paramPanelWindow.lwBehaviors.addItem(paramPanelWindow.item)
879
878
 
880
879
  # check if behavior type must be shown
881
880
  for behavior in [self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])]:
@@ -3634,6 +3633,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3634
3633
  else:
3635
3634
  current_time = self.getLaps()
3636
3635
 
3636
+ # check if observation time interval
3637
+ if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[1]:
3638
+ # check if current time outside of interval
3639
+ if current_time >= self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[1]:
3640
+ self.beep("beep")
3641
+ self.liveTimer.stop()
3642
+ self.liveObservationStarted = False
3643
+ # set current time to end of observation interval
3644
+ current_time = dec(self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[1])
3645
+ self.pb_live_obs.setText("Live observation finished")
3646
+
3637
3647
  self.lb_current_media_time.setText(util.convertTime(self.timeFormat, current_time))
3638
3648
 
3639
3649
  # extract State events
@@ -3663,14 +3673,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3663
3673
  self.liveTimer.stop()
3664
3674
  self.pb_live_obs.setText("Live observation stopped (scan sampling)")
3665
3675
 
3666
- # observation time interval
3667
- if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[1]:
3668
- if current_time >= self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[1]:
3669
- self.beep("beep")
3670
- self.liveTimer.stop()
3671
- self.liveObservationStarted = False
3672
- self.pb_live_obs.setText("Live observation finished")
3673
-
3674
3676
  def start_live_observation(self):
3675
3677
  """
3676
3678
  activate the live observation mode
@@ -3772,8 +3774,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3772
3774
  selected_observations,
3773
3775
  start_coding=start_coding,
3774
3776
  end_coding=end_coding,
3775
- start_interval=start_interval,
3776
- end_interval=end_interval,
3777
+ # start_interval=start_interval,
3778
+ # end_interval=end_interval,
3779
+ start_interval=None,
3780
+ end_interval=None,
3777
3781
  maxTime=max_media_duration_all_obs,
3778
3782
  show_include_modifiers=False,
3779
3783
  show_exclude_non_coded_behaviors=False,
@@ -5750,10 +5754,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5750
5754
  if self.playerType == cfg.MEDIA:
5751
5755
  self.pause_video()
5752
5756
 
5753
- if cfg.OBSERVATION_TIME_INTERVAL in self.pj[cfg.OBSERVATIONS][self.observationId]:
5754
- self.seek_mediaplayer(int(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.OBSERVATION_TIME_INTERVAL][0]))
5755
- else:
5756
- self.seek_mediaplayer(0)
5757
+ self.seek_mediaplayer(int(self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[0]))
5757
5758
 
5758
5759
  self.update_visualizations()
5759
5760
 
@@ -5762,6 +5763,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5762
5763
  self.extract_frame(self.dw_player[0])
5763
5764
 
5764
5765
  def obs_param(self):
5766
+ """
5767
+ allow user to select observations and then subjects and behaviors
5768
+ """
5765
5769
  _, selected_observations = select_observations.select_observations2(self, mode=cfg.MULTIPLE, windows_title="")
5766
5770
 
5767
5771
  if not selected_observations:
@@ -5786,8 +5790,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5786
5790
 
5787
5791
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
5788
5792
 
5793
+ print(f"{start_coding=}")
5794
+ print(f"{end_coding=}")
5795
+
5789
5796
  start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
5790
5797
 
5798
+ print(f"{start_interval=}")
5799
+ print(f"{end_interval=}")
5800
+
5791
5801
  parameters: dict = select_subj_behav.choose_obs_subj_behav_category(
5792
5802
  self,
5793
5803
  selected_observations,
@@ -5803,6 +5813,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5803
5813
  if parameters == {}:
5804
5814
  return [], {}
5805
5815
 
5816
+ print(f"{parameters=}")
5817
+
5806
5818
  if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
5807
5819
  QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
5808
5820
  return [], {}
@@ -5810,94 +5822,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5810
5822
  logging.debug(f"{parameters=}")
5811
5823
  return selected_observations, parameters
5812
5824
 
5813
- def run_plugin(self):
5814
- """
5815
- run plugin
5816
- """
5817
- if not self.project:
5818
- QMessageBox.warning(
5819
- self,
5820
- cfg.programName,
5821
- "No observations found. Open a project first",
5822
- QMessageBox.Ok | QMessageBox.Default,
5823
- QMessageBox.NoButton,
5824
- )
5825
- return
5826
-
5827
- logging.debug(f"{self.config_param.get(cfg.ANALYSIS_PLUGINS, {})=}")
5828
-
5829
- plugin_name = self.sender().text()
5830
- if plugin_name not in self.config_param.get(cfg.ANALYSIS_PLUGINS, {}):
5831
- QMessageBox.critical(self, cfg.programName, f"Plugin '{plugin_name}' not found")
5832
- return
5833
-
5834
- plugin_path = self.config_param.get(cfg.ANALYSIS_PLUGINS, {})[plugin_name]
5835
-
5836
- logging.debug(f"{plugin_path=}")
5837
-
5838
- if not pl.Path(plugin_path).is_file():
5839
- QMessageBox.critical(self, cfg.programName, f"The plugin {plugin_path} was not found.")
5840
- return
5841
-
5842
- logging.debug(f"run plugin from {plugin_path}")
5843
-
5844
- module_name = pl.Path(plugin_path).stem
5845
-
5846
- spec = importlib.util.spec_from_file_location(module_name, plugin_path)
5847
- plugin_module = importlib.util.module_from_spec(spec)
5848
-
5849
- logging.debug(f"{plugin_module=}")
5850
-
5851
- spec.loader.exec_module(plugin_module)
5852
-
5853
- logging.info(
5854
- f"{plugin_module.__plugin_name__} loaded v.{getattr(plugin_module, '__version__')} v. {getattr(plugin_module, '__version_date__')}"
5855
- )
5856
-
5857
- selected_observations, parameters = self.obs_param()
5858
- if not selected_observations:
5859
- return
5860
-
5861
- df = project_functions.project2dataframe(self.pj, selected_observations)
5862
-
5863
- logging.debug("dataframe info")
5864
- logging.debug(f"{df.info()}")
5865
- logging.debug(f"{df.head()}")
5866
-
5867
- # df_results, str_results = plugin_module.main(df, observations_list=selected_observations, parameters=parameters)
5868
-
5869
- plugin_results = plugin_module.main(df, observations_list=selected_observations, parameters=parameters)
5870
- # test if tuple: if not transform to tuple
5871
- if not isinstance(plugin_results, tuple):
5872
- plugin_results = tuple([plugin_results])
5873
-
5874
- self.plugin_visu: list = []
5875
- for result in plugin_results:
5876
- if isinstance(result, str):
5877
- self.plugin_visu.append(dialog.Results_dialog())
5878
- self.plugin_visu[-1].setWindowTitle(self.sender().text())
5879
- self.plugin_visu[-1].ptText.clear()
5880
- self.plugin_visu[-1].ptText.appendPlainText(result)
5881
- self.plugin_visu[-1].show()
5882
- elif isinstance(result, pd.DataFrame):
5883
- self.plugin_visu.append(
5884
- view_df.View_df(self.sender().text(), f"{plugin_module.__version__} ({plugin_module.__version_date__})", result)
5885
- )
5886
- self.plugin_visu[-1].show()
5887
- else:
5888
- # result is not str nor dataframe
5889
- QMessageBox.critical(
5890
- None,
5891
- cfg.programName,
5892
- (
5893
- f"Plugin returns an unknown object type: {type(result)}\n\n"
5894
- "Plugins must return str and/or Pandas Dataframes.\n"
5895
- "Check the plugin code."
5896
- ),
5897
- QMessageBox.Ok | QMessageBox.Default,
5898
- QMessageBox.NoButton,
5899
- )
5900
-
5901
5825
 
5902
5826
  def main():
5903
5827
  # QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
@@ -76,7 +76,7 @@ def export_events_as_behavioral_sequences(self, separated_subjects=False, timed=
76
76
  else:
77
77
  max_media_duration_all_obs = None
78
78
  start_coding, end_coding = dec("NaN"), dec("NaN")
79
- start_interval, end_interval = dec("NaN"), dec("NaN")
79
+ start_interval, end_interval = None, None
80
80
 
81
81
  parameters = select_subj_behav.choose_obs_subj_behav_category(
82
82
  self,
@@ -169,7 +169,7 @@ def export_tabular_events(self, mode: str = "tabular") -> None:
169
169
  else:
170
170
  max_media_duration_all_obs = None
171
171
  start_coding, end_coding = dec("NaN"), dec("NaN")
172
- start_interval, end_interval = dec("NaN"), dec("NaN")
172
+ start_interval, end_interval = None, None
173
173
 
174
174
  parameters = select_subj_behav.choose_obs_subj_behav_category(
175
175
  self,
@@ -373,7 +373,7 @@ def export_aggregated_events(self):
373
373
  else:
374
374
  max_media_duration_all_obs = None
375
375
  start_coding, end_coding = dec("NaN"), dec("NaN")
376
- start_interval, end_interval = dec("NaN"), dec("NaN")
376
+ start_interval, end_interval = None, None
377
377
 
378
378
  parameters = select_subj_behav.choose_obs_subj_behav_category(
379
379
  self,
@@ -681,8 +681,10 @@ def export_events_as_textgrid(self) -> None:
681
681
  selected_observations,
682
682
  start_coding=start_coding,
683
683
  end_coding=end_coding,
684
- start_interval=start_interval,
685
- end_interval=end_interval,
684
+ # start_interval=start_interval,
685
+ # end_interval=end_interval,
686
+ start_interval=None,
687
+ end_interval=None,
686
688
  show_include_modifiers=False,
687
689
  show_exclude_non_coded_behaviors=False,
688
690
  maxTime=max_obs_length,
@@ -786,7 +788,7 @@ def export_events_as_textgrid(self) -> None:
786
788
 
787
789
  if parameters["time"] == cfg.TIME_OBS_INTERVAL:
788
790
  max_media_duration, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], [obs_id])
789
- obs_interval = self.pj[cfg.OBSERVATIONS][obs_id][cfg.OBSERVATION_TIME_INTERVAL]
791
+ obs_interval = self.pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
790
792
  offset = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET])
791
793
  min_time = float(obs_interval[0]) + offset
792
794
  # Use max media duration for max time if no interval is defined (=0)
@@ -121,7 +121,10 @@ def export_events_jwatcher(
121
121
  except Exception:
122
122
  # coded behavior not defined in ethogram
123
123
  continue
124
- if [ethogram[k][cfg.TYPE] for k in ethogram if ethogram[k][cfg.BEHAVIOR_CODE] == behav_code] == [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],
127
+ ]:
125
128
  if behav_code in mem_number_of_state_events:
126
129
  mem_number_of_state_events[behav_code] += 1
127
130
  else:
@@ -31,32 +31,40 @@ def save_geometry(widget: QWidget, widget_name: str):
31
31
  save window geometry in ini file
32
32
  """
33
33
 
34
- try:
35
- ini_file_path = pl.Path.home() / pl.Path(".boris")
36
- if ini_file_path.is_file():
34
+ ini_file_path = pl.Path.home() / pl.Path(".boris")
35
+ if ini_file_path.is_file():
36
+ try:
37
37
  settings = QSettings(str(ini_file_path), QSettings.IniFormat)
38
38
  settings.setValue(f"{widget_name} geometry", widget.saveGeometry())
39
- except Exception:
40
- logging.warning(f"error during saving {widget_name} geometry")
39
+ except Exception:
40
+ logging.warning(f"error during saving {widget_name} geometry")
41
41
 
42
42
 
43
- def restore_geometry(widget: QWidget, widget_name: str, default_geometry):
43
+ def restore_geometry(widget: QWidget, widget_name: str, default_width_height):
44
44
  """
45
45
  restore window geometry in ini file
46
46
  """
47
+ def default_resize(widget, default_width_height):
48
+ if default_width_height != (0, 0):
49
+ try:
50
+ widget.resize(default_width_height[0], default_width_height[1])
51
+ except Exception:
52
+ logging.warning("Error during restoring default")
53
+
47
54
 
55
+ logging.debug(f'restore geometry function for {widget_name}')
48
56
  try:
49
57
  ini_file_path = pl.Path.home() / pl.Path(".boris")
50
58
  if ini_file_path.is_file():
51
59
  settings = QSettings(str(ini_file_path), QSettings.IniFormat)
60
+ print(settings.value(f"{widget_name} geometry"))
52
61
  widget.restoreGeometry(settings.value(f"{widget_name} geometry"))
62
+ logging.debug(f'geometry restored for {widget_name} {settings.value(f"{widget_name} geometry")}')
63
+ else:
64
+ default_resize(widget, default_width_height)
53
65
  except Exception:
54
66
  logging.warning(f"error during restoring {widget_name} geometry")
55
- if default_geometry != (0, 0):
56
- try:
57
- widget.resize(default_geometry[0], default_geometry[1])
58
- except Exception:
59
- logging.warning("Error during restoring default")
67
+ default_resize(widget, default_width_height)
60
68
 
61
69
 
62
70
  def set_icons(self, theme_mode: str) -> None: