boris-behav-obs 8.16.5__py3-none-any.whl → 9.7.12__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.
Files changed (126) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +28 -40
  4. boris/add_modifier.py +88 -80
  5. boris/add_modifier_ui.py +266 -144
  6. boris/advanced_event_filtering.py +23 -29
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_export_to_feral.py +225 -0
  9. boris/analysis_plugins/_latency.py +59 -0
  10. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  11. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  13. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  14. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  15. boris/analysis_plugins/number_of_occurences.py +22 -0
  16. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  17. boris/analysis_plugins/time_budget.py +61 -0
  18. boris/behav_coding_map_creator.py +235 -236
  19. boris/behavior_binary_table.py +33 -50
  20. boris/behaviors_coding_map.py +17 -18
  21. boris/boris_cli.py +6 -25
  22. boris/cmd_arguments.py +12 -1
  23. boris/coding_pad.py +19 -36
  24. boris/config.py +109 -50
  25. boris/config_file.py +58 -67
  26. boris/connections.py +105 -58
  27. boris/converters.py +13 -37
  28. boris/converters_ui.py +187 -110
  29. boris/cooccurence.py +250 -0
  30. boris/core.py +2174 -1303
  31. boris/core_qrc.py +15892 -10829
  32. boris/core_ui.py +941 -806
  33. boris/db_functions.py +17 -42
  34. boris/dev.py +27 -7
  35. boris/dialog.py +461 -242
  36. boris/duration_widget.py +9 -14
  37. boris/edit_event.py +61 -31
  38. boris/edit_event_ui.py +208 -97
  39. boris/event_operations.py +405 -281
  40. boris/events_cursor.py +25 -17
  41. boris/events_snapshots.py +36 -82
  42. boris/exclusion_matrix.py +4 -9
  43. boris/export_events.py +180 -203
  44. boris/export_observation.py +60 -73
  45. boris/external_processes.py +123 -98
  46. boris/geometric_measurement.py +427 -218
  47. boris/gui_utilities.py +91 -14
  48. boris/image_overlay.py +4 -4
  49. boris/import_observations.py +190 -98
  50. boris/ipc_mpv.py +325 -0
  51. boris/irr.py +20 -57
  52. boris/latency.py +31 -24
  53. boris/measurement_widget.py +14 -18
  54. boris/media_file.py +17 -19
  55. boris/menu_options.py +16 -6
  56. boris/modifier_coding_map_creator.py +1013 -0
  57. boris/modifiers_coding_map.py +7 -9
  58. boris/mpv2.py +128 -35
  59. boris/observation.py +501 -211
  60. boris/observation_operations.py +1037 -393
  61. boris/observation_ui.py +573 -363
  62. boris/observations_list.py +51 -58
  63. boris/otx_parser.py +74 -68
  64. boris/param_panel.py +45 -59
  65. boris/param_panel_ui.py +254 -138
  66. boris/player_dock_widget.py +91 -56
  67. boris/plot_data_module.py +20 -53
  68. boris/plot_events.py +56 -153
  69. boris/plot_events_rt.py +16 -30
  70. boris/plot_spectrogram_rt.py +83 -56
  71. boris/plot_waveform_rt.py +27 -49
  72. boris/plugins.py +468 -0
  73. boris/portion/__init__.py +18 -8
  74. boris/portion/const.py +35 -18
  75. boris/portion/dict.py +5 -5
  76. boris/portion/func.py +2 -2
  77. boris/portion/interval.py +21 -41
  78. boris/portion/io.py +41 -32
  79. boris/preferences.py +307 -123
  80. boris/preferences_ui.py +686 -227
  81. boris/project.py +294 -271
  82. boris/project_functions.py +626 -537
  83. boris/project_import_export.py +204 -213
  84. boris/project_ui.py +673 -441
  85. boris/qrc_boris.py +6 -3
  86. boris/qrc_boris5.py +6 -3
  87. boris/select_modifiers.py +62 -90
  88. boris/select_observations.py +19 -197
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +51 -33
  91. boris/subjects_pad.py +7 -9
  92. boris/synthetic_time_budget.py +42 -26
  93. boris/time_budget_functions.py +169 -169
  94. boris/time_budget_widget.py +77 -89
  95. boris/transitions.py +41 -41
  96. boris/utilities.py +594 -226
  97. boris/version.py +3 -3
  98. boris/video_equalizer.py +16 -14
  99. boris/video_equalizer_ui.py +199 -130
  100. boris/video_operations.py +86 -28
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +240 -136
  104. boris_behav_obs-9.7.12.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.12.dist-info/RECORD +110 -0
  106. {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.12.dist-info}/WHEEL +1 -1
  107. boris_behav_obs-9.7.12.dist-info/entry_points.txt +2 -0
  108. boris/README.TXT +0 -22
  109. boris/add_modifier.ui +0 -323
  110. boris/converters.ui +0 -289
  111. boris/core.qrc +0 -37
  112. boris/core.ui +0 -1571
  113. boris/edit_event.ui +0 -233
  114. boris/icons/logo_eye.ico +0 -0
  115. boris/map_creator.py +0 -982
  116. boris/observation.ui +0 -814
  117. boris/param_panel.ui +0 -379
  118. boris/preferences.ui +0 -537
  119. boris/project.ui +0 -1074
  120. boris/vlc_local.py +0 -90
  121. boris_behav_obs-8.16.5.dist-info/LICENSE.TXT +0 -674
  122. boris_behav_obs-8.16.5.dist-info/METADATA +0 -134
  123. boris_behav_obs-8.16.5.dist-info/RECORD +0 -107
  124. boris_behav_obs-8.16.5.dist-info/entry_points.txt +0 -2
  125. {boris → boris_behav_obs-9.7.12.dist-info/licenses}/LICENSE.TXT +0 -0
  126. {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.12.dist-info}/top_level.txt +0 -0
