boris-behav-obs 9.1__tar.gz → 9.2__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 (122) hide show
  1. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/PKG-INFO +1 -1
  2. boris_behav_obs-9.2/boris/analysis_plugins/number_of_occurences.py +22 -0
  3. boris_behav_obs-9.2/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +34 -0
  4. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/analysis_plugins/time_budget.py +9 -47
  5. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/connections.py +3 -1
  6. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/cooccurence.py +2 -1
  7. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/core.py +11 -91
  8. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/dialog.py +9 -5
  9. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/edit_event.py +3 -8
  10. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/event_operations.py +17 -9
  11. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/observation_operations.py +5 -5
  12. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/param_panel.py +6 -0
  13. boris_behav_obs-9.2/boris/plugins.py +247 -0
  14. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/project_functions.py +37 -22
  15. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/select_subj_behav.py +4 -0
  16. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/version.py +2 -2
  17. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris_behav_obs.egg-info/PKG-INFO +1 -1
  18. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/pyproject.toml +2 -2
  19. boris_behav_obs-9.1/boris/analysis_plugins/number_of_occurences.py +0 -62
  20. boris_behav_obs-9.1/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -74
  21. boris_behav_obs-9.1/boris/plugins.py +0 -79
  22. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/LICENSE.TXT +0 -0
  23. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/MANIFEST.in +0 -0
  24. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/README.TXT +0 -0
  25. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/README.md +0 -0
  26. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/__init__.py +0 -0
  27. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/__main__.py +0 -0
  28. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/about.py +0 -0
  29. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/add_modifier.py +0 -0
  30. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/add_modifier_ui.py +0 -0
  31. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/advanced_event_filtering.py +0 -0
  32. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/analysis_plugins/__init__.py +0 -0
  33. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/behav_coding_map_creator.py +0 -0
  34. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/behavior_binary_table.py +0 -0
  35. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/behaviors_coding_map.py +0 -0
  36. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/boris_cli.py +0 -0
  37. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/cmd_arguments.py +0 -0
  38. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/coding_pad.py +0 -0
  39. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/config.py +0 -0
  40. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/config_file.py +0 -0
  41. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/converters.py +0 -0
  42. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/converters_ui.py +0 -0
  43. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/core_qrc.py +0 -0
  44. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/core_ui.py +0 -0
  45. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/db_functions.py +0 -0
  46. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/dev.py +0 -0
  47. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/duration_widget.py +0 -0
  48. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/edit_event_ui.py +0 -0
  49. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/events_cursor.py +0 -0
  50. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/events_snapshots.py +0 -0
  51. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/exclusion_matrix.py +0 -0
  52. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/export_events.py +0 -0
  53. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/export_observation.py +0 -0
  54. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/external_processes.py +0 -0
  55. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/geometric_measurement.py +0 -0
  56. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/gui_utilities.py +0 -0
  57. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/image_overlay.py +0 -0
  58. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/import_observations.py +0 -0
  59. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/irr.py +0 -0
  60. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/latency.py +0 -0
  61. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/map_creator.py +0 -0
  62. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/measurement_widget.py +0 -0
  63. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/media_file.py +0 -0
  64. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/menu_options.py +0 -0
  65. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/modifiers_coding_map.py +0 -0
  66. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/mpv-1.0.3.py +0 -0
  67. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/mpv.py +0 -0
  68. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/mpv2.py +0 -0
  69. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/observation.py +0 -0
  70. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/observation_ui.py +0 -0
  71. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/observations_list.py +0 -0
  72. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/otx_parser.py +0 -0
  73. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/param_panel_ui.py +0 -0
  74. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/player_dock_widget.py +0 -0
  75. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/plot_data_module.py +0 -0
  76. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/plot_events.py +0 -0
  77. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/plot_events_rt.py +0 -0
  78. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/plot_spectrogram_rt.py +0 -0
  79. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/plot_waveform_rt.py +0 -0
  80. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/portion/__init__.py +0 -0
  81. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/portion/const.py +0 -0
  82. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/portion/dict.py +0 -0
  83. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/portion/func.py +0 -0
  84. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/portion/interval.py +0 -0
  85. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/portion/io.py +0 -0
  86. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/preferences.py +0 -0
  87. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/preferences_ui.py +0 -0
  88. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/project.py +0 -0
  89. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/project_import_export.py +0 -0
  90. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/project_ui.py +0 -0
  91. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/qrc_boris.py +0 -0
  92. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/qrc_boris5.py +0 -0
  93. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/select_modifiers.py +0 -0
  94. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/select_observations.py +0 -0
  95. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/state_events.py +0 -0
  96. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/subjects_pad.py +0 -0
  97. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/synthetic_time_budget.py +0 -0
  98. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/time_budget_functions.py +0 -0
  99. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/time_budget_widget.py +0 -0
  100. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/transitions.py +0 -0
  101. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/utilities.py +0 -0
  102. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/video_equalizer.py +0 -0
  103. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/video_equalizer_ui.py +0 -0
  104. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/video_operations.py +0 -0
  105. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/view_df.py +0 -0
  106. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/view_df_ui.py +0 -0
  107. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris/write_event.py +0 -0
  108. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris_behav_obs.egg-info/SOURCES.txt +0 -0
  109. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
  110. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris_behav_obs.egg-info/entry_points.txt +0 -0
  111. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris_behav_obs.egg-info/requires.txt +0 -0
  112. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/boris_behav_obs.egg-info/top_level.txt +0 -0
  113. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/setup.cfg +0 -0
  114. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_db_functions.py +0 -0
  115. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_export_observation.py +0 -0
  116. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_irr.py +0 -0
  117. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_observation_gui.py +0 -0
  118. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_otx_parser.py +0 -0
  119. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_preferences_gui.py +0 -0
  120. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_project_functions.py +0 -0
  121. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_time_budget.py +0 -0
  122. {boris_behav_obs-9.1 → boris_behav_obs-9.2}/tests/test_utilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: boris-behav-obs
