boris-behav-obs 8.25.4__py2.py3-none-any.whl → 8.26.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.
boris/select_modifiers.py CHANGED
@@ -43,6 +43,10 @@ from . import utilities as util
43
43
 
44
44
 
45
45
  class ModifiersList(QDialog):
46
+ """
47
+ class for selection the modifier(s)
48
+ """
49
+
46
50
  def __init__(self, code: str, modifiers_dict: dict, currentModifier: str):
47
51
  super().__init__()
48
52
  self.setWindowTitle(cfg.programName)
@@ -60,11 +64,11 @@ class ModifiersList(QDialog):
60
64
  self.modifiersSetNumber = 0
61
65
 
62
66
  for idx in util.sorted_keys(modifiers_dict):
63
- if self.modifiers_dict[idx]["type"] not in [
67
+ if self.modifiers_dict[idx]["type"] not in (
64
68
  cfg.SINGLE_SELECTION,
65
69
  cfg.MULTI_SELECTION,
66
70
  cfg.NUMERIC_MODIFIER,
67
- ]:
71
+ ):
68
72
  continue
69
73
 
70
74
  V2layout = QVBoxLayout()
@@ -75,10 +79,13 @@ class ModifiersList(QDialog):
75
79
  lb.setText(f"Modifier <b>{self.modifiers_dict[idx]['name']}</b>")
76
80
  V2layout.addWidget(lb)
77
81
 
78
- if self.modifiers_dict[idx]["type"] in [
82
+ lb = QLabel(f"<small>{self.modifiers_dict[idx]['description']}</small>")
83
+ V2layout.addWidget(lb)
84
+
85
+ if self.modifiers_dict[idx]["type"] in (
79
86
  cfg.SINGLE_SELECTION,
80
87
  cfg.MULTI_SELECTION,
81
- ]:
88
+ ):
82
89
  lw = QListWidget()
83
90
  self.modifiers_dict[idx]["widget"] = lw
84
91
  lw.setObjectName(f"lw_modifiers_({self.modifiers_dict[idx]['type']})")
@@ -114,6 +121,7 @@ class ModifiersList(QDialog):
114
121
  if self.modifiers_dict[idx]["type"] in [cfg.NUMERIC_MODIFIER]:
115
122
  le = QLineEdit()
116
123
  self.modifiers_dict[idx]["widget"] = le
124
+ le.setObjectName(f"le_modifiers_({self.modifiers_dict[idx]['type']})")
117
125
 
118
126
  if currentModifierList != [""] and currentModifierList[int(idx)] != "None":
119
127
  le.setText(currentModifierList[int(idx)])
@@ -160,10 +168,14 @@ class ModifiersList(QDialog):
160
168
 
161
169
  # accept and close dialog if enter pressed
162
170
  if ek == Qt.Key_Enter or ek == Qt.Key_Return: # enter or enter from numeric pad
163
- self.accept()
171
+ if not self.pbOK_clicked():
172
+ return False
164
173
  return True
165
174
 
175
+ modifiersSetIndex = 0
166
176
  for widget in self.children():
177
+ if "_modifiers" in widget.objectName():
178
+ modifiersSetIndex = modifiersSetIndex + 1
167
179
  if "lw_modifiers" in widget.objectName():
168
180
  if self.modifiersSetNumber == 1:
169
181
  # check if modifiers have code
@@ -172,7 +184,7 @@ class ModifiersList(QDialog):
172
184
  break
173
185
  else:
174
186
  # modifiers have no associated code: the modifier starting with hit key will be selected
175
- if ek not in [Qt.Key_Down, Qt.Key_Up]:
187
+ if ek not in (Qt.Key_Down, Qt.Key_Up):
176
188
  if ek == Qt.Key_Space and f"({cfg.MULTI_SELECTION})" in widget.objectName(): # checking using SPACE bar
177
189
  if widget.item(widget.currentRow()).checkState() == Qt.Checked:
178
190
  widget.item(widget.currentRow()).setCheckState(Qt.Unchecked)
@@ -198,6 +210,7 @@ class ModifiersList(QDialog):
198
210
  return
199
211
 
200
212
  for index in range(widget.count()):
213
+ # check function kesy (F1, F2...)
201
214
  if ek in cfg.function_keys:
202
215
  if f"({cfg.function_keys[ek]})" in widget.item(index).text().upper():
203
216
  if f"({cfg.SINGLE_SELECTION})" in widget.objectName():
@@ -206,6 +219,10 @@ class ModifiersList(QDialog):
206
219
  if self.modifiersSetNumber == 1:
207
220
  self.accept()
208
221
  return True
222
+ # else move to next set of mofifiers
223
+ elif modifiersSetIndex != self.modifiersSetNumber:
224
+ widget.parent().focusNextChild()
225
+ return True
209
226
 
210
227
  if f"({cfg.MULTI_SELECTION})" in widget.objectName():
211
228
  if widget.item(index).checkState() == Qt.Checked:
@@ -220,6 +237,10 @@ class ModifiersList(QDialog):
220
237
  if self.modifiersSetNumber == 1:
221
238
  self.accept()
222
239
  return True
240
+ # else move to next set of mofifiers
241
+ elif modifiersSetIndex != self.modifiersSetNumber:
242
+ widget.parent().focusNextChild()
243
+ return True
223
244
 
224
245
  if f"({cfg.MULTI_SELECTION})" in widget.objectName():
225
246
  if widget.item(index).checkState() == Qt.Checked:
@@ -271,6 +292,9 @@ class ModifiersList(QDialog):
271
292
  return self.modifiers_dict
272
293
 
273
294
  def pbOK_clicked(self):
295
+ """
296
+ OK button is clicked
297
+ """
274
298
  for idx in util.sorted_keys(self.modifiers_dict):
275
299
  if self.modifiers_dict[idx]["type"] == cfg.NUMERIC_MODIFIER:
276
300
  if self.modifiers_dict[idx]["widget"].text():
