boris-behav-obs 8.9.16__py3-none-any.whl → 9.7.6__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.

Potentially problematic release.


This version of boris-behav-obs might be problematic. Click here for more details.

Files changed (129) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +36 -39
  4. boris/add_modifier.py +122 -109
  5. boris/add_modifier_ui.py +239 -135
  6. boris/advanced_event_filtering.py +81 -45
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +228 -229
  18. boris/behavior_binary_table.py +33 -50
  19. boris/behaviors_coding_map.py +17 -18
  20. boris/boris_cli.py +6 -25
  21. boris/cmd_arguments.py +12 -1
  22. boris/coding_pad.py +42 -49
  23. boris/config.py +161 -77
  24. boris/config_file.py +63 -83
  25. boris/connections.py +112 -57
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2511 -1824
  30. boris/core_qrc.py +15895 -10185
  31. boris/core_ui.py +946 -792
  32. boris/db_functions.py +21 -41
  33. boris/dev.py +134 -0
  34. boris/dialog.py +505 -244
  35. boris/duration_widget.py +15 -20
  36. boris/edit_event.py +84 -28
  37. boris/edit_event_ui.py +214 -78
  38. boris/event_operations.py +517 -415
  39. boris/events_cursor.py +25 -17
  40. boris/events_snapshots.py +36 -82
  41. boris/exclusion_matrix.py +4 -9
  42. boris/export_events.py +213 -583
  43. boris/export_observation.py +98 -611
  44. boris/external_processes.py +156 -97
  45. boris/geometric_measurement.py +652 -287
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +9 -9
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +26 -63
  51. boris/latency.py +34 -25
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +52 -84
  54. boris/menu_options.py +17 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv.py +1 -0
  58. boris/mpv2.py +732 -705
  59. boris/observation.py +655 -310
  60. boris/observation_operations.py +1036 -404
  61. boris/observation_ui.py +584 -356
  62. boris/observations_list.py +71 -53
  63. boris/otx_parser.py +74 -80
  64. boris/param_panel.py +31 -16
  65. boris/param_panel_ui.py +254 -138
  66. boris/player_dock_widget.py +90 -60
  67. boris/plot_data_module.py +43 -46
  68. boris/plot_events.py +127 -90
  69. boris/plot_events_rt.py +17 -31
  70. boris/plot_spectrogram_rt.py +95 -30
  71. boris/plot_waveform_rt.py +32 -21
  72. boris/plugins.py +431 -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 +306 -83
  80. boris/preferences_ui.py +685 -228
  81. boris/project.py +448 -293
  82. boris/project_functions.py +689 -254
  83. boris/project_import_export.py +213 -222
  84. boris/project_ui.py +674 -438
  85. boris/qrc_boris.py +6 -3
  86. boris/qrc_boris5.py +6 -3
  87. boris/select_modifiers.py +74 -48
  88. boris/select_observations.py +20 -199
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +53 -37
  91. boris/subjects_pad.py +6 -9
  92. boris/synthetic_time_budget.py +45 -28
  93. boris/time_budget_functions.py +171 -171
  94. boris/time_budget_widget.py +84 -114
  95. boris/transitions.py +41 -47
  96. boris/utilities.py +766 -266
  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 +125 -28
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +538 -0
  104. boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
  106. {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
  107. boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
  108. boris/README.TXT +0 -22
  109. boris/add_modifier.ui +0 -323
  110. boris/boris_ui.py +0 -886
  111. boris/converters.ui +0 -289
  112. boris/core.qrc +0 -35
  113. boris/core.ui +0 -1543
  114. boris/edit_event.ui +0 -175
  115. boris/icons/logo_eye.ico +0 -0
  116. boris/map_creator.py +0 -850
  117. boris/observation.ui +0 -773
  118. boris/param_panel.ui +0 -379
  119. boris/preferences.ui +0 -537
  120. boris/project.ui +0 -1069
  121. boris/project_server.py +0 -236
  122. boris/vlc.py +0 -10343
  123. boris/vlc_local.py +0 -90
  124. boris_behav_obs-8.9.16.dist-info/LICENSE.TXT +0 -674
  125. boris_behav_obs-8.9.16.dist-info/METADATA +0 -129
  126. boris_behav_obs-8.9.16.dist-info/RECORD +0 -108
  127. boris_behav_obs-8.9.16.dist-info/entry_points.txt +0 -2
  128. {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
  129. {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.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", # Keeps mpv running with no video
66
+ "--input-default-bindings=no",
67
+ "--input-vo-keyboard=no",
68
+ ],
69
+ stdout=subprocess.PIPE,
70
+ stderr=subprocess.PIPE,
71
+ )
72
+
73
+ def send_command(self, command):
74
+ """
75
+ Send a JSON command to the mpv IPC server.
76
+ """
77
+ # print(f"send command: {command}")
78
+ try:
79
+ # Create a Unix socket
80
+ with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
81
+ # Connect to the MPV IPC server
82
+ client.connect(self.socket_path)
83
+ # Send the JSON command
84
+ # print(f"{json.dumps(command).encode('utf-8')=}")
85
+ client.sendall(json.dumps(command).encode("utf-8") + b"\n")
86
+ # Receive the response
87
+ response = client.recv(2000)
88
+
89
+ # print(f"{response=}")
90
+ # Parse the response as JSON
91
+ response_data = json.loads(response.decode("utf-8"))
92
+ if response_data["error"] != "success":
93
+ logging.warning(f"send command: {command} response data: {response_data}")
94
+ # Return the 'data' field which contains the playback position
95
+ return response_data.get("data")
96
+ except FileNotFoundError:
97
+ logger.critical("Error: Socket file not found.")
98
+ except Exception as e:
99
+ logger.critical(f"An error occurred: {e}")
100
+ return None
101
+
102
+ @property
103
+ def time_pos(self):
104
+ time_pos = self.send_command({"command": ["get_property", "time-pos"]})
105
+ return time_pos
106
+
107
+ @property
108
+ def duration(self):
109
+ duration_ = self.send_command({"command": ["get_property", "duration"]})
110
+ return duration_
111
+
112
+ @property
113
+ def video_zoom(self):
114
+ return self.send_command({"command": ["get_property", "video-zoom"]})
115
+
116
+ @video_zoom.setter
117
+ def video_zoom(self, value):
118
+ self.send_command({"command": ["set_property", "video-zoom", value]})
119
+ return
120
+
121
+ @property
122
+ def pause(self):
123
+ return self.send_command({"command": ["get_property", "pause"]})
124
+
125
+ @pause.setter
126
+ def pause(self, value):
127
+ return self.send_command({"command": ["set_property", "pause", value]})
128
+
129
+ @property
130
+ def estimated_frame_number(self):
131
+ return self.send_command({"command": ["get_property", "estimated-frame-number"]})
132
+
133
+ def stop(self):
134
+ self.send_command({"command": ["stop"]})
135
+ return
136
+
137
+ @property
138
+ def playlist(self):
139
+ return self.send_command({"command": ["get_property", "playlist"]})
140
+
141
+ def playlist_next(self):
142
+ self.send_command({"command": ["playlist-next"]})
143
+ return
144
+
145
+ def playlist_prev(self):
146
+ self.send_command({"command": ["playlist-prev"]})
147
+ return
148
+
149
+ @property
150
+ def playlist_pos(self):
151
+ return self.send_command({"command": ["get_property", "playlist-pos"]})
152
+
153
+ @playlist_pos.setter
154
+ def playlist_pos(self, value):
155
+ return self.send_command({"command": ["set_property", "playlist-pos", value]})
156
+
157
+ @property
158
+ def playlist_count(self):
159
+ return self.send_command({"command": ["get_property", "playlist-count"]})
160
+
161
+ def playlist_append(self, media):
162
+ return self.send_command({"command": ["loadfile", media, "append"]})
163
+
164
+ def wait_until_playing(self):
165
+ return
166
+
167
+ def seek(self, value, mode: str):
168
+ self.send_command({"command": ["seek", value, mode]})
169
+ return
170
+
171
+ @property
172
+ def playback_time(self):
173
+ playback_time_ = self.send_command({"command": ["get_property", "playback-time"]})
174
+ return playback_time_
175
+
176
+ def frame_step(self):
177
+ self.send_command({"command": ["frame-step"]})
178
+ return
179
+
180
+ def frame_back_step(self):
181
+ self.send_command({"command": ["frame-back-step"]})
182
+ return
183
+
184
+ def screenshot_to_file(self, value):
185
+ self.send_command({"command": ["screenshot-to-file", value, "video"]})
186
+ return
187
+
188
+ @property
189
+ def speed(self):
190
+ return self.send_command({"command": ["get_property", "speed"]})
191
+
192
+ @speed.setter
193
+ def speed(self, value):
194
+ self.send_command({"command": ["set_property", "speed", value]})
195
+ return
196
+
197
+ @property
198
+ def video_rotate(self):
199
+ return self.send_command({"command": ["get_property", "video-rotate"]})
200
+
201
+ @video_rotate.setter
202
+ def video_rotate(self, value):
203
+ self.send_command({"command": ["set_property", "video-rotate", value]})
204
+ return
205
+
206
+ @property
207
+ def sub_visibility(self):
208
+ return self.send_command({"command": ["get_property", "sub-visibility"]})
209
+
210
+ @sub_visibility.setter
211
+ def sub_visibility(self, value):
212
+ self.send_command({"command": ["set_property", "sub-visibility", value]})
213
+ return
214
+
215
+ @property
216
+ def brightness(self):
217
+ return self.send_command({"command": ["get_property", "brightness"]})
218
+
219
+ @brightness.setter
220
+ def brightness(self, value):
221
+ self.send_command({"command": ["set_property", "brightness", value]})
222
+ return
223
+
224
+ @property
225
+ def contrast(self):
226
+ return self.send_command({"command": ["get_property", "contrast"]})
227
+
228
+ @contrast.setter
229
+ def contrast(self, value):
230
+ self.send_command({"command": ["set_property", "contrast", value]})
231
+ return
232
+
233
+ @property
234
+ def saturation(self):
235
+ return self.send_command({"command": ["get_property", "saturation"]})
236
+
237
+ @saturation.setter
238
+ def saturation(self, value):
239
+ self.send_command({"command": ["set_property", "saturation", value]})
240
+ return
241
+
242
+ @property
243
+ def gamma(self):
244
+ return self.send_command({"command": ["get_property", "gamma"]})
245
+
246
+ @gamma.setter
247
+ def gamma(self, value):
248
+ self.send_command({"command": ["set_property", "gamma", value]})
249
+ return
250
+
251
+ @property
252
+ def hue(self):
253
+ return self.send_command({"command": ["get_property", "hue"]})
254
+
255
+ @hue.setter
256
+ def hue(self, value):
257
+ self.send_command({"command": ["set_property", "hue", value]})
258
+ return
259
+
260
+ @property
261
+ def container_fps(self):
262
+ return self.send_command({"command": ["get_property", "container-fps"]})
263
+
264
+ @property
265
+ def width(self):
266
+ return self.send_command({"command": ["get_property", "width"]})
267
+
268
+ @property
269
+ def height(self):
270
+ return self.send_command({"command": ["get_property", "height"]})
271
+
272
+ @property
273
+ def video_format(self):
274
+ return self.send_command({"command": ["get_property", "video-format"]})
275
+
276
+ @property
277
+ def deinterlace(self):
278
+ return self.send_command({"command": ["get_property", "deinterlace"]})
279
+
280
+ @deinterlace.setter
281
+ def deinterlace(self, value):
282
+ self.send_command({"command": ["set_property", "deinterlace", value]})
283
+ return
284
+
285
+ @property
286
+ def audio_bitrate(self):
287
+ return self.send_command({"command": ["get_property", "audio-bitrate"]})
288
+
289
+ @property
290
+ def eof_reached(self):
291
+ return self.send_command({"command": ["get_property", "eof-reached"]})
292
+
293
+ @property
294
+ def core_idle(self):
295
+ return self.send_command({"command": ["get_property", "core-idle"]})
296
+
297
+ @property
298
+ def video_pan_x(self):
299
+ return self.send_command({"command": ["get_property", "video-pan-x"]})
300
+
301
+ @video_pan_x.setter
302
+ def video_pan_x(self, value):
303
+ self.send_command({"command": ["set_property", "video-pan-x", value]})
304
+ return
305
+
306
+ @property
307
+ def video_pan_y(self):
308
+ return self.send_command({"command": ["get_property", "video-pan-y"]})
309
+
310
+ @video_pan_y.setter
311
+ def video_pan_y(self, value):
312
+ self.send_command({"command": ["set_property", "video-pan-y", value]})
313
+ return
314
+
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
 
@@ -408,22 +387,19 @@ def needleman_wunsch_identity(
408
387
  align1 = align1[::-1]
409
388
  align2 = align2[::-1]
410
389
 
411
- i, j = 0, 0
412
-
390
+ i = 0
413
391
  symbol = []
414
- found = 0
415
392
  score = 0
416
393
  identity = 0
417
394
  for i in range(0, len(align1)):
418
395
  if align1[i] == align2[i]:
419
396
  symbol.append(align1[i])
420
- identity = identity + 1
397
+ identity += 1
421
398
  score += match_score(align1[i], align2[i])
422
399
 
423
400
  elif align1[i] != align2[i] and align1[i] != "-" and align2[i] != "-":
424
401
  score += match_score(align1[i], align2[i])
425
402
  symbol.append(" ")
426
- found = 0
427
403
 
428
404
  # if one of them is a gap, output a space
429
405
  elif align1[i] == "-" or align2[i] == "-":
@@ -487,52 +463,43 @@ def needleman_wunsch_identity(
487
463
  first_event = cursor.execute(
488
464
  (
489
465
  "SELECT min(start) FROM aggregated_events "
490
- f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
466
+ f"WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "
491
467
  ),
492
468
  (obsid1, obsid2) + tuple(selected_subjects),
493
469
  ).fetchone()[0]
494
470
 
495
471
  if first_event is None:
496
472
  logging.debug(f"An observation has no recorded events: {obsid1} or {obsid2}")
473
+
497
474
  return -100, f"An observation has no recorded events: {obsid1} {obsid2}"
498
475
 
499
476
  logging.debug(f"first_event: {first_event}")
500
477
 
501
478
  last_event = cursor.execute(
502
- (
503
- "SELECT max(start) FROM aggregated_events "
504
- f"WHERE observation in (?, ?) AND subject in ({','.join('?'*len(selected_subjects))}) "
505
- ),
479
+ (f"SELECT max(stop) FROM aggregated_events WHERE observation in (?, ?) AND subject in ({','.join('?' * len(selected_subjects))}) "),
506
480
  (obsid1, obsid2) + tuple(selected_subjects),
507
481
  ).fetchone()[0]
508
482
 
509
483
  logging.debug(f"last_event: {last_event}")
510
484
 
511
485
  nb_events1 = cursor.execute(
512
- (
513
- "SELECT COUNT(*) FROM aggregated_events "
514
- f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
515
- ),
486
+ (f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
516
487
  (obsid1,) + tuple(selected_subjects),
517
488
  ).fetchone()[0]
518
489
 
519
490
  nb_events2 = cursor.execute(
520
- (
521
- "SELECT COUNT(*) FROM aggregated_events "
522
- f"WHERE observation = ? AND subject in ({','.join('?'*len(selected_subjects))}) "
523
- ),
491
+ (f"SELECT COUNT(*) FROM aggregated_events WHERE observation = ? AND subject in ({','.join('?' * len(selected_subjects))}) "),
524
492
  (obsid2,) + tuple(selected_subjects),
525
493
  ).fetchone()[0]
526
494
 
527
- seq1, seq2 = {}, {}
495
+ seq1: dict = {}
496
+ seq2: dict = {}
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)))
@@ -662,7 +625,7 @@ def needleman_wunch(self):
662
625
  out2 += "\t".join([f"{x:8.6f}" for x in nws_results[r, :]]) + "\n"
663
626
 
664
627
  self.results = dialog.Results_dialog()
665
- self.results.setWindowTitle(cfg.programName + " - Needleman-Wunsch similarity")
628
+ self.results.setWindowTitle(f"{cfg.programName} - Needleman-Wunsch similarity")
666
629
  self.results.ptText.setReadOnly(True)
667
630
  if len(selected_observations) == 2:
668
631
  self.results.ptText.appendPlainText(out)