boris/ipc_mpv.py ADDED
@@ -0,0 +1,325 @@
1
+ """
2
+ BORIS
3
+ Behavioral Observation Research Interactive Software
4
+ Copyright 2012-2025 Olivier Friard
5
+
6
+ This file is part of BORIS.
7
+
8
+ BORIS is free software; you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation; either version 3 of the License, or
11
+ any later version.
12
+
13
+ BORIS is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with this program; if not see <http://www.gnu.org/licenses/>.
20
+
21
+ """
22
+
23
+ import socket
24
+ import json
25
+ import subprocess
26
+
27
+ import logging
28
+ import config as cfg
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class IPC_MPV:
34
+ """
35
+ class for managing mpv through Inter Process Communication (IPC)
36
+ """
37
+
38
+ media_durations: list = []
39
+ cumul_media_durations: list = []
40
+ fps: list = []
41
+ _pause: bool = False
42
+
43
+ def __init__(self, socket_path: str = cfg.MPV_SOCKET, parent=None):
44
+ # print(f"{parent=}")
45
+ self.socket_path = socket_path
46
+ self.process = None
47
+ # self.sock = None
48
+ self.init_mpv()
49
+ # self.init_socket()
50
+
51
+ def init_mpv(self):
52
+ """
53
+ Start mpv process and embed it in the PySide6 application.
54
+ """
55
+ logger.info("Start mpv ipc process")
56
+ # print(f"{self.winId()=}")
57
+ self.process = subprocess.Popen(
58
+ [
59
+ "mpv",
60
+ "--ontop",
61
+ "--no-border",
62
+ "--osc=no", # no on screen commands
63
+ "--input-ipc-server=" + self.socket_path,
64
+ # "--wid=" + str(int(self.winId())), # Embed in the widget
65
+ "--idle=yes", # Keeps mpv running with no video
66
+ "--keep-open=always",
67
+ "--input-default-bindings=no",
68
+ "--input-vo-keyboard=no",
69
+ ],
70
+ stdout=subprocess.PIPE,
71
+ stderr=subprocess.PIPE,
72
+ )
73
+
74
+ def send_command(self, command):
75
+ """
76
+ Send a JSON command to the mpv IPC server.
77
+ """
78
+ # print(f"send command: {command}")
79
+ try:
80
+ # Create a Unix socket
81
+ with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
82
+ # Connect to the MPV IPC server
83
+ client.connect(self.socket_path)
84
+ # Send the JSON command
85
+ # print(f"{json.dumps(command).encode('utf-8')=}")
86
+ client.sendall(json.dumps(command).encode("utf-8") + b"\n")
87
+ # Receive the response
88
+ response = client.recv(2000)
89
+
90
+ # print(f"{response=}")
91
+ # Parse the response as JSON
92
+ response_data = json.loads(response.decode("utf-8"))
93
+ if response_data["error"] != "success":
94
+ logging.warning(f"send command: {command} response data: {response_data}")
95
+ # Return the 'data' field which contains the playback position
96
+ return response_data.get("data")
97
+ except FileNotFoundError:
98
+ logger.critical("Error: Socket file not found.")
99
+ except Exception as e:
100
+ logger.critical(f"An error occurred: {e}")
101
+ return None
102
+
103
+ @property
104
+ def time_pos(self):
105
+ time_pos = self.send_command({"command": ["get_property", "time-pos"]})
106
+ return time_pos
107
+
108
+ @property
109
+ def duration(self):
110
+ duration_ = self.send_command({"command": ["get_property", "duration"]})
111
+ return duration_
112
+
113
+ @property
114
+ def video_zoom(self):
115
+ return self.send_command({"command": ["get_property", "video-zoom"]})
116
+
117
+ @video_zoom.setter
118
+ def video_zoom(self, value):
119
+ self.send_command({"command": ["set_property", "video-zoom", value]})
120
+ return
121
+
122
+ @property
123
+ def pause(self):
124
+ return self.send_command({"command": ["get_property", "pause"]})
125
+
126
+ @pause.setter
127
+ def pause(self, value):
128
+ return self.send_command({"command": ["set_property", "pause", value]})
129
+
130
+ @property
131
+ def estimated_frame_number(self):
132
+ return self.send_command({"command": ["get_property", "estimated-frame-number"]})
133
+
134
+ def stop(self):
135
+ self.send_command({"command": ["stop"]})
136
+ return
137
+
138
+ @property
139
+ def playlist(self):
140
+ return self.send_command({"command": ["get_property", "playlist"]})
141
+
142
+ def playlist_next(self):
143
+ self.send_command({"command": ["playlist-next"]})
144
+ return
145
+
146
+ def playlist_prev(self):
147
+ self.send_command({"command": ["playlist-prev"]})
148
+ return
149
+
150
+ @property
151
+ def playlist_pos(self):
152
+ return self.send_command({"command": ["get_property", "playlist-pos"]})
153
+
154
+ @playlist_pos.setter
155
+ def playlist_pos(self, value):
156
+ return self.send_command({"command": ["set_property", "playlist-pos", value]})
157
+
158
+ @property
159
+ def playlist_count(self):
160
+ return self.send_command({"command": ["get_property", "playlist-count"]})
161
+
162
+ def playlist_append(self, media):
163
+ return self.send_command({"command": ["loadfile", media, "append"]})
164
+
165
+ def wait_until_playing(self):
166
+ return
167
+
168
+ def seek(self, value, mode: str):
169
+ self.send_command({"command": ["seek", value, mode]})
170
+ return
171
+
172
+ @property
173
+ def playback_time(self):
174
+ playback_time_ = self.send_command({"command": ["get_property", "playback-time"]})
175
+ return playback_time_
176
+
177
+ def frame_step(self):
178
+ self.send_command({"command": ["frame-step"]})
179
+ return
180
+
181
+ def frame_back_step(self):
182
+ self.send_command({"command": ["frame-back-step"]})
183
+ return
184
+
185
+ def screenshot_to_file(self, value):
186
+ self.send_command({"command": ["screenshot-to-file", value, "video"]})
187
+ return
188
+
189
+ @property
190
+ def speed(self):
191
+ return self.send_command({"command": ["get_property", "speed"]})
192
+
193
+ @speed.setter
194
+ def speed(self, value):
195
+ self.send_command({"command": ["set_property", "speed", value]})
196
+ return
197
+
198
+ @property
199
+ def video_rotate(self):
200
+ return self.send_command({"command": ["get_property", "video-rotate"]})
201
+
202
+ @video_rotate.setter
203
+ def video_rotate(self, value):
204
+ self.send_command({"command": ["set_property", "video-rotate", value]})
205
+ return
206
+
207
+ @property
208
+ def sub_visibility(self):
209
+ return self.send_command({"command": ["get_property", "sub-visibility"]})
210
+
211
+ @sub_visibility.setter
212
+ def sub_visibility(self, value):
213
+ self.send_command({"command": ["set_property", "sub-visibility", value]})
214
+ return
215
+
216
+ @property
217
+ def brightness(self):
218
+ return self.send_command({"command": ["get_property", "brightness"]})
219
+
220
+ @brightness.setter
221
+ def brightness(self, value):
222
+ self.send_command({"command": ["set_property", "brightness", value]})
223
+ return
224
+
225
+ @property
226
+ def contrast(self):
227
+ return self.send_command({"command": ["get_property", "contrast"]})
228
+
229
+ @contrast.setter
230
+ def contrast(self, value):
231
+ self.send_command({"command": ["set_property", "contrast", value]})
232
+ return
233
+
234
+ @property
235
+ def saturation(self):
236
+ return self.send_command({"command": ["get_property", "saturation"]})
237
+
238
+ @saturation.setter
239
+ def saturation(self, value):
240
+ self.send_command({"command": ["set_property", "saturation", value]})
241
+ return
242
+
243
+ @property
244
+ def gamma(self):
245
+ return self.send_command({"command": ["get_property", "gamma"]})
246
+
247
+ @gamma.setter
248
+ def gamma(self, value):
249
+ self.send_command({"command": ["set_property", "gamma", value]})
250
+ return
251
+
252
+ @property
253
+ def hue(self):
254
+ return self.send_command({"command": ["get_property", "hue"]})
255
+
256
+ @hue.setter
257
+ def hue(self, value):
258
+ self.send_command({"command": ["set_property", "hue", value]})
259
+ return
260
+
261
+ @property
262
+ def container_fps(self):
263
+ return self.send_command({"command": ["get_property", "container-fps"]})
264
+
265
+ @property
266
+ def width(self):
267
+ return self.send_command({"command": ["get_property", "width"]})
268
+
269
+ @property
270
+ def height(self):
271
+ return self.send_command({"command": ["get_property", "height"]})
272
+
273
+ @property
274
+ def video_format(self):
275
+ return self.send_command({"command": ["get_property", "video-format"]})
276
+
277
+ @property
278
+ def deinterlace(self):
279
+ return self.send_command({"command": ["get_property", "deinterlace"]})
280
+
281
+ @deinterlace.setter
282
+ def deinterlace(self, value):
283
+ self.send_command({"command": ["set_property", "deinterlace", value]})
284
+ return
285
+
286
+ @property
287
+ def audio_bitrate(self):
288
+ return self.send_command({"command": ["get_property", "audio-bitrate"]})
289
+
290
+ @property
291
+ def eof_reached(self):
292
+ return self.send_command({"command": ["get_property", "eof-reached"]})
293
+
294
+ @property
295
+ def core_idle(self):
296
+ return self.send_command({"command": ["get_property", "core-idle"]})
297
+
298
+ @property
299
+ def video_pan_x(self):
300
+ return self.send_command({"command": ["get_property", "video-pan-x"]})
301
+
302
+ @video_pan_x.setter
303
+ def video_pan_x(self, value):
304
+ self.send_command({"command": ["set_property", "video-pan-x", value]})
305
+ return
306
+
307
+ @property
308
+ def video_pan_y(self):
309
+ return self.send_command({"command": ["get_property", "video-pan-y"]})
310
+
311
+ @video_pan_y.setter
312
+ def video_pan_y(self, value):
313
+ self.send_command({"command": ["set_property", "video-pan-y", value]})
314
+ return
315
+
316
+ """
317
+ @property
318
+ def xxx(self):
319
+ return self.send_command({"command": ["get_property", "xxx"]})
320
+
321
+ @xxx.setter
322
+ def xxx(self, value):
323
+ self.send_command({"command": ["set_property", "xxx", value]})
324
+ return
325
+ """
boris/irr.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2023 Olivier Friard
4
+ Copyright 2012-2025 Olivier Friard
5
5
 