@@ -280,8 +304,8 @@ class ModifiersList(QDialog):
280
304
  QMessageBox.warning(
281
305
  self,
282
306
  cfg.programName,
283
- "<b>{}</b> is not a numeric value".format(self.modifiers_dict[idx]["widget"].text()),
307
+ f"<b>{self.modifiers_dict[idx]['widget'].text()}</b> is not a numeric value",
284
308
  )
285
- return
309
+ return False
286
310
 
287
311
  self.accept()
@@ -112,7 +112,7 @@ class timeBudgetResults(QWidget):
112
112
  spacerItem = QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
113
113
  hbox2.addItem(spacerItem)
114
114
 
115
- self.pbClose = QPushButton("Close", clicked=self.close_clicked)
115
+ self.pbClose = QPushButton(cfg.CLOSE, clicked=self.close_clicked)
116
116
  hbox2.addWidget(self.pbClose)
117
117
 
118
118
  hbox.addLayout(hbox2)
boris/utilities.py CHANGED
@@ -537,9 +537,9 @@ def get_current_states_modifiers_by_subject(
537
537
  break
538
538
  if x[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_behaviors_codes:
539
539
  if (x[cfg.EVENT_BEHAVIOR_FIELD_IDX], x[cfg.EVENT_MODIFIER_FIELD_IDX]) not in current_states[x[cfg.EVENT_SUBJECT_FIELD_IDX]]:
540
- current_states[x[cfg.EVENT_SUBJECT_FIELD_IDX]][
541
- (x[cfg.EVENT_BEHAVIOR_FIELD_IDX], x[cfg.EVENT_MODIFIER_FIELD_IDX])
542
- ] = False
540
+ current_states[x[cfg.EVENT_SUBJECT_FIELD_IDX]][(x[cfg.EVENT_BEHAVIOR_FIELD_IDX], x[cfg.EVENT_MODIFIER_FIELD_IDX])] = (
541
+ False
542
+ )
543
543
 
544
544
  current_states[x[cfg.EVENT_SUBJECT_FIELD_IDX]][
545
545
  (x[cfg.EVENT_BEHAVIOR_FIELD_IDX], x[cfg.EVENT_MODIFIER_FIELD_IDX])
@@ -1493,6 +1493,17 @@ def all_subjects(subjects: dict) -> list:
1493
1493
  return [subjects[x][cfg.SUBJECT_NAME] for x in sorted_keys(subjects)]
1494
1494
 
1495
1495
 
1496
+ def has_coding_map(ethogram: dict, behavior_idx: str) -> bool:
1497
+ """
1498
+ check if behavior index has a coding map
1499
+ """
1500
+ if not ethogram.get(behavior_idx, False):
1501
+ return False
1502
+ if not ethogram[behavior_idx].get("coding map", False):
1503
+ return False
1504
+ return False
1505
+
1506
+
1496
1507
  def dir_images_number(dir_path_str: str) -> dict:
1497
1508
  """
1498
1509
  return number of images in dir_path (see cfg.IMAGE_EXTENSIONS)
boris/version.py CHANGED
@@ -20,5 +20,5 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
- __version__ = "8.25.4"
24
- __version_date__ = "2024-04-18"
23
+ __version__ = "8.26.1"
24
+ __version_date__ = "2024-06-06"
boris/write_event.py CHANGED
@@ -181,75 +181,6 @@ def write_event(self, event: dict, mem_time: dec) -> int:
181
181
  )
182
182
  return 1
183
183
 
184
- if "from map" not in event: # modifiers only for behaviors without coding map
185
- # check if event has modifiers
186
- modifier_str = ""
187
-
188
- if event[cfg.MODIFIERS]:
189
- selected_modifiers, modifiers_external_data = {}, {}
190
- # check if modifiers are from external data
191
- for idx in event[cfg.MODIFIERS]:
192
- if event[cfg.MODIFIERS][idx]["type"] == cfg.EXTERNAL_DATA_MODIFIER:
193
- if "row" not in event: # no edit
194
- for idx2 in self.plot_data:
195
- if self.plot_data[idx2].y_label.upper() == event[cfg.MODIFIERS][idx]["name"].upper():
196
- modifiers_external_data[idx] = dict(event[cfg.MODIFIERS][idx])
197
- modifiers_external_data[idx]["selected"] = self.plot_data[idx2].lb_value.text()
198
- else: # edit
199
- original_modifiers_list = event.get("original_modifiers", "").split("|")
200
- modifiers_external_data[idx] = dict(event[cfg.MODIFIERS][idx])
201
- modifiers_external_data[idx]["selected"] = original_modifiers_list[int(idx)]
202
-
203
- # check if modifiers are in single, multiple or numeric
204
- if [x for x in event[cfg.MODIFIERS] if event[cfg.MODIFIERS][x]["type"] != cfg.EXTERNAL_DATA_MODIFIER]:
205
- # pause media
206
- if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in [cfg.MEDIA]:
207
- if self.playerType == cfg.MEDIA:
208
- if self.dw_player[0].player.pause:
209
- memState = "paused"
210
- elif self.dw_player[0].player.time_pos is not None:
211
- memState = "playing"
212
- else:
213
- memState = "stopped"
214
- if memState == "playing":
215
- self.pause_video()
216
-
217
- # check if editing (original_modifiers key)
218
- currentModifiers = event.get("original_modifiers", "")
219
-
220
- modifiers_selector = select_modifiers.ModifiersList(event["code"], eval(str(event[cfg.MODIFIERS])), currentModifiers)
221
-
222
- r = modifiers_selector.exec_()
223
- if r:
224
- selected_modifiers = modifiers_selector.get_modifiers()
225
-
226
- # restart media
227
- if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.MEDIA:
228
- if self.playerType == cfg.MEDIA:
229
- if memState == "playing":
230
- self.play_video()
231
- if not r: # cancel button pressed
232
- return
233
-
234
- all_modifiers = {**selected_modifiers, **modifiers_external_data}
235
-
236
- modifier_str = ""
237
- for idx in util.sorted_keys(all_modifiers):
238
- if modifier_str:
239
- modifier_str += "|"
240
- if all_modifiers[idx]["type"] in [cfg.SINGLE_SELECTION, cfg.MULTI_SELECTION]:
241
- modifier_str += ",".join(all_modifiers[idx].get("selected", ""))
242
- if all_modifiers[idx]["type"] in [cfg.NUMERIC_MODIFIER, cfg.EXTERNAL_DATA_MODIFIER]:
243
- modifier_str += all_modifiers[idx].get("selected", "NA")
244
-
245
- else:
246
- modifier_str = event["from map"]
247
-
248
- modifier_str = re.sub(r" \(.*\)", "", modifier_str)
249
-
250
- # update current state
251
- # TODO: verify event["subject"] / self.currentSubject
252
-
253
184
  # extract State events
254
185
  state_behaviors_codes = util.state_behavior_codes(self.pj[cfg.ETHOGRAM])
255
186
 
@@ -261,7 +192,7 @@ def write_event(self, event: dict, mem_time: dec) -> int:
261
192
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.IMAGES:
262
193
  position = dec(image_idx) # decimal to pass to util.get_current_states_modifiers_by_subject
263
194
 
264
- current_states = util.get_current_states_modifiers_by_subject(
195
+ current_states: dict = util.get_current_states_modifiers_by_subject(
265
196
  state_behaviors_codes,
266
197
  self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS],
267
198
  dict(self.pj[cfg.SUBJECTS], **{"": {"name": ""}}),
@@ -269,6 +200,108 @@ def write_event(self, event: dict, mem_time: dec) -> int:
269
200
  include_modifiers=False,
270
201
  )
271
202
 
203
+ # check if ask modifiers at stop is enabled
204
+ flag_ask_at_stop = False
205
+ if event["type"] == cfg.STATE_EVENT:
206
+ for idx in event[cfg.MODIFIERS]:
207
+ if event[cfg.MODIFIERS][idx].get("ask at stop", False):
208
+ flag_ask_at_stop = True
209
+ break
210
+
211
+ flag_ask_modifier = False
212
+ if flag_ask_at_stop:
213
+ # TODO: check if new event is a STOP one
214
+
215
+ idx_subject: str = ""
216
+ for idx in current_states:
217
+ if idx in self.pj[cfg.SUBJECTS] and self.pj[cfg.SUBJECTS][idx][cfg.SUBJECT_NAME] == self.currentSubject:
218
+ idx_subject = idx
219
+ break
220
+
221
+ if event[cfg.BEHAVIOR_CODE] in current_states[idx_subject]:
222
+ flag_ask_modifier = True
223
+ else:
224
+ flag_ask_modifier = True
225
+
226
+ if flag_ask_modifier:
227
+ if "from map" in event: # modifiers only for behaviors without coding map
228
+ modifier_str = event["from map"]
229
+ else:
230
+ # check if event has modifiers
231
+ modifier_str: str = ""
232
+
233
+ if event[cfg.MODIFIERS]:
234
+ selected_modifiers: dict = {}
235
+ modifiers_external_data: dict = {}
236
+ # check if modifiers are from external data
237
+ for idx in event[cfg.MODIFIERS]:
238
+ if event[cfg.MODIFIERS][idx]["type"] == cfg.EXTERNAL_DATA_MODIFIER:
239
+ if editing_event:
240
+ original_modifiers_list = event.get("original_modifiers", "").split("|")
241
+ modifiers_external_data[idx] = dict(event[cfg.MODIFIERS][idx])
242
+ modifiers_external_data[idx]["selected"] = original_modifiers_list[int(idx)]
243
+
244
+ else: # no edit
245
+ for idx2 in self.plot_data:
246
+ if self.plot_data[idx2].y_label.upper() == event[cfg.MODIFIERS][idx]["name"].upper():
247
+ modifiers_external_data[idx] = dict(event[cfg.MODIFIERS][idx])
248
+ modifiers_external_data[idx]["selected"] = self.plot_data[idx2].lb_value.text()
249
+
250
+ # check if modifiers are in single, multiple or numeric
251
+ if [x for x in event[cfg.MODIFIERS] if event[cfg.MODIFIERS][x]["type"] != cfg.EXTERNAL_DATA_MODIFIER]:
252
+ # pause media
253
+ if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in (cfg.MEDIA):
254
+ if self.playerType == cfg.MEDIA:
255
+ if self.dw_player[0].player.pause:
256
+ memState = cfg.PAUSED
257
+ elif self.dw_player[0].player.time_pos is not None:
258
+ memState = cfg.PLAYING
259
+ else:
260
+ memState = cfg.STOPPED
261
+ if memState == cfg.PLAYING:
262
+ self.pause_video()
263
+
264
+ # check if editing (original_modifiers key)
265
+ currentModifiers = event.get("original_modifiers", "")
266
+
267
+ # print(f"{event=}")
268
+
269
+ modifiers_selector = select_modifiers.ModifiersList(
270
+ event[cfg.BEHAVIOR_CODE], eval(str(event[cfg.MODIFIERS])), currentModifiers
271
+ )
272
+
273
+ r = modifiers_selector.exec_()
274
+ if r:
275
+ selected_modifiers = modifiers_selector.get_modifiers()
276
+
277
+ # restart media
278
+ if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.MEDIA:
279
+ if self.playerType == cfg.MEDIA:
280
+ if memState == cfg.PLAYING:
281
+ self.play_video()
282
+ if not r: # cancel button pressed
283
+ return
284
+
285
+ all_modifiers = {**selected_modifiers, **modifiers_external_data}
286
+
287
+ modifier_str: str = ""
288
+ for idx in util.sorted_keys(all_modifiers):
289
+ if modifier_str:
290
+ modifier_str += "|"
291
+ if all_modifiers[idx]["type"] in (cfg.SINGLE_SELECTION, cfg.MULTI_SELECTION):
292
+ modifier_str += ",".join(all_modifiers[idx].get("selected", ""))
293
+ if all_modifiers[idx]["type"] in (cfg.NUMERIC_MODIFIER, cfg.EXTERNAL_DATA_MODIFIER):
294
+ modifier_str += all_modifiers[idx].get("selected", "NA")
295
+
296
+ modifier_str = re.sub(r" \(.*\)", "", modifier_str)
297
+ else: # do not ask modifier
298
+ modifier_str = ""
299
+
300
+ # update current state
301
+ # TODO: verify event["subject"] / self.currentSubject
302
+
303
+ # print(f"{current_states=}")
304
+
272
305
  # logging.debug(f"self.currentSubject {self.currentSubject}")
273
306
  # logging.debug(f"current_states {current_states}")
274
307
 
@@ -279,7 +312,7 @@ def write_event(self, event: dict, mem_time: dec) -> int:
279
312
 
280
313
  if not editing_event:
281
314
  if self.currentSubject:
282
- csj: list = []
315
+ csj: list = [] # list of current state for the current subject
283
316
  for idx in current_states:
284
317
  if idx in self.pj[cfg.SUBJECTS] and self.pj[cfg.SUBJECTS][idx][cfg.SUBJECT_NAME] == self.currentSubject:
285
318
  csj = current_states[idx]
@@ -293,6 +326,8 @@ def write_event(self, event: dict, mem_time: dec) -> int:
293
326
 
294
327
  logging.debug(f"csj {csj}")
295
328
 
329
+ # print(f"{csj=}")
330
+
296
331
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in (cfg.LIVE, cfg.MEDIA):
297
332
  check_index = cfg.PJ_OBS_FIELDS[self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE]][cfg.TIME]
298
333
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.IMAGES:
@@ -308,6 +343,23 @@ def write_event(self, event: dict, mem_time: dec) -> int:
308
343
  if ev[cfg.EVENT_BEHAVIOR_FIELD_IDX] == cs:
309
344
  cm[cs] = ev[cfg.EVENT_MODIFIER_FIELD_IDX]
310
345
 
346
+ # print(f"{cm=}")
347
+
348
+ if flag_ask_at_stop:
349
+ # set modifier to START behavior
350
+ if event[cfg.BEHAVIOR_CODE] in csj:
351
+ mem_idx = -1
352
+ for idx, e in enumerate(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS]):
353
+ # print(f"{e=}")
354
+ if e[0] >= mem_time:
355
+ break
356
+ # same behavior, same subject and modifier(s) not set
357
+ if e[2] == event[cfg.BEHAVIOR_CODE] and e[1] == self.currentSubject and e[3] == "":
358
+ mem_idx = idx
359
+ if mem_idx != -1:
360
+ self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][mem_idx][3] = modifier_str
361
+ csj.remove(event[cfg.BEHAVIOR_CODE])
362
+
311
363
  for cs in csj:
312
364
  # close state if same state without modifier
313
365
  if (
@@ -318,23 +370,74 @@ def write_event(self, event: dict, mem_time: dec) -> int:
318
370
  modifier_str = cm[cs]
319
371
  continue
320
372
 
321
- if (event["excluded"] and cs in event["excluded"].split(",")) or (event[cfg.BEHAVIOR_CODE] == cs and cm[cs] != modifier_str):
373
+ if (event[cfg.EXCLUDED] and cs in event[cfg.EXCLUDED].split(",")) or (
374
+ event[cfg.BEHAVIOR_CODE] == cs and cm[cs] != modifier_str
375
+ ):
376
+ # check if behavior to stop is a 'ask modifier at stop'
377
+ behavior_to_stop = [
378
+ self.pj[cfg.ETHOGRAM][x]
379
+ for x in self.pj[cfg.ETHOGRAM]
380
+ if self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] == cs and self.pj[cfg.ETHOGRAM][x]["type"] == cfg.STATE_EVENT
381
+ ]
382
+ if behavior_to_stop:
383
+ behavior_to_stop = behavior_to_stop[0]
384
+
385
+ flag_behavior_ask_at_stop = False
386
+ for idx in behavior_to_stop[cfg.MODIFIERS]:
387
+ if behavior_to_stop[cfg.MODIFIERS][idx].get("ask at stop", False):
388
+ flag_behavior_ask_at_stop = True
389
+ break
390
+ if flag_behavior_ask_at_stop:
391
+ modifiers_selector = select_modifiers.ModifiersList(
392
+ behavior_to_stop[cfg.BEHAVIOR_CODE],
393
+ eval(str(behavior_to_stop[cfg.MODIFIERS])),
394
+ currentModifier="",
395
+ )
396
+
397
+ r = modifiers_selector.exec_()
398
+ if r:
399
+ selected_modifiers = modifiers_selector.get_modifiers()
400
+ # print(f"{selected_modifiers=}")
401
+
402
+ behavior_to_stop_modifier_str: str = ""
403
+ for idx in util.sorted_keys(selected_modifiers):
404
+ if behavior_to_stop_modifier_str:
405
+ behavior_to_stop_modifier_str += "|"
406
+ if selected_modifiers[idx]["type"] in (cfg.SINGLE_SELECTION, cfg.MULTI_SELECTION):
407
+ behavior_to_stop_modifier_str += ",".join(selected_modifiers[idx].get("selected", ""))
408
+ if selected_modifiers[idx]["type"] in (cfg.NUMERIC_MODIFIER, cfg.EXTERNAL_DATA_MODIFIER):
409
+ behavior_to_stop_modifier_str += selected_modifiers[idx].get("selected", "NA")
410
+
411
+ # set the start modifier
412
+ mem_idx = -1
413
+ for idx, e in enumerate(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS]):
414
+ if e[0] >= mem_time - dec("0.001"):
415
+ break
416
+ # same behavior, same subject and modifier(s) not set
417
+ if e[2] == behavior_to_stop[cfg.BEHAVIOR_CODE] and e[1] == self.currentSubject and e[3] == "":
418
+ mem_idx = idx
419
+ if mem_idx != -1:
420
+ self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][mem_idx][3] = behavior_to_stop_modifier_str
421
+
422
+ else:
423
+ behavior_to_stop_modifier_str = cm[cs]
424
+
322
425
  # add excluded state event to observations (= STOP them)
323
426
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in (cfg.LIVE):
324
427
  bisect.insort(
325
428
  self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS],
326
- [mem_time - dec("0.001"), self.currentSubject, cs, cm[cs], ""],
429
+ [mem_time - dec("0.001"), self.currentSubject, cs, behavior_to_stop_modifier_str, ""],
327
430
  )
328
431
 
329
432
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in (cfg.MEDIA):
330
433
  bisect.insort(
331
434
  self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS],
332
- [mem_time - dec("0.001"), self.currentSubject, cs, cm[cs], "", cfg.NA],
435
+ [mem_time - dec("0.001"), self.currentSubject, cs, behavior_to_stop_modifier_str, "", cfg.NA],
333
436
  )
334
437
 
335
438
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in (cfg.IMAGES):
336
439
  self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].append(
337
- [mem_time, self.currentSubject, cs, cm[cs], "", image_idx, image_path]
440
+ [mem_time, self.currentSubject, cs, behavior_to_stop_modifier_str, "", image_idx, image_path]
338
441
  )
339
442
 
340
443
  # order by image index ASC
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: boris-behav-obs
3
- Version: 8.25.4
3
+ Version: 8.26.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
@@ -697,9 +697,9 @@ Requires-Python: >=3.8
697
697
  Description-Content-Type: text/x-rst
698
698
  License-File: LICENSE.TXT
699
699
  Requires-Dist: exifread >=3.0.0
700
- Requires-Dist: numpy >=1.21
700
+ Requires-Dist: numpy >=1.26.4
701
701
  Requires-Dist: matplotlib >=3.3.3
702
- Requires-Dist: pandas >=1.3.5
702
+ Requires-Dist: pandas >=2.2.2
703
703
  Requires-Dist: tablib[cli,html,ods,pandas,xls,xlsx] >=3
704
704
  Requires-Dist: pyqt5 >=5.15
705
705
  Requires-Dist: pyreadr
@@ -1,36 +1,29 @@
1
- boris/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
2
- boris/README.TXT,sha256=0506tPRqGbRsnH_JxX1HJXW3TkEuAFmUG1W1ArBTBmk,788
3
1
  boris/__init__.py,sha256=vEqzYsbV0DwIWYPXIv3OXrBBFW71dpYRSL63Dedv4xE,773
4
2
  boris/__main__.py,sha256=_cKab_aODdiWYjyf6mWu57D-84izlVDPAm13QxH-cCg,764
5
3
  boris/about.py,sha256=JnsPLW8o9iYyp8I8S4Chhh3GmWNijxyyxRe9e9VFMFU,5253
6
- boris/add_modifier.py,sha256=VrXmsSBkPSB7rN-Awwl7_ny0UJdFWl5cHMuLqQSh7Qc,24974
7
- boris/add_modifier.ui,sha256=HiaX_TO0qzbEE0CsezuKKqJKwZ2e1c8R_J4D2KaKg5w,9389
8
- boris/add_modifier_ui.py,sha256=QovmV97MPoyX9Mb1OnIKxR6x5tXn2Z9FVi6qfwRwouw,10248
9
- boris/advanced_event_filtering.py,sha256=4d3lF-zn32IZW5cLUQ5bAZ3eDZjyuNBGBsSOYzHInLs,15184
10
- boris/behav_coding_map_creator.py,sha256=rWU9Wm4KDxYDFJtSOC0lUWTviV1eYzlkuJdQl6x2dR0,37858
4
+ boris/add_modifier.py,sha256=dtP1xZCwO0QlUJVir0JP2ShL5WrOvH26vQfwGwu683Q,26465
5
+ boris/add_modifier_ui.py,sha256=ZKXDcpavvzF5X8gkTuza1WYsw3x9TB-qQjrhn1fka5c,10537
6
+ boris/advanced_event_filtering.py,sha256=ScKwLy5zELU8R9WOS5wVZPk4OtWG1Cn4XeMxyHzG1Rc,15180
7
+ boris/behav_coding_map_creator.py,sha256=FNU5fUjvXQhTn-fmFQpksgIUzE0ZkAxgpPCXRpSGsAM,37860
11
8
  boris/behavior_binary_table.py,sha256=_XIJuly5xF5oSqjl8YIBMgJ_o3gtyviTJ5DJafvO-Mo,11677
12
- boris/behaviors_coding_map.py,sha256=MJuwr84Nl1unKr4dr3UY5mT-UorMac5nJagzz3GYTKY,7307
9
+ boris/behaviors_coding_map.py,sha256=Q_-k8DsYtXgUBzOYUdoGl41EgLdVAvGyuir98CUDY5Q,7309
13
10
  boris/boris_cli.py,sha256=CeUr_QKKySu9v-Gw5fkeQPjA3y_dGv7_Y4tCY41OeoA,13196
14
11
  boris/cmd_arguments.py,sha256=dNMEvMgTRMadC1Oz5IXw4JEBJw8Tq4yCo4yuRCnNfXc,1848
15
12
  boris/coding_pad.py,sha256=XVbg3Q7S4O6Rgxc5ZCzx4jv7eaDvbw_K2T_nQWtsvdU,10996
16
- boris/config.py,sha256=XXfLWPCRw04Uw6Scwz-bhq3NGuIk7GznPKW2h3KC7Mw,16859
17
- boris/config_file.py,sha256=Qy0qkOWip_VHA5uqVLWGDIxJPlxYsMQMdHrYnI_yGWs,13555
13
+ boris/config.py,sha256=zx5SfQESXGZjJkxXIdZKn2nGpj8nwRc-ZifcBUMNzWM,16726
14
+ boris/config_file.py,sha256=jTOhrX0tfxc3oHV7R85QX828xhezCsawXg6nvy5QIbE,13519
18
15
  boris/connections.py,sha256=RIQsaooiz6pzc2jJMHw9CQSyX39TgbM5JAwifCu5eWQ,19280
19
16
  boris/converters.py,sha256=FymqUxLEVLQwsUv6EVF7MRPowN1e87aVT2d3XjZJgAM,11771
20
- boris/converters.ui,sha256=hOcg0QkCMdnLf3a9DR3O2gZBl7EbX495VGndWnQVE4E,7400
21
17
  boris/converters_ui.py,sha256=firMWVgS3c492FF-YFv3FehOBLsbBHKaLEaVcoVctgc,8745
22
18
  boris/cooccurence.py,sha256=scazxKCmFQjBvv7oS9h7ylB8GJdkSmwy1LJZeUv5DOY,9824
23
- boris/core.py,sha256=7EPhbtCRM42Ijg6E5ubh2HxFLImWmY4s8b9U3sGD8ws,222059
24
- boris/core.qrc,sha256=9DibRJgfKpJ1i6xub-JQtMotLrSKhqRaF_oIkoJRqo8,2398
25
- boris/core.ui,sha256=z0unUmECYdGwULy8wX8cNscOlLYcRypWuVdhwM4FJsQ,46554
19
+ boris/core.py,sha256=y_7xvlLojM-Jdu3FlAwzWLQoiiGtHIC2Eo6Qm2SaHhk,222161
26
20
  boris/core_qrc.py,sha256=5YMpr7ckI4TbXaeSyCMhNJBAlrGPeLes62RedU3EbWs,865287
27
21
  boris/core_ui.py,sha256=Z_6Ehp93fRVnPGyKRtgX2GcXRXb8QZojXipHSpJ_f3M,69854
28
22
  boris/db_functions.py,sha256=YyI2LMSFn772aMGdkOIpfXbPuoSWEvDckZZKevwvB-4,13326
29
23
  boris/dev.py,sha256=9pUElbjl9g17rFUJXX5aVSu55_iIKIuDxNdrB0DI_d0,3671
30
- boris/dialog.py,sha256=oHnC4j5IF0KUDyVkoher9PvAp5wH2DQizWIp_EefQ3Q,41213
24
+ boris/dialog.py,sha256=9x9fSRUVhsxqcit_cNtnjnQMWuM6VB2c3EQ6-G3JDmo,41217
31
25
  boris/duration_widget.py,sha256=FHnDDg6RBtORCZDG9gkpjxP9ivZ3Rb4uYWCdEkBl-a4,6930
32
26
  boris/edit_event.py,sha256=cvZ4j2rcck8q5Rd6n5d6JGQ0wZMduVQls2UDylCuQqY,7578
33
- boris/edit_event.ui,sha256=oC2AZH9rZm8f4HWvsOGxXVgAr6SUx2Wub-B44R2LDqM,5969
34
27
  boris/edit_event_ui.py,sha256=X98eKSS0urd0eGce-q2_gkPx79GvO1XbJUEjxGCMUCI,6984
35
28
  boris/event_operations.py,sha256=JYIZC4ObShZ-SPxNQaQZwKs8xJn4Dj0lvIiNgzc28Jo,38929
36
29
  boris/events_cursor.py,sha256=Jfu7yV1MG7HikNmMQmOVJ7ntmEZzDZn5HHp3va8llwM,1983
@@ -39,27 +32,25 @@ boris/exclusion_matrix.py,sha256=xaEFLy2Zv5SWryMz1D0cEpywsAuZxZuBaaGYLaBGhng,527
39
32
  boris/export_events.py,sha256=35tu9K9kEXlossdWjTNs74YuIs-ZQBSzw7MRnRIUK3Q,37826
40
33
  boris/export_observation.py,sha256=TC_Mxyor5e0HZViG9xxAX_V18i61ihFnVmhioNOy_Dw,49821
41
34
  boris/external_processes.py,sha256=069iEVOfDI-7zUYPH7WlYBEnKqTR18mjj8dReBkUACE,11965
42
- boris/geometric_measurement.py,sha256=VZCUM9puq57HPP5q0U7Dt2ZiOlRb5j7Fduu3oeVuggA,34607
35
+ boris/geometric_measurement.py,sha256=jd6HiWJNrjqRInG452a8Ke44Zj4QyNSYa9JKO8DZzOo,34609
43
36
  boris/gui_utilities.py,sha256=IhIz53CHSRvO-qjN5XrzpgAChf_LBcUWlE3iFMBeROw,3774
44
37
  boris/image_overlay.py,sha256=bxEHEr5p6nH_sgXRSGEWlJZJF7R07ItgHNM4_3kXTJA,2527
45
38
  boris/import_observations.py,sha256=FrXG_oOyPYWIX1XHHWvC9eg13FHsZPzhLGjCMSQFHT8,8838
46
39
  boris/irr.py,sha256=oFp8rJG9XxmKZ1q5fPnp79LEwFYHnu5i9Gk8CqRKiNM,22474
47
40
  boris/latency.py,sha256=EDgH2DhR3j3lExMLemwhh7SIWFCWX9kJ4YbVXhhYv-8,9737
48
41
  boris/map_creator.py,sha256=IJzvT2a9LoWk_Qx-Enc4jTsYh0ixpbrLNUFCLwB9idA,29437
49
- boris/measurement_widget.py,sha256=Og73L_ut0IuG89rydGNRta4PKjq1BPkk-D6hCrFrPpg,4484
42
+ boris/measurement_widget.py,sha256=NI2d9qftFTtnUzy-aOtKhx3w63-mqDIJ4rz1l_8_Zos,4486
50
43
  boris/media_file.py,sha256=ofOoGyrqEcm6HZQxnyObdmcLlKS0TrcPQNPNf8EG1BM,4766
51
44
  boris/menu_options.py,sha256=izzYx-Rc7powf8PqhAXD3XMPYvBoVZqOYbS1jaW4sII,7094
52
45
  boris/modifiers_coding_map.py,sha256=XBTubBHw6Sayw5jUNaWR_jVIw6ttaJcbhbmUSF6_lx4,4566
53
46
  boris/mpv.py,sha256=EfzIHjPbgewG4w3smEtqEUPZoVwYmMQkL4Q8ZyW-a58,76410
54
47
  boris/mpv2.py,sha256=EXRtzQqFjOn4wMC6482Ilq3fNQ9N1GRP1VxwLzdeaBY,88077
55
- boris/observation.py,sha256=9CmVvZiXQsRVD2W_MQHm40ZMqRzjYaVx_NIpnqYufsg,52830
56
- boris/observation.ui,sha256=ZOng-yYtPJxLoUs_5Po35b5G9myTw1CTtD9Gqo_UhMI,27051
48
+ boris/observation.py,sha256=pHP68WnjA1rZsefo6zNtmwaXcjwjpItZCQrefqwOCCQ,52832
57
49
  boris/observation_operations.py,sha256=tMKrL7sM-jcM4__Hpg7mPTfLkjxytXMBdKe2CKFGe5k,100217
58
50
  boris/observation_ui.py,sha256=dfk7pS2DlaiEgMjouH0-_t6kPgMwovQbloHtc3ZoCmY,30278
59
51
  boris/observations_list.py,sha256=rMuEVa3QccA89vjARcEVtSlLnr5s0sfihUlcwciV3OM,10600
60
- boris/otx_parser.py,sha256=-Sec_NzVUhwey0oscA1ObSE1rGh-CxwTZaj0jhGzPdM,16377
52
+ boris/otx_parser.py,sha256=Ul5GokK46azx60P6-pk8wbSPUqUR9O4WLV42z3rE40s,16367
61
53
  boris/param_panel.py,sha256=XpFuoNtVuPx_3EieYt0JaQ2J3Bk4ZyMa4RgJha_10oE,7473
62
- boris/param_panel.ui,sha256=RHcf_MvWO4qPC3pB_L4ow4l_qTX21yRfrZvePhCSLsE,11013
63
54
  boris/param_panel_ui.py,sha256=R6Yfxd7m41_Mo5XCD9wlyAprrEKdCyBZm0jicwh_cGk,11704
64
55
  boris/player_dock_widget.py,sha256=Pl2GB-a2XmiKiPPsun9_Q8otXXrugV1tdCEW3YMP_VE,5642
65
56
  boris/plot_data_module.py,sha256=OazYAPHker30s1oYYKkSa0IXSj-DbB1-Qgcau0x-Il8,16582
@@ -67,33 +58,30 @@ boris/plot_events.py,sha256=HAKrdS3qf0A_PG1pGo0FRyk62femDxfxGss3w8CrlUw,23541
67
58
  boris/plot_events_rt.py,sha256=UfO1NKSqBR7j-vnfG8CKuOcdg5o8QQI_0eYdcz29Ufc,8358
68
59
  boris/plot_spectrogram_rt.py,sha256=yc8ifg9HCl-v_upMHkH_rmIZLhWBYkWBOhtKftvRvh0,8394
69
60
  boris/plot_waveform_rt.py,sha256=kNk6NVjM3wT3MpAw5zE9ktH94Pkd2AJ4ppv6uM6BVBc,7502
70
- boris/preferences.py,sha256=FiRiDpQXIRw2Ni1vjz3sPSZ2jv7971uv0JW5daQysYU,11622
71
- boris/preferences.ui,sha256=xaetyovsXq9G5YEg0ZwgFamcX8Px1XDu75xt_wEeFIA,19198
61
+ boris/preferences.py,sha256=5KxUwP10mnsAk8CH3eSlzvd8mtsHsFJfWhzot3QOy3Y,11540
72
62
  boris/preferences_ui.py,sha256=LQ46jW9kltJBQD85EHd5cCmlHG4c9N6v6YQLQvUisGM,21935
73
- boris/project.py,sha256=8eiwud6N4slLlj1GAb71xvy5vs2-etOrfK0RM1qUQac,81465
74
- boris/project.ui,sha256=ZhUnCYABSAKc_kh5PLjzIFznfY7n4mGHxMXhQH8tmJM,36021
63
+ boris/project.py,sha256=i9XT744HoJhlV93C0QuEMD8tNmzIHL0E-iZ4PIOSadc,82219
75
64
  boris/project_functions.py,sha256=R3DS_faDuMGKTpMnV0OhqgZp9yO1ojoA5wUIXdtHNIE,70972
76
65
  boris/project_import_export.py,sha256=lCL4fOZuG5MaEf_wfDSHiE7-M2ZQsIbUR7KpHZjb1Uw,38097
77
66
  boris/project_ui.py,sha256=TRJlsqq4XbIl8k2f20BKr2PtKa0QNjFNr6UnMieZYqU,35915
78
67
  boris/qrc_boris.py,sha256=4U2cD_fIBTtsTPhirOl9I3kTArOhXWYvfuO_A0M9N0I,675001
79
68
  boris/qrc_boris5.py,sha256=6bwT8OVtIr84nVIYp2ldUL7XL6WJi9zgJow6kgRxuIQ,161601
80
- boris/select_modifiers.py,sha256=jbzXKTuAuWnIVsykAuzlQmTqkAcmQsJl7c3cJiPbw_w,12051
69
+ boris/select_modifiers.py,sha256=TAo7xi5bhiMp9WOAwEPq9xc8i4Nsa2QrcpoCrUgPwhE,13178
81
70
  boris/select_observations.py,sha256=snMC23nYD_qi88q95rXqV-NqbGO5BrLrC-CKlmVR3ok,14441
82
71
  boris/select_subj_behav.py,sha256=bX-YCeysK5QowYgoxQyy-9qeZmhzWM4SwArXxSAa5ho,10342
83
72
  boris/state_events.py,sha256=AcZmD0pIJ4AszTQe9conWlh8gJhwdBjZiGm1KOXt804,7768
84
73
  boris/subjects_pad.py,sha256=d-vI0qBcXfonN_BvYI19ZBWgY0Ywekc2DSWLFHIjz0o,3553
85
74
  boris/synthetic_time_budget.py,sha256=m3QJ0Ju4av7WzMUKFIvxJd5x4iNVTXc4-j9tWa3MIOE,9858
86
75
  boris/time_budget_functions.py,sha256=L-0PuPWYR33UMfqmxpcCVH-w4mLLrtZ8b8kBTmBwlsI,50299
87
- boris/time_budget_widget.py,sha256=nz7fmdfAQsI8Myyq7rvjnAiuvMKcYP59t7sNQPXjT4g,42850
76
+ boris/time_budget_widget.py,sha256=9WV-iKYSl1f3HBrGcL-eHDjUswrFQh6-WUcffTtce1Y,42852
88
77
  boris/transitions.py,sha256=2zucdoa2jTpWtM6nWHr8lOvjXkrQmo9j71fz_fMLALU,11998
89
- boris/utilities.py,sha256=V27JgcKTWajCevmUBFc_tOfZPjklwXW2HiMHE0sup2Y,48431
90
- boris/version.py,sha256=M1QIV9i2Gd5MEabuH2QBD8Zdc1JDK2NrnHeDmqE4jWM,788
78
+ boris/utilities.py,sha256=07qXAQ6aGyiSC2h6RMJ031OlXYy05-KTLJtnQtXn6FU,48724
79
+ boris/version.py,sha256=p1Eo1rAKt4WoBUXddzmCG41ZVzlVjIetATyhD0XQVaI,788
91
80
  boris/video_equalizer.py,sha256=QpVgmdqX_E4HnMa2f_Qo_fKJTl9nBoTQd_ykv9RWlIQ,5862
92
81
  boris/video_equalizer_ui.py,sha256=A2_Sz9AAVnJygTRUeK_YXxf-WWQpxSSlFw0MjkxiwSg,9762
93
82
  boris/video_operations.py,sha256=96jR-3snNn9VeEURRD6rCwvOL2sSHXoqlP_gYFwgO8A,9379
94
83
  boris/vlc_local.py,sha256=M4yQ8pGW-sY9zMJXDlWg5fzfcwLep1pFQjqwyREHlKo,3053
95
- boris/write_event.py,sha256=ckNx1JqElvUli1s-W0Xk3NSziDRxhoV9_d2IDHe9J9M,18356
96
- boris/icons/logo_eye.ico,sha256=pGgNoNXijGOuv6c_iaFR9OsjJ1GBqvrtzxNtfCLv_sc,45470
84
+ boris/write_event.py,sha256=Lsv7Jxoi4b4qVqjsxwiB2yago_TQ9BtwjjG7OejQEhI,23528
97
85
  boris/portion/__init__.py,sha256=ZBUG4I7YWhRkeWdP-JEpxhxldJlUYQkeaJseTjdhtJE,602
98
86
  boris/portion/const.py,sha256=hEp26BKcEg1Js4DfZsBHmDtJJts83Tl1HWQ0CNJNwEc,1588
99
87
  boris/portion/dict.py,sha256=SyHxc7PfDw2ufNLFQycwJtzmRfL48rDp4UrM2KN7IWc,11282
@@ -117,9 +105,9 @@ boris/qdarkstyle/utils/__init__.py,sha256=Nlma8-zbHoJc5n2NVT7OvwxPG5765JnsmMeGzr
117
105
  boris/qdarkstyle/utils/__main__.py,sha256=J1biUyDzfutKU1n9NdH9WnD0gFHaF-OJA4Q-n6Q2ehs,3309
118
106
  boris/qdarkstyle/utils/images.py,sha256=af-BJllzWgVoVz6QMvhFcKqvF3mo44AThaBjuAuHtNE,14444
119
107
  boris/qdarkstyle/utils/scss.py,sha256=n7WNo6pPRft8-dU7_gfjB_jA-JZAy50-S792RwR7Ri0,9366
120
- boris_behav_obs-8.25.4.dist-info/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
121
- boris_behav_obs-8.25.4.dist-info/METADATA,sha256=6kjRUo8YmiJnz60lU7w-mwKUuPpQgAvotCigGh3ljlY,45541
122
- boris_behav_obs-8.25.4.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
123
- boris_behav_obs-8.25.4.dist-info/entry_points.txt,sha256=fuO7JxKFLOm6xp6m3JHRA1UO_QW1dYU-F0IooA1NqQs,37
124
- boris_behav_obs-8.25.4.dist-info/top_level.txt,sha256=fJSgm62S7WesiwTorGbOO4nNN0yzgZ3klgfGi3Px4qI,6
125
- boris_behav_obs-8.25.4.dist-info/RECORD,,
108
+ boris_behav_obs-8.26.1.dist-info/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
109
+ boris_behav_obs-8.26.1.dist-info/METADATA,sha256=38_OR718pDduPAAmZi8ixtl_iH1zyPE4T38USEQALBY,45543
110
+ boris_behav_obs-8.26.1.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
111
+ boris_behav_obs-8.26.1.dist-info/entry_points.txt,sha256=fuO7JxKFLOm6xp6m3JHRA1UO_QW1dYU-F0IooA1NqQs,37
112
+ boris_behav_obs-8.26.1.dist-info/top_level.txt,sha256=fJSgm62S7WesiwTorGbOO4nNN0yzgZ3klgfGi3Px4qI,6
113
+ boris_behav_obs-8.26.1.dist-info/RECORD,,