3
- Version: 9.1
3
+ Version: 9.2
4
4
  Summary: BORIS - Behavioral Observation Research Interactive Software
5
5
  Author-email: Olivier Friard <olivier.friard@unito.it>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -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)
@@ -160,7 +160,9 @@ def connections(self):
160
160
  self.actionAll_transitions.triggered.connect(lambda: transitions.transitions_matrix(self, "frequency"))
161
161
  self.actionNumber_of_transitions.triggered.connect(lambda: transitions.transitions_matrix(self, "number"))
162
162
 
163
- self.actionFrequencies_of_transitions_after_behaviors.triggered.connect(lambda: self.transitions_matrix("frequencies_after_behaviors"))
163
+ self.actionFrequencies_of_transitions_after_behaviors.triggered.connect(
164
+ lambda: transitions.transitions_matrix(self, "frequencies_after_behaviors")
165
+ )
164
166
 
165
167
  # menu playback
166
168
  self.actionJumpTo.triggered.connect(self.jump_to)
@@ -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(
@@ -113,7 +114,7 @@ def get_cooccurence(self):
113
114
  show_exclude_non_coded_behaviors=True,
114
115
  )
115
116
 
116
- if parameters == {}: # cancel button pressed
117
+ if not parameters: # cancel button pressed
117
118
  return
118
119
 
119
120
  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")
@@ -5762,6 +5759,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5762
5759
  self.extract_frame(self.dw_player[0])
5763
5760
 
5764
5761
  def obs_param(self):
5762
+ """
5763
+ allow user to select observations and then subjects and behaviors
5764
+ """
5765
5765
  _, selected_observations = select_observations.select_observations2(self, mode=cfg.MULTIPLE, windows_title="")
5766
5766
 
5767
5767
  if not selected_observations:
@@ -5786,8 +5786,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5786
5786
 
5787
5787
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
5788
5788
 
5789
+ print(f"{start_coding=}")
5790
+ print(f"{end_coding=}")
5791
+
5789
5792
  start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
5790
5793
 
