boris-behav-obs 9.2.1__py2.py3-none-any.whl → 9.2.3__py2.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.
@@ -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
  )
@@ -216,8 +216,6 @@ def behavior_binary_table(self):
216
216
  selected_observations,
217
217
  start_coding=start_coding,
218
218
  end_coding=end_coding,
219
- # start_interval=start_interval,
220
- # end_interval=end_interval,
221
219
  start_interval=start_interval,
222
220
  end_interval=end_interval,
223
221
  maxTime=max_media_duration_all_obs,
boris/connections.py CHANGED
@@ -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)
boris/core.py CHANGED
@@ -794,10 +794,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
794
794
 
795
795
  def filter_behaviors(
796
796
  self,
797
- title="Select the behaviors to show in the ethogram table",
798
- text="Behaviors to show in ethogram list",
799
- table=cfg.ETHOGRAM,
800
- 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,
801
801
  ) -> Tuple[bool, list]:
802
802
  """
803
803
  allow user to:
@@ -809,6 +809,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
809
809
  title (str): title of dialog box
810
810
  text (str): text of dialog box
811
811
  table (str): table where behaviors will be filtered
812
+ behavior_type
812
813
 
813
814
  Returns:
814
815
  (None if table = ETHOGRAM)
@@ -858,21 +859,22 @@ class MainWindow(QMainWindow, Ui_MainWindow):
858
859
  categories = ["###no category###"]
859
860
 
860
861
  for category in categories:
861
- if category != "###no category###":
862
- if category == "":
863
- paramPanelWindow.item = QListWidgetItem("No category")
864
- paramPanelWindow.item.setData(34, "No category")
865
- else:
866
- paramPanelWindow.item = QListWidgetItem(category)
867
- 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)
868
870
 
869
- font = QFont()
870
- font.setBold(True)
871
- paramPanelWindow.item.setFont(font)
872
- paramPanelWindow.item.setData(33, "category")
873
- 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)
874
876
 
875
- paramPanelWindow.lwBehaviors.addItem(paramPanelWindow.item)
877
+ paramPanelWindow.lwBehaviors.addItem(paramPanelWindow.item)
876
878
 
877
879
  # check if behavior type must be shown
878
880
  for behavior in [self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])]:
@@ -3639,10 +3641,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3639
3641
  self.liveTimer.stop()
3640
3642
  self.liveObservationStarted = False
3641
3643
  # set current time to end of observation interval
3642
- current_time = dec(self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[1])
3644
+ current_time = dec(self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[1])
3643
3645
  self.pb_live_obs.setText("Live observation finished")
3644
3646
 
3645
-
3646
3647
  self.lb_current_media_time.setText(util.convertTime(self.timeFormat, current_time))
3647
3648
 
3648
3649
  # extract State events
@@ -3672,7 +3673,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3672
3673
  self.liveTimer.stop()
3673
3674
  self.pb_live_obs.setText("Live observation stopped (scan sampling)")
3674
3675
 
3675
-
3676
3676
  def start_live_observation(self):
3677
3677
  """
3678
3678
  activate the live observation mode
@@ -5754,10 +5754,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5754
5754
  if self.playerType == cfg.MEDIA:
5755
5755
  self.pause_video()
5756
5756
 
5757
- if cfg.OBSERVATION_TIME_INTERVAL in self.pj[cfg.OBSERVATIONS][self.observationId]:
5758
- self.seek_mediaplayer(int(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.OBSERVATION_TIME_INTERVAL][0]))
5759
- else:
5760
- self.seek_mediaplayer(0)
5757
+ self.seek_mediaplayer(int(self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[0]))
5761
5758
 
5762
5759
  self.update_visualizations()
5763
5760
 
boris/export_events.py CHANGED
@@ -788,7 +788,7 @@ def export_events_as_textgrid(self) -> None:
788
788
 
789
789
  if parameters["time"] == cfg.TIME_OBS_INTERVAL:
790
790
  max_media_duration, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], [obs_id])
791
- 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])
792
792
  offset = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET])
793
793
  min_time = float(obs_interval[0]) + offset
