boris-behav-obs 9.1.1__py2.py3-none-any.whl → 9.2.1__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.
@@ -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]:
@@ -6,8 +6,8 @@ number of occurences of behaviors
6
6
 
7
7
  import pandas as pd
8
8
 
9
- __version__ = "0.2.0"
10
- __version_date__ = "2025-01-25"
9
+ __version__ = "0.3.0"
10
+ __version_date__ = "2025-03-17"
11
11
  __plugin_name__ = "Number of occurences of behaviors"
12
12
  __author__ = "Olivier Friard - University of Torino - Italy"
13
13
 
@@ -20,43 +20,3 @@ def run(df: pd.DataFrame):
20
20
  df_results: pd.DataFrame = df.groupby(["Subject", "Behavior"])["Behavior"].count().reset_index(name="number of occurences")
21
21
 
22
22
  return df_results
23
-
24
-
25
- def main(df: pd.DataFrame, observations_list: list = [], parameters: dict = {}) -> pd.DataFrame:
26
- """
27
- filter by selected observations.
28
- filter by selected subjects.
29
- filter by selected behaviors.
30
- filter by time interval.
31
- """
32
-
33
- # filter selected observations
34
- if observations_list:
35
- df = df[df["Observation id"].isin(observations_list)]
36
-
37
- if parameters:
38
- # filter selected subjects
39
- df = df[df["Subject"].isin(parameters["selected subjects"])]
40
-
41
- # filter selected behaviors
42
- df = df[df["Behavior"].isin(parameters["selected behaviors"])]
43
-
44
- # filter selected time interval
45
- if parameters["start time"] is not None and parameters["end time"] is not None:
46
- MIN_TIME = parameters["start time"]
47
- MAX_TIME = parameters["end time"]
48
- df_interval = df[
49
- (
50
- ((df["Start (s)"] >= MIN_TIME) & (df["Start (s)"] <= MAX_TIME))
51
- | ((df["Stop (s)"] >= MIN_TIME) & (df["Stop (s)"] <= MAX_TIME))
52
- )
53
- | ((df["Start (s)"] < MIN_TIME) & (df["Stop (s)"] > MAX_TIME))
54
- ]
55
- df_interval.loc[df["Start (s)"] < MIN_TIME, "Start (s)"] = MIN_TIME
56
- df_interval.loc[df["Stop (s)"] > MAX_TIME, "Stop (s)"] = MAX_TIME
57
-
58
- df_interval.loc[:, "Duration (s)"] = df_interval["Stop (s)"] - df_interval["Start (s)"]
59
-
60
- df = df_interval
61
-
62
- return run(df)
@@ -6,8 +6,8 @@ number of occurences of behaviors by independent_variable
6
6
 
7
7
  import pandas as pd
8
8
 
9
- __version__ = "0.2.0"
10
- __version_date__ = "2025-01-25"
9
+ __version__ = "0.3.0"
10
+ __version_date__ = "2025-03-17"
11
11
  __plugin_name__ = "Number of occurences of behaviors by subject by independent_variable"
12
12
  __author__ = "Olivier Friard - University of Torino - Italy"
13
13
 
@@ -15,9 +15,9 @@ __author__ = "Olivier Friard - University of Torino - Italy"
15
15
  def run(df: pd.DataFrame):
16
16
  """
17
17
  Calculate the number of occurrences of behaviors by subject and by independent_variable.
18
- """
19
18
 