6
6
 
7
7
  This program is free software; you can redistribute it and/or modify
@@ -25,7 +25,7 @@ import logging
25
25
  from decimal import Decimal as dec
26
26
 
27
27
  import numpy as np
28
- from PyQt5.QtWidgets import QInputDialog, QMessageBox
28
+ from PySide6.QtWidgets import QInputDialog, QMessageBox
29
29
 
30
30
  from . import config as cfg
31
31
  from . import db_functions, dialog, project_functions, select_subj_behav
@@ -126,7 +126,7 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
126
126
  first_event = cursor.execute(
127
127
  (
128
128
  "SELECT min(start) FROM aggregated_events "
129
- f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
129
+ f"WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "
130
130
  ),
131
131
  (obsid1, obsid2) + tuple(selected_subjects),
132
132
  ).fetchone()[0]
@@ -134,27 +134,18 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
134
134
  logging.debug(f"first_event: {first_event}")
135
135
 
136
136
  last_event = cursor.execute(
137
- (
138
- "SELECT max(stop) FROM aggregated_events "
139
- f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
140
- ),
137
+ (f"SELECT max(stop) FROM aggregated_events WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "),
141
138
  (obsid1, obsid2) + tuple(selected_subjects),
142
139
  ).fetchone()[0]
143
140
 