794
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:
@@ -359,14 +359,20 @@ def time_intervals_range(observations: dict, observations_list: list) -> Tuple[O
359
359
  decimal.Decimal: time of latest end interval
360
360
 
361
361
  """
362
- start_interval_list = []
363
- end_interval_list = []
362
+ start_interval_list: list = []
363
+ end_interval_list: list = []
364
364
  for obs_id in observations_list:
365
365
  observation = observations[obs_id]
366
366
  offset = observation[cfg.TIME_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)
367
+ # check if observation interval is defined
368
+ if (
369
+ not observation.get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[0]
370
+ and not observation.get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[1]
371
+ ):
372
+ return None, None
373
+
374
+ start_interval_list.append(dec(observation[cfg.OBSERVATION_TIME_INTERVAL][0]) + offset)
375
+ end_interval_list.append(dec(observation[cfg.OBSERVATION_TIME_INTERVAL][1]) + offset)
370
376
 
371
377
  if not start_interval_list:
372
378
  earliest_start_interval = None
@@ -872,8 +878,8 @@ def new_observation(self, mode: str = cfg.NEW, obsId: str = "") -> None:
872
878
  observationWindow.cb_observation_time_interval.setText(
873
879
  (
874
880
  "Limit observation to a time interval: "
875
- f"{self.pj[cfg.OBSERVATIONS][obsId][cfg.OBSERVATION_TIME_INTERVAL][0]} - "
876
- f"{self.pj[cfg.OBSERVATIONS][obsId][cfg.OBSERVATION_TIME_INTERVAL][1]}"
881
+ f"{self.pj[cfg.OBSERVATIONS][obsId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[0]} - "
882
+ f"{self.pj[cfg.OBSERVATIONS][obsId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[1]}"
877
883
  )
878
884
  )
879
885
 
@@ -1950,8 +1956,9 @@ def initialize_new_media_observation(self) -> bool:
1950
1956
  )
1951
1957
 
1952
1958
  # position media
1953
- if cfg.OBSERVATION_TIME_INTERVAL in self.pj[cfg.OBSERVATIONS][self.observationId]:
1954
- self.seek_mediaplayer(int(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.OBSERVATION_TIME_INTERVAL][0]), player=i)
1959
+ self.seek_mediaplayer(
1960
+ int(self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[0]), player=i
1961
+ )
1955
1962
 
1956
1963
  # restore video zoom level
1957
1964
  if cfg.ZOOM_LEVEL in self.pj[cfg.OBSERVATIONS][self.observationId][cfg.MEDIA_INFO]:
@@ -23,7 +23,18 @@ This file is part of BORIS.
23
23
  import sys
24
24
  import logging
25
25
  import functools
26
- from PySide6.QtWidgets import QLabel, QDockWidget, QWidget, QHBoxLayout, QVBoxLayout, QSlider, QSizePolicy, QStackedWidget, QToolButton
26
+ from PySide6.QtWidgets import (
27
+ QApplication,
28
+ QLabel,
29
+ QDockWidget,
30
+ QWidget,
31
+ QHBoxLayout,
32
+ QVBoxLayout,
33
+ QSlider,
34
+ QSizePolicy,
35
+ QStackedWidget,
36
+ QToolButton,
37
+ )
27
38
  from PySide6.QtCore import Signal, QEvent, Qt
28
39
  from PySide6.QtGui import QIcon, QAction
29
40
 
@@ -141,7 +152,10 @@ class DW_player(QDockWidget):
141
152
  self.mute_button.setFocusPolicy(Qt.NoFocus)
142
153
  self.mute_button.setAutoRaise(True)
143
154
  self.mute_action = QAction()
144
- self.mute_action.setIcon(QIcon(":/volume_xmark"))
155
+
156
+ theme_mode = "dark" if QApplication.instance().palette().window().color().value() < 128 else "light"
157
+
158
+ self.mute_action.setIcon(QIcon(f":/volume_xmark_{theme_mode}"))
145
159
  self.mute_action.triggered.connect(self.mute_action_triggered)
146
160
  self.mute_button.setDefaultAction(self.mute_action)
147
161
 
@@ -180,10 +194,11 @@ class DW_player(QDockWidget):
180
194
  """
181
195
  emit signal when mute action is triggered
182
196
  """
197
+ theme_mode = "dark" if QApplication.instance().palette().window().color().value() < 128 else "light"
183
198
  if self.player.mute:
184
- self.mute_action.setIcon(QIcon(":/volume_xmark"))
199
+ self.mute_action.setIcon(QIcon(f":/volume_xmark_{theme_mode}"))
185
200
  else:
186
- self.mute_action.setIcon(QIcon(":/volume_off"))
201
+ self.mute_action.setIcon(QIcon(f":/volume_off_{theme_mode}"))
187
202
  self.mute_action_triggered_signal.emit(self.id_)
188
203
 
189
204
  def keyPressEvent(self, event):
boris/plot_events.py CHANGED
@@ -441,7 +441,7 @@ def create_events_plot(
441
441
  max_time = float(obs_length)
442
442
 
443
443
  if interval == cfg.TIME_OBS_INTERVAL:
444
- obs_interval = self.pj[cfg.OBSERVATIONS][obs_id][cfg.OBSERVATION_TIME_INTERVAL]
444
+ obs_interval = self.pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
445
445
  offset = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET])
446
446
  min_time = float(obs_interval[0]) + offset
447
447
  # Use max media duration for max time if no interval is defined (=0)
boris/plugins.py CHANGED
@@ -65,24 +65,44 @@ def load_plugins(self):
65
65
  """
66
66
  load selected plugins in analysis menu
67
67
  """
68
+
69
+ def msg():
70
+ QMessageBox.warning(
71
+ self,
72
+ cfg.programName,
73
+ f"A plugin with the same name is already loaded ({self.config_param[cfg.ANALYSIS_PLUGINS][plugin_name]}).\n\nThe plugin from {file_} is not loaded.",
74
+ QMessageBox.Ok | QMessageBox.Default,
75
+ QMessageBox.NoButton,
76
+ )
77
+
68
78
  self.menu_plugins.clear()
69
79
  self.config_param[cfg.ANALYSIS_PLUGINS] = {}
70
80
 
71
81
  # load BORIS plugins
72
- for file_ in (Path(__file__).parent / "analysis_plugins").glob("*.py"):
82
+ for file_ in sorted((Path(__file__).parent / "analysis_plugins").glob("*.py")):
73
83
  if file_.name == "__init__.py":
74
84
  continue
75
85
  plugin_name = get_plugin_name(file_)
76
86
  if plugin_name is not None and plugin_name not in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
87
+ # check if plugin with same name already loaded
88
+ if plugin_name in self.config_param[cfg.ANALYSIS_PLUGINS]:
89
+ msg()
90
+ continue
91
+
77
92
  self.config_param[cfg.ANALYSIS_PLUGINS][plugin_name] = str(file_)
78
93
 
79
94
  # load personal plugins
80
95
  if self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, ""):
81
- for file_ in Path(self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, "")).glob("*.py"):
96
+ for file_ in sorted(Path(self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, "")).glob("*.py")):
82
97
  if file_.name == "__init__.py":
83
98
  continue
84
99
  plugin_name = get_plugin_name(file_)
85
100
  if plugin_name is not None and plugin_name not in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
101
+ # check if plugin with same name already loaded
102
+ if plugin_name in self.config_param[cfg.ANALYSIS_PLUGINS]:
103
+ msg()
104
+ continue
105
+
86
106
  self.config_param[cfg.ANALYSIS_PLUGINS][plugin_name] = str(file_)
87
107
 
88
108
  logging.debug(f"{self.config_param.get(cfg.ANALYSIS_PLUGINS, {})=}")
@@ -108,7 +128,7 @@ def plugin_df_filter(df: pd.DataFrame, observations_list: list = [], parameters:
108
128
  # filter selected behaviors
109
129
  df = df[df["Behavior"].isin(parameters["selected behaviors"])]
110
130
 
111
- if parameters["time"] == "interval of observation":
131
+ if parameters["time"] == cfg.TIME_OBS_INTERVAL:
112
132
  # filter each observation with observation interval start/stop
113
133
 
114
134
  # keep events between observation interval start time and observation interval stop/end
@@ -142,6 +162,7 @@ def plugin_df_filter(df: pd.DataFrame, observations_list: list = [], parameters:
142
162
  | ((df["Start (s)"] < MIN_TIME) & (df["Stop (s)"] > MAX_TIME))
143
163
  ]
144
164
 
165
+ # cut state events to interval
145
166
  df_interval.loc[df["Start (s)"] < MIN_TIME, "Start (s)"] = MIN_TIME
146
167
  df_interval.loc[df["Stop (s)"] > MAX_TIME, "Stop (s)"] = MAX_TIME
147
168
 
@@ -152,7 +173,7 @@ def plugin_df_filter(df: pd.DataFrame, observations_list: list = [], parameters:
152
173
  print("filtered")
153
174
  print("=" * 50)
154
175
 
155
- print(f"{df=}")
176
+ # print(f"{df=}")
156
177
 
157
178
  return df
158
179
 
@@ -205,8 +226,12 @@ def run_plugin(self, plugin_name):
205
226
  if not selected_observations:
206
227
  return
207
228
 
229
+ logging.info("preparing dtaaframe for plugin")
230
+
208
231
  df = project_functions.project2dataframe(self.pj, selected_observations)
209
232
 
233
+ logging.info("done")
234
+
210
235
  """
211
236
  logging.debug("dataframe info")
212
237
  logging.debug(f"{df.info()}")
@@ -214,7 +239,9 @@ def run_plugin(self, plugin_name):
214
239
  """
215
240
 
216
241
  # filter the dataframe with parameters
242
+ logging.info("filtering dataframe for plugin")
217
243
  filtered_df = plugin_df_filter(df, observations_list=selected_observations, parameters=parameters)
244
+ logging.info("done")
218
245
 
219
246
  plugin_results = plugin_module.run(filtered_df)
220
247
  # test if plugin_tests is a tuple: if not transform to tuple
@@ -1899,20 +1899,12 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1899
1899
  if idx_event in stop_event_idx:
1900
1900
  continue
1901
1901
  data["Observation id"].append(obs_id)
1902
- data["Observation date"].append(pj["observations"][obs_id]["date"])
1903
- data["Description"].append(" ".join(pj["observations"][obs_id]["description"].splitlines()))
1904
- data["Observation type"].append(pj["observations"][obs_id]["type"])
1905
-
1906
- data["Observation interval start"].append(
1907
- pj["observations"][obs_id]["observation time interval"][0]
1908
- if pj["observations"][obs_id]["observation time interval"][0]
1909
- else None
1910
- )
1911
- data["Observation interval stop"].append(
1912
- pj["observations"][obs_id]["observation time interval"][1]
1913
- if pj["observations"][obs_id]["observation time interval"][1]
1914
- else None
1915
- )
1902
+ data["Observation date"].append(pj[cfg.OBSERVATIONS][obs_id]["date"])
1903
+ data["Description"].append(" ".join(pj[cfg.OBSERVATIONS][obs_id]["description"].splitlines()))
1904
+ data["Observation type"].append(pj[cfg.OBSERVATIONS][obs_id]["type"])
1905
+
1906
+ data["Observation interval start"].append(pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[0])
1907
+ data["Observation interval stop"].append(pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[1])
1916
1908
 
1917
1909
  # data["Source"].append("")
1918
1910
  # data["Time offset (s)"].append(pj["observations"][obs_id]["time offset"])
@@ -95,7 +95,7 @@ def synthetic_time_budget(self) -> None:
95
95
  title="Select behaviors to exclude from the total time",
96
96
  text="The duration of the selected behaviors will be subtracted from the total time",
97
97
  table="",
98
- behavior_type=[cfg.STATE_EVENT],
98
+ behavior_type=cfg.STATE_EVENT_TYPES,
99
99
  )
100
100
  if cancel_pressed:
101
101
  return
@@ -227,7 +227,7 @@ def synthetic_binned_time_budget(self) -> None:
227
227
  title="Select behaviors to exclude",
228
228
  text=("The duration of the selected behaviors will be subtracted from the total time"),
229
229
  table="",
230
- behavior_type=[cfg.STATE_EVENT],
230
+ behavior_type=cfg.STATE_EVENT_TYPES,
231
231
  )
232
232
  if cancel_pressed:
233
233
  return
@@ -260,7 +260,7 @@ def synthetic_time_budget_bin(pj: dict, selected_observations: list, parameters_
260
260
  max_time = dec(end_time)
261
261
 
262
262
  if time_interval == cfg.TIME_OBS_INTERVAL:
263
- obs_interval = pj[cfg.OBSERVATIONS][obs_id][cfg.OBSERVATION_TIME_INTERVAL]
263
+ obs_interval = pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
264
264
  offset = pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET]
265
265
  min_time = dec(obs_interval[0]) + offset
266
266
  # Use max media duration for max time if no interval is defined (=0)
@@ -504,7 +504,7 @@ def time_budget(self, mode: str, mode2: str = "list"):
504
504
  max_time = float(obs_length)
505
505
 
506
506
  if parameters[cfg.TIME_INTERVAL] == cfg.TIME_OBS_INTERVAL:
507
- obs_interval = self.pj[cfg.OBSERVATIONS][obsId][cfg.OBSERVATION_TIME_INTERVAL]
507
+ obs_interval = self.pj[cfg.OBSERVATIONS][obsId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
508
508
  offset = float(self.pj[cfg.OBSERVATIONS][obsId][cfg.TIME_OFFSET])
509
509
  min_time = float(obs_interval[0]) + offset
510
510
  # Use max media duration for max time if no interval is defined (=0)
boris/utilities.py CHANGED
@@ -1456,8 +1456,6 @@ def accurate_media_analysis(ffmpeg_bin: str, file_name: str) -> dict:
1456
1456
 
1457
1457
  ffprobe_results = ffprobe_media_analysis(ffmpeg_bin, file_name)
1458
1458
 
1459
- print(f"{ffprobe_results=}")
1460
-
1461
1459
  logging.debug(f"file_name: {file_name}")
1462
1460
  logging.debug(f"ffprobe_results: {ffprobe_results}")
1463
1461
 
boris/version.py CHANGED
@@ -20,5 +20,5 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
- __version__ = "9.2.1"
24
- __version_date__ = "2025-03-17"
23
+ __version__ = "9.2.3"
24
+ __version_date__ = "2025-04-02"
@@ -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
@@ -5,18 +5,18 @@ boris/add_modifier.py,sha256=DWqxkKDBm21QH_kPvhpnltwLtFvPxne0VmZ1SY26hj8,26340
5
5
  boris/add_modifier_ui.py,sha256=Y7TLO5uS6zW7zpjXmjA4V_VIp_bFDNtjOTbJ9Q6m-mQ,11601
6
6
  boris/advanced_event_filtering.py,sha256=VlvU12mL6xYacZOvJAi5uLpHMcmAw5Pvuvmka-PN29c,15469
7
7
  boris/behav_coding_map_creator.py,sha256=PSEYMLvOwt0HR7H4kQZl6dli3q89wezWMv-Ri3EZWWc,37443
8
- boris/behavior_binary_table.py,sha256=sr2Wb7NFd-NjfXCL5fqsc_Uf7rvS0p9RxZwVbaFBeig,12502
8
+ boris/behavior_binary_table.py,sha256=bpmRDpEjq0rw3YOCoN_He3kfUe8A_R6E48kQR7KnkH8,12453
9
9
  boris/behaviors_coding_map.py,sha256=Pat6U2JHxH8epWH42ZlwzZJ0Rmgl-BKQ5KARjp3GbNg,7291
10
10
  boris/boris_cli.py,sha256=n0OiVvZM1gM6E7yKaff9wlgmpAGK4TK052VRi8AabJo,13196
11
11
  boris/cmd_arguments.py,sha256=oWb-FvhKLbKJhATlTHy9muWu8XnnUfOZ-3Fmz2M8Yzc,1848
12
12
  boris/coding_pad.py,sha256=fBKdp7DDyupySJosIYtqNd8s2E-GruzCgVhDFsoVWKE,10986
13
13
  boris/config.py,sha256=cy4MSVFMHYaEHpHdncljuiaMSTpV6rFNm1OyVYF28Bc,17295
14
14
  boris/config_file.py,sha256=1-2ZmTvKET57rwrLR1dXblt0AxMpGB1LAiHxu-Sy8es,13543
15
- boris/connections.py,sha256=y_KkIj3WQQEepJASATHBpZaqwT-ERyFNovy2Q8e7FAA,19321
15
+ boris/connections.py,sha256=UflmxTLTAeJgtu2C8nMv37erVYWftZQULqwZ6RkD9WA,19396
16
16
  boris/converters.py,sha256=c1Jps-URoglY5ILQHz-pCCf6-4DFUHZLtqr_ofsrFg0,11722
17
17
  boris/converters_ui.py,sha256=uu7LOBV_fKv2DBdOqsqPwjGsjgONr5ODBoscAA-EP48,9900
18
18
  boris/cooccurence.py,sha256=tVERC-V8MWjWHlGEfDuu08iS94qjt4do-38jwI62QaY,10367
19
- boris/core.py,sha256=3NbJcsngnDTaLuXPWblgidKqjzZhJTxzL8N69pwgtkI,234818
19
+ boris/core.py,sha256=HWJVt1MHRjlMgsyz044tCCtT3c8F47n2kGSH_HGtDFw,234686
20
20
  boris/core_qrc.py,sha256=T3ki5e2Pj0I0QBGz63MPUgZzl8F_VHZwSq074mRNBDU,650669
21
21
  boris/core_ui.py,sha256=SeC26uveDCjrCBLsRPuQ6FaapKfON_HIRcQStEDLhl4,76384
22
22
  boris/db_functions.py,sha256=Uw9wWH_Pe-qNzpV1k21YG_jKsoOmfY_iiK_7ARZHGDc,13352
@@ -29,8 +29,8 @@ boris/event_operations.py,sha256=ys1DxZ6iooMvvq_hzpPVZSW0dF9Trbx921XV3jeKOxw,382
29
29
  boris/events_cursor.py,sha256=VPY_ygD0fxE5lp25mcd2l00XQXurCR6hprffF4tKRbU,2078
30
30
  boris/events_snapshots.py,sha256=PjWzQvUGQtIcEc_7FDsRphf7fAhhTccQgYc2eQSA65g,27621
31
31
  boris/exclusion_matrix.py,sha256=ff88xD6aqc8bpIuj9ZCz9ju_HeqqgibQwoaJrIOJ6RI,5272
32
- boris/export_events.py,sha256=8h6AenIuXJj-JWlkSS0UR38mhjUjmFv3_5QOyBSpYec,39744
33
- boris/export_observation.py,sha256=uPJH_5MXqGm4JSeaSlTikh1i6OKSyLIaZFzS9wp-9qk,50729
32
+ boris/export_events.py,sha256=3B336WEA0g_8oW3VDo_kfq5D0ISu-e7z2f-_ROUvU9c,39756
33
+ boris/export_observation.py,sha256=SvKhuGa-Ag_kK3igL9DFdJ0TKoQLDneu54R_uiSHUyo,50813
34
34
  boris/external_processes.py,sha256=vpmhA4Lj2GblBIrDD0YjesB8HPOgx4K9gSWVhTop4Cg,11927
35
35
  boris/geometric_measurement.py,sha256=4pI-AYpBSFlJBqS-f8dnkgLtj_Z2E5kwwAdh6WwZ4kk,35049
36
36
  boris/gui_utilities.py,sha256=5vjIWbUOHFbqKSti-kT0GoteBBEQ5fUYdNGdMxcg_0A,4607
@@ -47,23 +47,23 @@ boris/mpv-1.0.3.py,sha256=EXRtzQqFjOn4wMC6482Ilq3fNQ9N1GRP1VxwLzdeaBY,88077
47
47
  boris/mpv.py,sha256=EfzIHjPbgewG4w3smEtqEUPZoVwYmMQkL4Q8ZyW-a58,76410
48
48
  boris/mpv2.py,sha256=IUI4t4r9GYX7G5OXTjd3RhMMOkDdfal_15buBgksLsk,92152
49
49
  boris/observation.py,sha256=oop08nflDLZAgDbIB8GOiVdTgLhppJ_ODH0Z24cyqvE,57176
50
- boris/observation_operations.py,sha256=8rVC_iWb0Y8zkd64_XvDxD2e7_jsKdGfDSdyi4eDRAs,105643
50
+ boris/observation_operations.py,sha256=j7TSGzWSRmAdmD7vdrfvEBFtCfM0HXbo9VeIHNew2PY,105748
51
51
  boris/observation_ui.py,sha256=DAnU94QBNvkLuHT6AxTwqSk_D_n6VUhSl8PexZv_dUk,33309
52
52
  boris/observations_list.py,sha256=NqwECGHtHYmKhSe-qCfqPmJ24SSfzlXvIXS2i3op_zE,10591
53
53
  boris/otx_parser.py,sha256=70QvilzFHXbjAHR88YH0aEXJ3xxheLS5fZGgHFHGpNE,16367
54
54
  boris/param_panel.py,sha256=G0XzNmJIX89-n2OQTDccuY_wWMhr3p7GB4ZorbU6EWc,8786
55
55
  boris/param_panel_ui.py,sha256=4emQDFmuL4_R7bKxosLjdUb-VSPWkDm7suy38F5EKcA,13260
56
- boris/player_dock_widget.py,sha256=VBuuGoAb2D027oFnMBlIQPiSQcEdedhoQltNFSJq8mw,5982
56
+ boris/player_dock_widget.py,sha256=aDC6a7JEEY64TWMDU_jBBaavN-Z-fd9JQo9BnnNI4_M,6303
57
57
  boris/plot_data_module.py,sha256=6QbLKfyGp4TYRyHnB9G45y5XrpeXLytcorltEAWfYak,16562
58
- boris/plot_events.py,sha256=hA_Yt-ZaxbAOKjN7gYHiaEx5TPR-7HIWGJXXg4cq6lw,24048
58
+ boris/plot_events.py,sha256=CF6gnsTeaPG-P1USwh4An2s31NoMJ1roHDImcQrQj3c,24060
59
59
  boris/plot_events_rt.py,sha256=xig__Uea3mQqO5raMBVB3pm3vuQkjAbJpwSS7AwIob8,8327
60
60
  boris/plot_spectrogram_rt.py,sha256=JV8N7T8133wGVhlPxmgOb426u1g1p21-LbTqgaeddkk,8361
61
61
  boris/plot_waveform_rt.py,sha256=05JN_6HCq674ROore_6PNw93GQNZJQDlDxp2ODAFkkA,7474
62
- boris/plugins.py,sha256=RtC_Ew6VxAwWQqRuKQj9FgpsSBGoZmZuCZ5gPj0EiwI,8963
62
+ boris/plugins.py,sha256=FSeZdbGFPGfi5TDM3RQG6mEVwsp1cGKhJIZs-H0BMy4,9901
63
63
  boris/preferences.py,sha256=qPfd9Tyg7u30kXwVqMOgkdy2RXri9bItRa5U2-ZVQmg,16847
64
64
  boris/preferences_ui.py,sha256=D2bFLb3E0m6IwSeqKoItRDiqvPmJGoeXLHF2K02n1Zo,26293
65
65
  boris/project.py,sha256=hAeAb5pD0u_l0bezU9ePvbTOYQKfxrFGvYB2NAqSDHg,84377
66
- boris/project_functions.py,sha256=p_CC2UoFEbb0PpKt9c5MtJulSszz-fV1o1sgFRFVQEk,79920
66
+ boris/project_functions.py,sha256=mPaKTjcegsC6n-J8ZsOhWh_4TepJ3Y-vwXiIgl9nij0,79702
67
67
  boris/project_import_export.py,sha256=1FdsYFzZ_jrhPRaH7xEkcPnh-hQXN4HFz1PhsIsSoL8,38361
68
68
  boris/project_ui.py,sha256=yB-ewhHt8S8DTTRIk-dNK2tPMNU24lNji9fDW_Xazu8,38805
69
69
  boris/qrc_boris.py,sha256=aH-qUirYY1CGxmTK1SFCPvuZfazIHX4DdUKF1gxZeYM,675008
@@ -73,12 +73,12 @@ boris/select_observations.py,sha256=HM0suMes1YxVwQ-Xakw7ROaxbN0EyeSiZFZNrTQIHAA,
73
73
  boris/select_subj_behav.py,sha256=ulXbsRY-AIyQRSwXhVlvkNRS_eqWaCvkDKTTyOLqvoE,11742
74
74
  boris/state_events.py,sha256=R5CcT_cldnxqhQkvX_V1AMNxH0Nc6jLzMZYiWkeJwAE,7770
75
75
  boris/subjects_pad.py,sha256=lSRRGfLfD10_YpGua8RGVdKhoXlsXawGhNibPkRhuzM,3541
76
- boris/synthetic_time_budget.py,sha256=sj-5jq7MH-bxPUwKwpi7Xud-jkKTs6PUFlN_01vgimE,10481
77
- boris/time_budget_functions.py,sha256=1-7_G84SDs7rp1EWr5zHInzRVDUkUdIfm_AX0516ceQ,52539
78
- boris/time_budget_widget.py,sha256=H_HQHTuMq7U6PlGjJ-pZO4dDaOrYSsAKK697Gb6EQoI,43201
76
+ boris/synthetic_time_budget.py,sha256=3Eb9onMLmgqCLd50CuxV9L8RV2ESzfaMWvPK_bXUMMk,10489
77
+ boris/time_budget_functions.py,sha256=y5He8crz0xsTxVfz0jATwFFQVnPAIrNHja_0sF6NtRE,52551
78
+ boris/time_budget_widget.py,sha256=z-tyITBtIz-KH1H2OdMB5a8x9QQLK7Wu96-zkC6NVDA,43213
79
79
  boris/transitions.py,sha256=_aZJfJWv3EBrtmQ7qsdTCayQo6uWU7BXqtQQgflEhr4,12250
80
- boris/utilities.py,sha256=zwWpH-lozAUj_8K6Gf2Hl-BSW0aRdWo37HGXTWQ86qk,52782
81
- boris/version.py,sha256=_3fXAvcL2OmwfUOC38JcxwXIb6rqJr_VIPrGA2HMk1k,787
80
+ boris/utilities.py,sha256=H69vrYGkQuQJVE_ywcYC6DNYcS8VTC-GoClmVEUXacA,52748
81
+ boris/version.py,sha256=K9SFa-Pzo_c5MuwLfeEIpmpUpKasa-rVS6AtqazzEKg,787
82
82
  boris/video_equalizer.py,sha256=FartoGghFK-T53zklP70rPKYqTuzL8qdvfGlsOF2wwc,5854
83
83
  boris/video_equalizer_ui.py,sha256=1CG3s79eM4JAbaCx3i1ILZXLceb41_gGXlOLNfpBgnw,10142
84
84
  boris/video_operations.py,sha256=mh3iR__Sm2KnV44L_sW2pOo3AgLwlM7wiTnnqQiAVs4,9381
@@ -95,9 +95,9 @@ boris/portion/dict.py,sha256=SyHxc7PfDw2ufNLFQycwJtzmRfL48rDp4UrM2KN7IWc,11282
95
95
  boris/portion/func.py,sha256=3TkQtFKLfsqntwd27HSGHceFhnXHmT-EbNMqktElC5Q,2174
96
96
  boris/portion/interval.py,sha256=bAdUiJjGeUAPgsBAImwNeviiwfQq5odfhFZccAWzOTA,20299
97
97
  boris/portion/io.py,sha256=ppNeRpiLNrocF1yzGeuEUIhYMf2LfsR-cji3d0nmvUs,6371
98
- boris_behav_obs-9.2.1.dist-info/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
99
- boris_behav_obs-9.2.1.dist-info/METADATA,sha256=6dLb1vMMq5FHHeDTBpefrr6TIQjgg1pz_VoSMKqA_GE,42102
100
- boris_behav_obs-9.2.1.dist-info/WHEEL,sha256=Kol19cahXavY536r5Aj6aAgK_6CmctrOu3bgNJMSNJA,109
101
- boris_behav_obs-9.2.1.dist-info/entry_points.txt,sha256=-Vl37ZFjZYK5wTSDf5LAzd2cVLON1Pfy1xkx490Wxak,47
102
- boris_behav_obs-9.2.1.dist-info/top_level.txt,sha256=fJSgm62S7WesiwTorGbOO4nNN0yzgZ3klgfGi3Px4qI,6
103
- boris_behav_obs-9.2.1.dist-info/RECORD,,
98
+ boris_behav_obs-9.2.3.dist-info/licenses/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
99
+ boris_behav_obs-9.2.3.dist-info/METADATA,sha256=-SAdQZY5G3wnHZ5O4LXQuQFEko_m4uv4aOstv1KLJHA,1342
100
+ boris_behav_obs-9.2.3.dist-info/WHEEL,sha256=MAQBAzGbXNI3bUmkDsiV_duv8i-gcdnLzw7cfUFwqhU,109
101
+ boris_behav_obs-9.2.3.dist-info/entry_points.txt,sha256=k__8XvFi4vaA4QFvQehCZjYkKmZH34HSAJI2iYCWrMs,52
102
+ boris_behav_obs-9.2.3.dist-info/top_level.txt,sha256=fJSgm62S7WesiwTorGbOO4nNN0yzgZ3klgfGi3Px4qI,6
103
+ boris_behav_obs-9.2.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.1.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ boris-behav-obs = boris.core:main