20
- str_results: str = ""
19
+ This plugin returns a Pandas dataframe
20
+ """
21
21
 
22
22
  df_results: df.DataFrame = (
23
23
  df.groupby(
@@ -31,44 +31,4 @@ def run(df: pd.DataFrame):
31
31
  .reset_index(name="number of occurences")
32
32
  )
33
33
 
34
- return df_results, str_results
35
-
36
-
37
- def main(df: pd.DataFrame, observations_list: list = [], parameters: dict = {}) -> pd.DataFrame:
38
- """
39
- filter by selected observations.
40
- filter by selected subjects.
41
- filter by selected behaviors.
42
- filter by time interval.
43
- """
44
-
45
- # filter selected observations
46
- if observations_list:
47
- df = df[df["Observation id"].isin(observations_list)]
48
-
49
- if parameters:
50
- # filter selected subjects
51
- df = df[df["Subject"].isin(parameters["selected subjects"])]
52
-
53
- # filter selected behaviors
54
- df = df[df["Behavior"].isin(parameters["selected behaviors"])]
55
-
56
- # filter selected time interval
57
- if parameters["start time"] is not None and parameters["end time"] is not None:
58
- MIN_TIME = parameters["start time"]
59
- MAX_TIME = parameters["end time"]
60
- df_interval = df[
61
- (
62
- ((df["Start (s)"] >= MIN_TIME) & (df["Start (s)"] <= MAX_TIME))
63
- | ((df["Stop (s)"] >= MIN_TIME) & (df["Stop (s)"] <= MAX_TIME))
64
- )
65
- | ((df["Start (s)"] < MIN_TIME) & (df["Stop (s)"] > MAX_TIME))
66
- ]
67
- df_interval.loc[df["Start (s)"] < MIN_TIME, "Start (s)"] = MIN_TIME
68
- df_interval.loc[df["Stop (s)"] > MAX_TIME, "Stop (s)"] = MAX_TIME
69
-
70
- df_interval.loc[:, "Duration (s)"] = df_interval["Stop (s)"] - df_interval["Start (s)"]
71
-
72
- df = df_interval
73
-
74
- return run(df)
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)
@@ -216,6 +216,8 @@ 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,
219
221
  start_interval=start_interval,
220
222
  end_interval=end_interval,
221
223
  maxTime=max_media_duration_all_obs,
@@ -224,7 +226,7 @@ def behavior_binary_table(self):
224
226
  by_category=False,
225
227
  n_observations=len(selected_observations),
226
228
  )
227
- if parameters == {}:
229
+ if not parameters:
228
230
  return
229
231
  if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
230
232
  QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
boris/cooccurence.py CHANGED
@@ -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]:
boris/core.py CHANGED
@@ -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")
@@ -3634,6 +3631,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3634
3631
  else:
3635
3632
  current_time = self.getLaps()
3636
3633
 
3634
+ # check if observation time interval
3635
+ if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[1]:
3636
+ # check if current time outside of interval
3637
+ if current_time >= self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [None, None])[1]:
3638
+ self.beep("beep")
3639
+ self.liveTimer.stop()
3640
+ self.liveObservationStarted = False
3641
+ # 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])
3643
+ self.pb_live_obs.setText("Live observation finished")
3644
+
3645
+
3637
3646
  self.lb_current_media_time.setText(util.convertTime(self.timeFormat, current_time))
3638
3647
 
3639
3648
  # extract State events
@@ -3663,13 +3672,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3663
3672
  self.liveTimer.stop()
3664
3673
  self.pb_live_obs.setText("Live observation stopped (scan sampling)")
3665
3674
 
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
3675
 
3674
3676
  def start_live_observation(self):
3675
3677
  """
@@ -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,
@@ -5762,6 +5766,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5762
5766
  self.extract_frame(self.dw_player[0])
5763
5767
 
5764
5768
  def obs_param(self):
5769
+ """
5770
+ allow user to select observations and then subjects and behaviors
5771
+ """
5765
5772
  _, selected_observations = select_observations.select_observations2(self, mode=cfg.MULTIPLE, windows_title="")
5766
5773
 
5767
5774
  if not selected_observations:
@@ -5786,8 +5793,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5786
5793
 
5787
5794
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
5788
5795
 
5796
+ print(f"{start_coding=}")
5797
+ print(f"{end_coding=}")
5798
+
5789
5799
  start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
5790
5800
 
5801
+ print(f"{start_interval=}")
5802
+ print(f"{end_interval=}")
5803
+
5791
5804
  parameters: dict = select_subj_behav.choose_obs_subj_behav_category(
5792
5805
  self,
5793
5806
  selected_observations,
@@ -5803,6 +5816,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5803
5816
  if parameters == {}:
5804
5817
  return [], {}
5805
5818
 
5819
+ print(f"{parameters=}")
5820
+
5806
5821
  if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
5807
5822
  QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
5808
5823
  return [], {}
@@ -5810,94 +5825,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5810
5825
  logging.debug(f"{parameters=}")
5811
5826
  return selected_observations, parameters
5812
5827
 
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
5828
 
5902
5829
  def main():
5903
5830
  # QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
boris/export_events.py CHANGED
@@ -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,
boris/gui_utilities.py CHANGED
@@ -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:
@@ -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
 
boris/param_panel.py CHANGED
@@ -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)
boris/plugins.py CHANGED
@@ -19,10 +19,19 @@ Copyright 2012-2025 Olivier Friard
19
19
  MA 02110-1301, USA.