5794
+ print(f"{start_interval=}")
5795
+ print(f"{end_interval=}")
5796
+
5791
5797
  parameters: dict = select_subj_behav.choose_obs_subj_behav_category(
5792
5798
  self,
5793
5799
  selected_observations,
@@ -5803,6 +5809,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5803
5809
  if parameters == {}:
5804
5810
  return [], {}
5805
5811
 
5812
+ print(f"{parameters=}")
5813
+
5806
5814
  if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
5807
5815
  QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
5808
5816
  return [], {}
@@ -5810,94 +5818,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5810
5818
  logging.debug(f"{parameters=}")
5811
5819
  return selected_observations, parameters
5812
5820
 
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
5821
 
5902
5822
  def main():
5903
5823
  # QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
@@ -20,15 +20,16 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
+ from decimal import Decimal as dec
24
+ from typing import Union
25
+ import datetime as dt
23
26
  import logging
24
- import sys
27
+ import math
25
28
  import pathlib as pl
26
- import traceback
27
29
  import platform
28
- import datetime as dt
29
30
  import subprocess
30
- from decimal import Decimal as dec
31
- from typing import Union
31
+ import sys
32
+ import traceback
32
33
 
33
34
  from PySide6.QtCore import Qt, Signal, qVersion, QRect, QTime, QDateTime, QSize
34
35
  from PySide6.QtWidgets import (
@@ -409,6 +410,9 @@ class get_time_widget(QWidget):
409
410
  set time on time widget
410
411
  """
411
412
 
413
+ if math.isnan(new_time):
414
+ return
415
+
412
416
  self.pb_sign.setText("-" if new_time < 0 else "+")
413
417
 
414
418
  # seconds
@@ -21,6 +21,7 @@ This file is part of BORIS.
21
21
  """
22
22
 
23
23
  from decimal import Decimal as dec
24
+ import math
24
25
 
25
26
  from PySide6.QtWidgets import (
26
27
  QDialog,
@@ -67,14 +68,7 @@ class DlgEditEvent(QDialog, Ui_Form):
67
68
  for w in (self.lb_frame_idx, self.sb_frame_idx, self.cb_set_frame_idx_na):
68
69
  w.setVisible(False)
69
70
 
70
- if (observation_type in (cfg.LIVE, cfg.MEDIA)) or (observation_type == cfg.IMAGES and self.time_value != cfg.NA):
71
- # self.time_widget = duration_widget.Duration_widget(self.time_value)
72
- # if time_format == cfg.S:
73
- # self.time_widget.set_format_s()
74
- # if time_format == cfg.HHMMSS:
75
- # self.time_widget.set_format_hhmmss()
76
-
77
- # future time widget
71
+ if (observation_type in (cfg.LIVE, cfg.MEDIA)) or (observation_type == cfg.IMAGES and not math.isnan(self.time_value)):
78
72
  self.time_widget = dialog.get_time_widget(self.time_value)
79
73
 
80
74
  if time_format == cfg.S:
@@ -87,6 +81,7 @@ class DlgEditEvent(QDialog, Ui_Form):
87
81
  self.horizontalLayout_2.insertWidget(0, self.time_widget)
88
82
 
89
83
  if observation_type == cfg.IMAGES:
84
+ self.time_widget = dialog.get_time_widget(self.time_value)
90
85
  # hide frame index widgets
91
86
  for w in (self.lb_frame_idx, self.sb_frame_idx, self.cb_set_frame_idx_na, self.pb_set_to_current_time):
92
87
  w.setVisible(False)
@@ -78,10 +78,13 @@ def add_event(self):
78
78
  )
79
79
  editWindow.setWindowTitle("Add a new event")
80
80
 
81
- sortedSubjects = [""] + sorted([self.pj[cfg.SUBJECTS][x][cfg.SUBJECT_NAME] for x in self.pj[cfg.SUBJECTS]])
81
+ sortedSubjects = [cfg.NO_FOCAL_SUBJECT] + sorted([self.pj[cfg.SUBJECTS][x][cfg.SUBJECT_NAME] for x in self.pj[cfg.SUBJECTS]])
82
+
83
+ print(f"{sortedSubjects=}")
82
84
 
83
85
  editWindow.cobSubject.addItems(sortedSubjects)
84
- editWindow.cobSubject.setCurrentIndex(editWindow.cobSubject.findText(self.currentSubject, Qt.MatchFixedString))
86
+ if self.currentSubject:
87
+ editWindow.cobSubject.setCurrentIndex(editWindow.cobSubject.findText(self.currentSubject, Qt.MatchFixedString))
85
88
 
86
89
  sortedCodes = sorted([self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in self.pj[cfg.ETHOGRAM]])
87
90
 
@@ -103,7 +106,9 @@ def add_event(self):
103
106
  if self.pj[cfg.ETHOGRAM][idx][cfg.BEHAVIOR_CODE] == editWindow.cobCode.currentText():
104
107
  event = self.full_event(idx)
105
108
 
106
- event[cfg.SUBJECT] = editWindow.cobSubject.currentText()
109
+ event[cfg.SUBJECT] = (
110
+ "" if editWindow.cobSubject.currentText() == cfg.NO_FOCAL_SUBJECT else editWindow.cobSubject.currentText()
111
+ )
107
112
  if editWindow.leComment.toPlainText():
108
113
  event[cfg.COMMENT] = editWindow.leComment.toPlainText()
109
114
 
@@ -152,7 +157,9 @@ def add_event(self):
152
157
  if self.pj[cfg.ETHOGRAM][idx][cfg.BEHAVIOR_CODE] == editWindow.cobCode.currentText():
153
158
  event = self.full_event(idx)
154
159
 
155
- event[cfg.SUBJECT] = editWindow.cobSubject.currentText()
160
+ event[cfg.SUBJECT] = (
161
+ "" if editWindow.cobSubject.currentText() == cfg.NO_FOCAL_SUBJECT else editWindow.cobSubject.currentText()
162
+ )
156
163
  if editWindow.leComment.toPlainText():
157
164
  event[cfg.COMMENT] = editWindow.leComment.toPlainText()
158
165
 
@@ -684,10 +691,9 @@ def edit_event(self):
684
691
  if self.pj[cfg.ETHOGRAM][key][cfg.BEHAVIOR_CODE] == edit_window.cobCode.currentText():
685
692
  event = self.full_event(key)
686
693
  # subject
687
- if edit_window.cobSubject.currentText() == cfg.NO_FOCAL_SUBJECT:
688
- event[cfg.SUBJECT] = ""
689
- else:
690
- event[cfg.SUBJECT] = edit_window.cobSubject.currentText()
694
+ event[cfg.SUBJECT] = (
695
+ "" if edit_window.cobSubject.currentText() == cfg.NO_FOCAL_SUBJECT else edit_window.cobSubject.currentText()
696
+ )
691
697
 
692
698
  event[cfg.COMMENT] = edit_window.leComment.toPlainText()
693
699
 
@@ -746,7 +752,9 @@ def edit_event(self):
746
752
  else:
747
753
  event[cfg.TIME] = edit_window.time_widget.get_time()
748
754
 
749
- event[cfg.SUBJECT] = edit_window.cobSubject.currentText()
755
+ event[cfg.SUBJECT] = (
756
+ "" if edit_window.cobSubject.currentText() == cfg.NO_FOCAL_SUBJECT else edit_window.cobSubject.currentText()
757
+ )
750
758
  event[cfg.COMMENT] = edit_window.leComment.toPlainText()
751
759
  event["row"] = pj_event_idx
752
760
  event["original_modifiers"] = self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][
@@ -43,9 +43,8 @@ from PySide6.QtWidgets import (
43
43
  QSlider,
44
44
  QMainWindow,
45
45
  QDockWidget,
46
- QPushButton,
47
46
  )
48
- from PySide6.QtCore import Qt, QDateTime, QTimer, QObject, QEvent
47
+ from PySide6.QtCore import Qt, QDateTime, QTimer
49
48
  from PySide6.QtGui import QFont, QIcon, QTextCursor
50
49
 
51
50
  from PySide6 import QtTest
@@ -365,8 +364,9 @@ def time_intervals_range(observations: dict, observations_list: list) -> Tuple[O
365
364
  for obs_id in observations_list:
366
365
  observation = observations[obs_id]
367
366
  offset = observation[cfg.TIME_OFFSET]
368
- start_interval_list.append(dec(observation[cfg.OBSERVATION_TIME_INTERVAL][0]) + offset)
369
- end_interval_list.append(dec(observation[cfg.OBSERVATION_TIME_INTERVAL][1]) + offset)
367
+ if dec(observation[cfg.OBSERVATION_TIME_INTERVAL][0]) + offset and dec(observation[cfg.OBSERVATION_TIME_INTERVAL][1]) + offset:
368
+ start_interval_list.append(dec(observation[cfg.OBSERVATION_TIME_INTERVAL][0]) + offset)
369
+ end_interval_list.append(dec(observation[cfg.OBSERVATION_TIME_INTERVAL][1]) + offset)
370
370
 
371
371
  if not start_interval_list:
372
372
  earliest_start_interval = None
@@ -376,7 +376,7 @@ def time_intervals_range(observations: dict, observations_list: list) -> Tuple[O
376
376
  if not end_interval_list:
377
377
  latest_end_interval = None
378
378
  else:
379
- latest_end_interval = min([x for x in end_interval_list])
379
+ latest_end_interval = max([x for x in end_interval_list])
380
380
 
381
381
  return earliest_start_interval, latest_end_interval
382
382
 
@@ -95,7 +95,13 @@ class Param_panel(QDialog, Ui_Dialog):
95
95
  if not ((self.start_interval is None) or self.start_interval.is_nan()):
96
96
  # Set start_time and end_time widgets values even if it is not shown with
97
97
  # more than 1 observation as some analyses might use it (eg: advanced event filtering)
98
+
99
+ print(f"{self.end_interval=}")
100
+
98
101
  end_interval = self.end_interval if self.end_interval != 0 else self.media_duration
102
+
103
+ print(f"{end_interval=}")
104
+
99
105
  self.start_time.set_time(self.start_interval)
100
106
  self.end_time.set_time(end_interval)
101
107
  self.frm_time_interval.setEnabled(False)