144
141
  logging.debug(f"last_event: {last_event}")
145
142
 
146
143
  nb_events1 = cursor.execute(
147
- (
148
- "SELECT COUNT(*) FROM aggregated_events "
149
- f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
150
- ),
144
+ (f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
151
145
  (obsid1,) + tuple(selected_subjects),
152
146
  ).fetchone()[0]
153
147
  nb_events2 = cursor.execute(
154
- (
155
- "SELECT COUNT(*) FROM aggregated_events "
156
- f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
157
- ),
148
+ (f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
158
149
  (obsid2,) + tuple(selected_subjects),
159
150
  ).fetchone()[0]
160
151
 
@@ -162,10 +153,8 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
162
153
 
163
154
  currentTime = dec(str(first_event))
164
155
  while currentTime <= last_event:
165
-
166
156
  for obsid in [obsid1, obsid2]:
167
157
  for subject in selected_subjects:
168
-
169
158
  s = subj_behav_modif(cursor, obsid, subject, currentTime, interval, include_modifiers)
170
159
 
171
160
  if s not in total_states:
@@ -185,11 +174,9 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
185
174
  seq2 = {}
186
175
  currentTime = dec(str(first_event))
187
176
  while currentTime <= last_event:
188
-
189
177
  seq1[currentTime] = []
190
178
  seq2[currentTime] = []
191
179
  for subject in selected_subjects:
192
-
193
180
  s1 = subj_behav_modif(cursor, obsid1, subject, currentTime, interval, include_modifiers)
194
181
  s2 = subj_behav_modif(cursor, obsid2, subject, currentTime, interval, include_modifiers)
195
182
 
@@ -211,11 +198,7 @@ def cohen_kappa(cursor, obsid1: str, obsid2: str, interval: dec, selected_subjec
211
198
  logging.debug(f"contingency_table:\n {contingency_table}")
212
199
 
213
200
  template = (
214
- "Observation: {obsid1}\n"
215
- "number of events: {nb_events1}\n\n"
216
- "Observation: {obsid2}\n"
217
- "number of events: {nb_events2:.0f}\n\n"
218
- "K = {K:.3f}"
201
+ "Observation: {obsid1}\nnumber of events: {nb_events1}\n\nObservation: {obsid2}\nnumber of events: {nb_events2:.0f}\n\nK = {K:.3f}"
219
202
  )
220
203
 
221
204
  # out += "Observation length: <b>{:.3f} s</b><br>".format(self.observationTotalMediaLength(obsid1))
@@ -298,8 +281,8 @@ def irr_cohen_kappa(self):
298
281
  selected_observations,
299
282
  start_coding=dec("NaN"),
300
283
  end_coding=dec("NaN"),
301
- flagShowIncludeModifiers=True,
302
- flagShowExcludeBehaviorsWoEvents=False,
284
+ show_include_modifiers=True,
285
+ show_exclude_non_coded_behaviors=False,
303
286
  n_observations=len(selected_observations),
304
287
  )
305
288
  if parameters == {}:
@@ -309,9 +292,7 @@ def irr_cohen_kappa(self):
309
292
  return
310
293
 
311
294
  # ask for time slice
312
- i, ok = QInputDialog.getDouble(
313
- self, "IRR - Cohen's Kappa (time-unit)", "Time unit (in seconds):", 1.0, 0.001, 86400, 3
314
- )
295
+ i, ok = QInputDialog.getDouble(self, "IRR - Cohen's Kappa (time-unit)", "Time unit (in seconds):", 1.0, 0.001, 86400, 3)
315
296
  if not ok:
316
297
  return
317
298
  interval = util.float2decimal(i)
@@ -354,7 +335,7 @@ def irr_cohen_kappa(self):
354
335
  out2 += "\t".join(["%8.6f" % x for x in irr_results[r, :]]) + "\n"
355
336
 
356
337
  self.results = dialog.Results_dialog()
357
- self.results.setWindowTitle(f"BORIS - IRR - Cohen's Kappa (time-unit) analysis results")
338
+ self.results.setWindowTitle("BORIS - IRR - Cohen's Kappa (time-unit) analysis results")
358
339
  self.results.ptText.setReadOnly(True)
359
340
  if len(selected_observations) == 2:
360
341
  self.results.ptText.appendPlainText(out)
@@ -363,9 +344,7 @@ def irr_cohen_kappa(self):
363
344
  self.results.show()
364
345
 
365
346
 
366
- def needleman_wunsch_identity(
367
- cursor, obsid1: str, obsid2: str, interval, selected_subjects: list, include_modifiers: bool
368
- ):
347
+ def needleman_wunsch_identity(cursor, obsid1: str, obsid2: str, interval, selected_subjects: list, include_modifiers: bool):
369
348
  """
370
349
  Needleman - Wunsch identity between 2 observations
371
350
 
@@ -484,13 +463,12 @@ def needleman_wunsch_identity(
484
463
  first_event = cursor.execute(
485
464
  (
486
465
  "SELECT min(start) FROM aggregated_events "
487
- f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
466
+ f"WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "
488
467
  ),
489
468
  (obsid1, obsid2) + tuple(selected_subjects),
490
469
  ).fetchone()[0]
491
470
 
492
471
  if first_event is None:
493
-
494
472
  logging.debug(f"An observation has no recorded events: {obsid1} or {obsid2}")
495
473
 
496
474
  return -100, f"An observation has no recorded events: {obsid1} {obsid2}"
@@ -498,28 +476,19 @@ def needleman_wunsch_identity(
498
476
  logging.debug(f"first_event: {first_event}")
499
477
 
500
478
  last_event = cursor.execute(
501
- (
502
- "SELECT max(stop) FROM aggregated_events "
503
- f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
504
- ),
479
+ (f"SELECT max(stop) FROM aggregated_events WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "),
505
480
  (obsid1, obsid2) + tuple(selected_subjects),
506
481
  ).fetchone()[0]
507
482
 
508
483
  logging.debug(f"last_event: {last_event}")
509
484
 
510
485
  nb_events1 = cursor.execute(
511
- (
512
- "SELECT COUNT(*) FROM aggregated_events "
513
- f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
514
- ),
486
+ (f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
515
487
  (obsid1,) + tuple(selected_subjects),
516
488
  ).fetchone()[0]
517
489
 
518
490
  nb_events2 = cursor.execute(
519
- (
520
- "SELECT COUNT(*) FROM aggregated_events "
521
- f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
522
- ),
491
+ (f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
523
492
  (obsid2,) + tuple(selected_subjects),
524
493
  ).fetchone()[0]
525
494
 
@@ -528,11 +497,9 @@ def needleman_wunsch_identity(
528
497
 
529
498
  currentTime = dec(str(first_event))
530
499
  while currentTime <= last_event:
531
-
532
500
  seq1[currentTime], seq2[currentTime] = [], []
533
501
 
534
502
  for subject in selected_subjects:
535
-
536
503
  s1 = subj_behav_modif(cursor, obsid1, subject, currentTime, interval, include_modifiers)
537
504
  s2 = subj_behav_modif(cursor, obsid2, subject, currentTime, interval, include_modifiers)
538
505
 
@@ -574,9 +541,7 @@ def needleman_wunch(self):
574
541
  if not selected_observations:
575
542
  return
576
543
  if len(selected_observations) < 2:
577
- QMessageBox.information(
578
- self, cfg.programName, "You have to select at least 2 observations for Needleman-Wunsch similarity"
579
- )
544
+ QMessageBox.information(self, cfg.programName, "You have to select at least 2 observations for Needleman-Wunsch similarity")
580
545
  return
581
546
 
582
547
  # check if coded behaviors are defined in ethogram
@@ -606,8 +571,8 @@ def needleman_wunch(self):
606
571
  selected_observations,
607
572
  start_coding=dec("NaN"),
608
573
  end_coding=dec("NaN"),
609
- flagShowIncludeModifiers=True,
610
- flagShowExcludeBehaviorsWoEvents=False,
574
+ show_include_modifiers=True,
575
+ show_exclude_non_coded_behaviors=False,
611
576
  n_observations=len(selected_observations),
612
577
  )
613
578
 
@@ -631,9 +596,7 @@ def needleman_wunch(self):
631
596
 
632
597
  cursor = db_connector.cursor()
633
598
  out = (
634
- "Needleman-Wunsch similarity\n\n"
635
- f"Time unit: {interval:.3f} s\n"
636
- f"Selected subjects: {', '.join(parameters[cfg.SELECTED_SUBJECTS])}\n\n"
599
+ f"Needleman-Wunsch similarity\n\nTime unit: {interval:.3f} s\nSelected subjects: {', '.join(parameters[cfg.SELECTED_SUBJECTS])}\n\n"
637
600
  )
638
601
  mem_done = []
639
602
  nws_results = np.ones((len(selected_observations), len(selected_observations)))