20
20
  """
21
21
 
22
+ import importlib
22
23
  import logging
24
+ import numpy as np
25
+ import pandas as pd
26
+ from pathlib import Path
27
+
23
28
  from PySide6.QtGui import QAction
29
+ from PySide6.QtWidgets import QMessageBox
30
+
24
31
  from . import config as cfg
25
- from pathlib import Path
32
+ from . import project_functions
33
+ from . import dialog
34
+ from . import view_df
26
35
 
27
36
 
28
37
  def add_plugins_to_menu(self):
@@ -32,7 +41,7 @@ def add_plugins_to_menu(self):
32
41
  for plugin_name in self.config_param.get(cfg.ANALYSIS_PLUGINS, {}):
33
42
  logging.debug(f"adding plugin '{plugin_name}' to menu")
34
43
  # Create an action for each submenu option
35
- action = QAction(self, triggered=self.run_plugin)
44
+ action = QAction(self, triggered=lambda checked=False, name=plugin_name: run_plugin(self, name))
36
45
  action.setText(plugin_name)
37
46
 
38
47
  self.menu_plugins.addAction(action)
@@ -77,3 +86,162 @@ def load_plugins(self):
77
86
  self.config_param[cfg.ANALYSIS_PLUGINS][plugin_name] = str(file_)
78
87
 
79
88
  logging.debug(f"{self.config_param.get(cfg.ANALYSIS_PLUGINS, {})=}")
89
+
90
+
91
+ def plugin_df_filter(df: pd.DataFrame, observations_list: list = [], parameters: dict = {}) -> pd.DataFrame:
92
+ """
93
+ filter the dataframe following parameters
94
+
95
+ filter by selected observations.
96
+ filter by selected subjects.
97
+ filter by selected behaviors.
98
+ filter by time interval.
99
+ """
100
+
101
+ # filter selected observations
102
+ df = df[df["Observation id"].isin(observations_list)]
103
+
104
+ if parameters:
105
+ # filter selected subjects
106
+ df = df[df["Subject"].isin(parameters["selected subjects"])]
107
+
108
+ # filter selected behaviors
109
+ df = df[df["Behavior"].isin(parameters["selected behaviors"])]
110
+
111
+ if parameters["time"] == "interval of observation":
112
+ # filter each observation with observation interval start/stop
113
+
114
+ # keep events between observation interval start time and observation interval stop/end
115
+ df_interval = df[
116
+ (
117
+ ((df["Start (s)"] >= df["Observation interval start"]) & (df["Start (s)"] <= df["Observation interval stop"]))
118
+ | ((df["Stop (s)"] >= df["Observation interval start"]) & (df["Stop (s)"] <= df["Observation interval stop"]))
119
+ )
120
+ | ((df["Start (s)"] < df["Observation interval start"]) & (df["Stop (s)"] > df["Observation interval stop"]))
121
+ ]
122
+
123
+ df_interval.loc[df["Start (s)"] < df["Observation interval start"], "Start (s)"] = df["Observation interval start"]
124
+ df_interval.loc[df["Stop (s)"] > df["Observation interval stop"], "Stop (s)"] = df["Observation interval stop"]
125
+
126
+ df_interval.loc[:, "Duration (s)"] = (df_interval["Stop (s)"] - df_interval["Start (s)"]).replace(0, np.nan)
127
+
128
+ df = df_interval
129
+
130
+ else:
131
+ # filter selected time interval
132
+ if parameters["start time"] is not None and parameters["end time"] is not None:
133
+ MIN_TIME = parameters["start time"]
134
+ MAX_TIME = parameters["end time"]
135
+
136
+ # keep events between start time and end_time
137
+ df_interval = df[
138
+ (
139
+ ((df["Start (s)"] >= MIN_TIME) & (df["Start (s)"] <= MAX_TIME))
140
+ | ((df["Stop (s)"] >= MIN_TIME) & (df["Stop (s)"] <= MAX_TIME))
141
+ )
142
+ | ((df["Start (s)"] < MIN_TIME) & (df["Stop (s)"] > MAX_TIME))
143
+ ]
144
+
145
+ df_interval.loc[df["Start (s)"] < MIN_TIME, "Start (s)"] = MIN_TIME
146
+ df_interval.loc[df["Stop (s)"] > MAX_TIME, "Stop (s)"] = MAX_TIME
147
+
148
+ df_interval.loc[:, "Duration (s)"] = (df_interval["Stop (s)"] - df_interval["Start (s)"]).replace(0, np.nan)
149
+
150
+ df = df_interval
151
+
152
+ print("filtered")
153
+ print("=" * 50)
154
+
155
+ print(f"{df=}")
156
+
157
+ return df
158
+
159
+
160
+ def run_plugin(self, plugin_name):
161
+ """
162
+ run plugin
163
+ """
164
+
165
+ if not self.project:
166
+ QMessageBox.warning(
167
+ self,
168
+ cfg.programName,
169
+ "No observations found. Open a project first",
170
+ QMessageBox.Ok | QMessageBox.Default,
171
+ QMessageBox.NoButton,
172
+ )
173
+ return
174
+
175
+ logging.debug(f"{self.config_param.get(cfg.ANALYSIS_PLUGINS, {})=}")
176
+
177
+ if plugin_name not in self.config_param.get(cfg.ANALYSIS_PLUGINS, {}):
178
+ QMessageBox.critical(self, cfg.programName, f"Plugin '{plugin_name}' not found")
179
+ return
180
+
181
+ plugin_path = self.config_param.get(cfg.ANALYSIS_PLUGINS, {})[plugin_name]
182
+
183
+ logging.debug(f"{plugin_path=}")
184
+
185
+ if not Path(plugin_path).is_file():
186
+ QMessageBox.critical(self, cfg.programName, f"The plugin {plugin_path} was not found.")
187
+ return
188
+
189
+ logging.debug(f"run plugin from {plugin_path}")
190
+
191
+ module_name = Path(plugin_path).stem
192
+
193
+ spec = importlib.util.spec_from_file_location(module_name, plugin_path)
194
+ plugin_module = importlib.util.module_from_spec(spec)
195
+
196
+ logging.debug(f"{plugin_module=}")
197
+
198
+ spec.loader.exec_module(plugin_module)
199
+
200
+ logging.info(
201
+ f"{plugin_module.__plugin_name__} loaded v.{getattr(plugin_module, '__version__')} v. {getattr(plugin_module, '__version_date__')}"
202
+ )
203
+
204
+ selected_observations, parameters = self.obs_param()
205
+ if not selected_observations:
206
+ return
207
+
208
+ df = project_functions.project2dataframe(self.pj, selected_observations)
209
+
210
+ """
211
+ logging.debug("dataframe info")
212
+ logging.debug(f"{df.info()}")
213
+ logging.debug(f"{df.head()}")
214
+ """
215
+
216
+ # filter the dataframe with parameters
217
+ filtered_df = plugin_df_filter(df, observations_list=selected_observations, parameters=parameters)
218
+
219
+ plugin_results = plugin_module.run(filtered_df)
220
+ # test if plugin_tests is a tuple: if not transform to tuple
221
+ if not isinstance(plugin_results, tuple):
222
+ plugin_results = tuple([plugin_results])
223
+
224
+ self.plugin_visu: list = []
225
+ for result in plugin_results:
226
+ if isinstance(result, str):
227
+ self.plugin_visu.append(dialog.Results_dialog())
228
+ self.plugin_visu[-1].setWindowTitle(plugin_name)
229
+ self.plugin_visu[-1].ptText.clear()
230
+ self.plugin_visu[-1].ptText.appendPlainText(result)
231
+ self.plugin_visu[-1].show()
232
+ elif isinstance(result, pd.DataFrame):
233
+ self.plugin_visu.append(view_df.View_df(plugin_name, f"{plugin_module.__version__} ({plugin_module.__version_date__})", result))
234
+ self.plugin_visu[-1].show()
235
+ else:
236
+ # result is not str nor dataframe
237
+ QMessageBox.critical(
238
+ None,
239
+ cfg.programName,
240
+ (
241
+ f"Plugin returns an unknown object type: {type(result)}\n\n"
242
+ "Plugins must return str and/or Pandas Dataframes.\n"
243
+ "Check the plugin code."
244
+ ),
245
+ QMessageBox.Ok | QMessageBox.Default,
246
+ QMessageBox.NoButton,
247
+ )
@@ -39,7 +39,6 @@ from . import db_functions
39
39
  from . import dialog
40
40
  from . import observation_operations
41
41
  from . import portion as I
42
- from . import project_functions
43
42
  from . import utilities as util
44
43
  from . import version
45
44
 
@@ -1806,9 +1805,11 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1806
1805
 
1807
1806
  data = {
1808
1807
  "Observation id": [],
1809
- # "Observation date": [],
1810
- # "Description": [],
1811
- # "Observation type": [],
1808
+ "Observation date": [],
1809
+ "Description": [],
1810
+ "Observation type": [],
1811
+ "Observation interval start": [],
1812
+ "Observation interval stop": [],
1812
1813
  # "Source": [],
1813
1814
  # "Time offset (s)": [],
1814
1815
  # "Coding duration": [],
@@ -1847,9 +1848,11 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1847
1848
 
1848
1849
  type_ = {
1849
1850
  "Observation id": "string",
1850
- # "Observation date": "string",
1851
- # "Description": "string",
1852
- # "Observation type": "string",
1851
+ "Observation date": "string",
1852
+ "Description": "string",
1853
+ "Observation type": "string",
1854
+ "Observation interval start": "float64",
1855
+ "Observation interval stop": "float64",
1853
1856
  # "Source": "string",
1854
1857
  # "Time offset (s)": "string",
1855
1858
  # "Coding duration": "float64",
@@ -1885,14 +1888,6 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1885
1888
  "Comment stop": "string",
1886
1889
  }
1887
1890
 
1888
- """
1889
- state_behaviors = [
1890
- pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
1891
- for x in pj[cfg.ETHOGRAM]
1892
- if pj[cfg.ETHOGRAM][x]["type"] in (cfg.STATE_EVENT, cfg.STATE_EVENT_WITH_CODING_MAP)
1893
- ]
1894
- """
1895
-
1896
1891
  state_behaviors = util.state_behavior_codes(pj[cfg.ETHOGRAM])
1897
1892
 
1898
1893
  for obs_id in pj[cfg.OBSERVATIONS]:
@@ -1904,9 +1899,21 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1904
1899
  if idx_event in stop_event_idx:
1905
1900
  continue
1906
1901
  data["Observation id"].append(obs_id)
1907
- # data["Observation date"].append(pj["observations"][obs_id]["date"])
1908
- # data["Description"].append(pj["observations"][obs_id]["description"])
1909
- # data["Observation type"].append(pj["observations"][obs_id]["type"])
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
+ )
1916
+
1910
1917
  # data["Source"].append("")
1911
1918
  # data["Time offset (s)"].append(pj["observations"][obs_id]["time offset"])
1912
1919
  # data["Coding duration"].append("")
@@ -1932,7 +1939,7 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1932
1939
  data[modifier_set].append(np.nan)
1933
1940
 
1934
1941
  data["Behavior type"].append(cfg.STATE_EVENT if event[2] in state_behaviors else cfg.POINT_EVENT)
1935
- data["Start (s)"].append(event[0])
1942
+ data["Start (s)"].append(float(event[0]))
1936
1943
  if event[2] in state_behaviors:
1937
1944
  # search stop
1938
1945
  # print(f"==> {idx_event=} {event[1:4]=}")
@@ -1941,8 +1948,8 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1941
1948
  if event2[1:4] == event[1:4]:
1942
1949
  # print("found")
1943
1950
  stop_event_idx.add(idx_event2)
1944
- data["Stop (s)"].append(event2[0])
1945
- data["Duration (s)"].append(event2[0] - event[0])
1951
+ data["Stop (s)"].append(float(event2[0]))
1952
+ data["Duration (s)"].append(float(event2[0] - event[0]))
1946
1953
  data["Comment start"].append(event[4])
1947
1954
  data["Comment stop"].append(event2[4])
1948
1955
  break
@@ -1950,9 +1957,17 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1950
1957
  raise ("not paired")
1951
1958
 
1952
1959
  else: # point
1953
- data["Stop (s)"].append(event[0])
1960
+ data["Stop (s)"].append(float(event[0]))
1954
1961
  data["Duration (s)"].append(np.nan)
1955
1962
  data["Comment start"].append(event[4])
1956
1963
  data["Comment stop"].append(event[4])
1957
1964
 
1965
+ # Set the display option to show all rows and columns
1966
+ pd.set_option("display.max_rows", None)
1967
+ pd.set_option("display.max_columns", None)
1968
+
1969
+ pd.DataFrame(data).info()
1970
+
1971
+ print(pd.DataFrame(data))
1972
+
1958
1973
  return pd.DataFrame(data)
@@ -123,6 +123,10 @@ def choose_obs_subj_behav_category(
123
123
  paramPanelWindow.start_time.set_time(start_coding)
124
124
  paramPanelWindow.end_time.set_time(end_coding)
125
125
 
126
+ # check observation time interval
127
+ if start_interval is None or start_interval.is_nan() or end_interval is None or end_interval.is_nan():
128
+ paramPanelWindow.rb_obs_interval.setEnabled(False)
129
+
126
130
  if selected_observations:
127
131
  observedSubjects = project_functions.extract_observed_subjects(self.pj, selected_observations)
128
132
  else:
@@ -71,8 +71,10 @@ def synthetic_time_budget(self) -> None:
71
71
  selected_observations,
72
72
  start_coding=start_coding,
73
73
  end_coding=end_coding,
74
- start_interval=start_interval,
75
- end_interval=end_interval,
74
+ # start_interval=start_interval,
75
+ # end_interval=end_interval,
76
+ start_interval=None,
77
+ end_interval=None,
76
78
  maxTime=max_media_duration_all_obs,
77
79
  show_exclude_non_coded_behaviors=False,
78
80
  by_category=False,
@@ -201,8 +203,10 @@ def synthetic_binned_time_budget(self) -> None:
201
203
  selected_observations,
202
204
  start_coding=start_coding,
203
205
  end_coding=end_coding,
204
- start_interval=start_interval,
205
- end_interval=end_interval,
206
+ # start_interval=start_interval,
207
+ # end_interval=end_interval,
208
+ start_interval=None,
209
+ end_interval=None,
206
210
  maxTime=max_media_duration_all_obs,
207
211
  show_exclude_non_coded_behaviors=False,
208
212
  by_category=False,
@@ -221,7 +225,7 @@ def synthetic_binned_time_budget(self) -> None:
221
225
  # ask for excluding behaviors durations from total time
222
226
  cancel_pressed, synth_tb_param[cfg.EXCLUDED_BEHAVIORS] = self.filter_behaviors(
223
227
  title="Select behaviors to exclude",
224
- text=("The duration of the selected behaviors will " "be subtracted from the total time"),
228
+ text=("The duration of the selected behaviors will be subtracted from the total time"),
225
229
  table="",
226
230
  behavior_type=[cfg.STATE_EVENT],
227
231
  )
@@ -436,8 +436,10 @@ def time_budget(self, mode: str, mode2: str = "list"):
436
436
  selected_observations,
437
437
  start_coding=start_coding,
438
438
  end_coding=end_coding,
439
- start_interval=start_interval,
440
- end_interval=end_interval,
439
+ # start_interval=start_interval,
440
+ # end_interval=end_interval,
441
+ start_interval=None,
442
+ end_interval=None,
441
443
  maxTime=max_media_duration_all_obs,
442
444
  by_category=(mode == "by_category"),
443
445
  n_observations=len(selected_observations),
@@ -744,7 +746,7 @@ def time_budget(self, mode: str, mode2: str = "list"):
744
746
 
745
747
  self.tb.twTB.resizeColumnsToContents()
746
748
 
747
- gui_utilities.restore_geometry(self.tb, "time budget", (0, 0))
749
+ gui_utilities.restore_geometry(self.tb, "time budget", (800, 600))
748
750
 
749
751
  self.tb.show()
750
752
 
boris/version.py CHANGED
@@ -20,5 +20,5 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
- __version__ = "9.1.1"
24
- __version_date__ = "2025-03-12"
23
+ __version__ = "9.2.1"
24
+ __version_date__ = "2025-03-17"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: boris-behav-obs
3
- Version: 9.1.1
3
+ Version: 9.2.1
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
@@ -3,9 +3,9 @@ boris/__main__.py,sha256=ANjTbXgXDoz2nB1tCtOIllfIVotCa602iebACX7rXaE,764
3
3
  boris/about.py,sha256=KEUz6nryrg8FceVyFsf8sMz-xWd2cGwIUfuVydHxqC4,5366
4
4
  boris/add_modifier.py,sha256=DWqxkKDBm21QH_kPvhpnltwLtFvPxne0VmZ1SY26hj8,26340
5
5
  boris/add_modifier_ui.py,sha256=Y7TLO5uS6zW7zpjXmjA4V_VIp_bFDNtjOTbJ9Q6m-mQ,11601
6
- boris/advanced_event_filtering.py,sha256=DyPHEj2QsTpkkYjBD0iMXqFYCkrMjMcmp1MOuSITtGk,15411
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=SsaPrXyGXwWYPJ_4R0BlfqeV-q1n0vGNDo0HB3ZmQi8,12426
8
+ boris/behavior_binary_table.py,sha256=sr2Wb7NFd-NjfXCL5fqsc_Uf7rvS0p9RxZwVbaFBeig,12502
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
@@ -15,8 +15,8 @@ boris/config_file.py,sha256=1-2ZmTvKET57rwrLR1dXblt0AxMpGB1LAiHxu-Sy8es,13543
15
15
  boris/connections.py,sha256=y_KkIj3WQQEepJASATHBpZaqwT-ERyFNovy2Q8e7FAA,19321
16
16
  boris/converters.py,sha256=c1Jps-URoglY5ILQHz-pCCf6-4DFUHZLtqr_ofsrFg0,11722
17
17
  boris/converters_ui.py,sha256=uu7LOBV_fKv2DBdOqsqPwjGsjgONr5ODBoscAA-EP48,9900
18
- boris/cooccurence.py,sha256=NV3lPhzKptyYh_pSjx1a_FqwaKstqIwj46GpNcyJ4aY,10236
19
- boris/core.py,sha256=ncIjy9W5jDKkdmytTNGwq2qscZVlZ0tMd9bOOro_9dg,237708
18
+ boris/cooccurence.py,sha256=tVERC-V8MWjWHlGEfDuu08iS94qjt4do-38jwI62QaY,10367
19
+ boris/core.py,sha256=3NbJcsngnDTaLuXPWblgidKqjzZhJTxzL8N69pwgtkI,234818
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,11 +29,11 @@ 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=YoqE0vHXeDMAk28e6rewI3TODdfMr8YbJsNnJqiXv0Y,39720
32
+ boris/export_events.py,sha256=8h6AenIuXJj-JWlkSS0UR38mhjUjmFv3_5QOyBSpYec,39744
33
33
  boris/export_observation.py,sha256=uPJH_5MXqGm4JSeaSlTikh1i6OKSyLIaZFzS9wp-9qk,50729
34
34
  boris/external_processes.py,sha256=vpmhA4Lj2GblBIrDD0YjesB8HPOgx4K9gSWVhTop4Cg,11927
35
35
  boris/geometric_measurement.py,sha256=4pI-AYpBSFlJBqS-f8dnkgLtj_Z2E5kwwAdh6WwZ4kk,35049
36
- boris/gui_utilities.py,sha256=402qypJ44JkcZ1PDeV79j8Z9faQ473TzEkjjO-scBz0,4170
36
+ boris/gui_utilities.py,sha256=5vjIWbUOHFbqKSti-kT0GoteBBEQ5fUYdNGdMxcg_0A,4607
37
37
  boris/image_overlay.py,sha256=zZAL8MTt2i2s58CuX81Nym3rJ5pKiTeP4AO8WbIUonM,2527
38
38
  boris/import_observations.py,sha256=hwEPIag741AXTFIuxDdZLDvLrsmvaqTkjyTjQu5M_RA,8798
39
39
  boris/irr.py,sha256=o5QN3B2b-02AUkrklMJCitFGsfiUDtmI0MxUbPv2cBg,22472
@@ -47,11 +47,11 @@ 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=w295Vlj6qparn7xcdh1tkwgXXx2QTnq9J6KUJGh1zvg,105533
50
+ boris/observation_operations.py,sha256=8rVC_iWb0Y8zkd64_XvDxD2e7_jsKdGfDSdyi4eDRAs,105643
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
- boris/param_panel.py,sha256=Sna2SlJt1CSUEE_bNX2X6ibFjs2Um0-ZjtQf5-JlJTc,8693
54
+ boris/param_panel.py,sha256=G0XzNmJIX89-n2OQTDccuY_wWMhr3p7GB4ZorbU6EWc,8786
55
55
  boris/param_panel_ui.py,sha256=4emQDFmuL4_R7bKxosLjdUb-VSPWkDm7suy38F5EKcA,13260
56
56
  boris/player_dock_widget.py,sha256=VBuuGoAb2D027oFnMBlIQPiSQcEdedhoQltNFSJq8mw,5982
57
57
  boris/plot_data_module.py,sha256=6QbLKfyGp4TYRyHnB9G45y5XrpeXLytcorltEAWfYak,16562
@@ -59,26 +59,26 @@ boris/plot_events.py,sha256=hA_Yt-ZaxbAOKjN7gYHiaEx5TPR-7HIWGJXXg4cq6lw,24048
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=2RyrS3fdJ2lA8FhG_qymDCfaFyzK3TaRy2ll3yHjlz4,2817
62
+ boris/plugins.py,sha256=RtC_Ew6VxAwWQqRuKQj9FgpsSBGoZmZuCZ5gPj0EiwI,8963
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=Bu-HD58uYL__v2ih5OhQnVF22_lPrnvX3LCw4wheC1I,79251
66
+ boris/project_functions.py,sha256=p_CC2UoFEbb0PpKt9c5MtJulSszz-fV1o1sgFRFVQEk,79920
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
70
70
  boris/qrc_boris5.py,sha256=prnOw7VGXWXRuVCYp_yIrmWhrlG1F9rx-3BQvkPenjY,161608
71
71
  boris/select_modifiers.py,sha256=42uG9F75pfPoPJ-blp-vFgmpBpVJtL42FlIxpNpq9z4,13319
72
72
  boris/select_observations.py,sha256=HM0suMes1YxVwQ-Xakw7ROaxbN0EyeSiZFZNrTQIHAA,7886
73
- boris/select_subj_behav.py,sha256=5ln2e83nVEcMW1TTeOE5K1gqd4lSvh7LeiSfhloZPrU,11537
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=NwJt6TO6XZwxF2s7ETvqiDEqrz9ad30g1sQ0bVBsN9o,10364
76
+ boris/synthetic_time_budget.py,sha256=sj-5jq7MH-bxPUwKwpi7Xud-jkKTs6PUFlN_01vgimE,10481
77
77
  boris/time_budget_functions.py,sha256=1-7_G84SDs7rp1EWr5zHInzRVDUkUdIfm_AX0516ceQ,52539
78
- boris/time_budget_widget.py,sha256=_yYzLa6aJRWHRataek_ciXMEKGW75HA9y0auhAJwzbA,43137
78
+ boris/time_budget_widget.py,sha256=H_HQHTuMq7U6PlGjJ-pZO4dDaOrYSsAKK697Gb6EQoI,43201
79
79
  boris/transitions.py,sha256=_aZJfJWv3EBrtmQ7qsdTCayQo6uWU7BXqtQQgflEhr4,12250
80
80
  boris/utilities.py,sha256=zwWpH-lozAUj_8K6Gf2Hl-BSW0aRdWo37HGXTWQ86qk,52782
81
- boris/version.py,sha256=MwBl656ww2Munfb847fzP6VQ6OX5HrRbTL8ngrLBaS8,787
81
+ boris/version.py,sha256=_3fXAvcL2OmwfUOC38JcxwXIb6rqJr_VIPrGA2HMk1k,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
@@ -86,18 +86,18 @@ boris/view_df.py,sha256=AKScLASX2Uatw7rqPbsnio83eVT4GZYCFhL091eMvlY,3370
86
86
  boris/view_df_ui.py,sha256=CaMeRH_vQ00CTDDFQn73ZZaS-r8BSTWpL-dMCFqzJ_Q,2775
87
87
  boris/write_event.py,sha256=jKZWzvDvQ-wwFvTLjA-zUjtXQCWwVC49Y5g-1UctpYw,23821
88
88
  boris/analysis_plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
- boris/analysis_plugins/number_of_occurences.py,sha256=U0NuU8leQixwDWlH26FsQmZl-wrvWKxNH_CWAWb8us4,1972
90
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py,sha256=fkyOuc1AiOSnrz7L6Ocd742XmDHhhQEtgi2KSiTOG4c,2252
91
- boris/analysis_plugins/time_budget.py,sha256=_eNv_9oxwa-L6pZVyATc8YNnsmUj_3Px2F0F3Y603p0,3547
89
+ boris/analysis_plugins/number_of_occurences.py,sha256=IDyDrdezqvSKT3BlD8QWpSYk8X9nnBBLI80OUnFJ3bY,509
90
+ boris/analysis_plugins/number_of_occurences_by_independent_variable.py,sha256=t39bmmmZIDCSbcDvVeiKAhKNNP2SdpHp417JczHEnP4,793
91
+ boris/analysis_plugins/time_budget.py,sha256=C1wNYwd5Jugr8h5z2aXRUBY8dF8pD4n953dPwNHY5VY,2244
92
92
  boris/portion/__init__.py,sha256=ZBUG4I7YWhRkeWdP-JEpxhxldJlUYQkeaJseTjdhtJE,602
93
93
  boris/portion/const.py,sha256=hEp26BKcEg1Js4DfZsBHmDtJJts83Tl1HWQ0CNJNwEc,1588
94
94
  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.1.1.dist-info/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
99
- boris_behav_obs-9.1.1.dist-info/METADATA,sha256=zuFFmX5VePSe15dz0kKJdQR-TulX0i47xsTTwUkEnLw,42102
100
- boris_behav_obs-9.1.1.dist-info/WHEEL,sha256=SrDKpSbFN1G94qcmBqS9nyHcDMp9cUS9OC06hC0G3G0,109
101
- boris_behav_obs-9.1.1.dist-info/entry_points.txt,sha256=-Vl37ZFjZYK5wTSDf5LAzd2cVLON1Pfy1xkx490Wxak,47
102
- boris_behav_obs-9.1.1.dist-info/top_level.txt,sha256=fJSgm62S7WesiwTorGbOO4nNN0yzgZ3klgfGi3Px4qI,6
103
- boris_behav_obs-9.1.1.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.0.0)
2
+ Generator: setuptools (76.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any