boris-behav-obs 9.6.1__py2.py3-none-any.whl → 9.6.3__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- boris/add_modifier.py +1 -5
- boris/analysis_plugins/irr_cohen_kappa.py +72 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +77 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +120 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +125 -0
- boris/analysis_plugins/time_budget.py +0 -4
- boris/behav_coding_map_creator.py +0 -1
- boris/boris_cli.py +1 -1
- boris/coding_pad.py +0 -2
- boris/core.py +11 -28
- boris/core_qrc.py +3 -0
- boris/db_functions.py +4 -4
- boris/edit_event.py +0 -9
- boris/exclusion_matrix.py +1 -1
- boris/export_events.py +63 -71
- boris/gui_utilities.py +0 -1
- boris/irr.py +10 -22
- boris/menu_options.py +2 -0
- boris/modifier_coding_map_creator.py +0 -2
- boris/observation_operations.py +2 -4
- boris/param_panel.py +0 -4
- boris/plot_spectrogram_rt.py +2 -1
- boris/plot_waveform_rt.py +2 -1
- boris/portion/__init__.py +18 -8
- boris/portion/const.py +35 -18
- boris/portion/dict.py +5 -5
- boris/portion/func.py +2 -2
- boris/portion/interval.py +21 -41
- boris/portion/io.py +41 -32
- boris/project_functions.py +2 -1
- boris/state_events.py +1 -1
- boris/time_budget_functions.py +0 -9
- boris/transitions.py +1 -1
- boris/version.py +2 -2
- boris/video_equalizer.py +0 -2
- boris/view_df.py +0 -2
- boris/write_event.py +4 -13
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/METADATA +12 -8
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/RECORD +43 -39
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/WHEEL +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.6.1.dist-info → boris_behav_obs-9.6.3.dist-info}/top_level.txt +0 -0
boris/portion/interval.py
CHANGED
|
@@ -2,7 +2,7 @@ from collections import namedtuple
|
|
|
2
2
|
from .const import Bound, inf
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
Atomic = namedtuple(
|
|
5
|
+
Atomic = namedtuple("Atomic", ["left", "lower", "upper", "right"])
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def mergeable(a, b):
|
|
@@ -96,7 +96,7 @@ class Interval:
|
|
|
96
96
|
one of the helpers provided in this module (open, closed, openclosed, etc.)
|
|
97
97
|
"""
|
|
98
98
|
|
|
99
|
-
__slots__ = (
|
|
99
|
+
__slots__ = ("_intervals",)
|
|
100
100
|
|
|
101
101
|
def __init__(self, *intervals):
|
|
102
102
|
"""
|
|
@@ -111,7 +111,7 @@ class Interval:
|
|
|
111
111
|
if not interval.empty:
|
|
112
112
|
self._intervals.extend(interval._intervals)
|
|
113
113
|
else:
|
|
114
|
-
raise TypeError(
|
|
114
|
+
raise TypeError("Parameters must be Interval instances")
|
|
115
115
|
|
|
116
116
|
if len(self._intervals) == 0:
|
|
117
117
|
# So we have at least one (empty) interval
|
|
@@ -181,10 +181,7 @@ class Interval:
|
|
|
181
181
|
"""
|
|
182
182
|
True if interval is empty, False otherwise.
|
|
183
183
|
"""
|
|
184
|
-
return (
|
|
185
|
-
self.lower > self.upper or
|
|
186
|
-
(self.lower == self.upper and (self.left == Bound.OPEN or self.right == Bound.OPEN))
|
|
187
|
-
)
|
|
184
|
+
return self.lower > self.upper or (self.lower == self.upper and (self.left == Bound.OPEN or self.right == Bound.OPEN))
|
|
188
185
|
|
|
189
186
|
@property
|
|
190
187
|
def atomic(self):
|
|
@@ -307,7 +304,7 @@ class Interval:
|
|
|
307
304
|
elif isinstance(value, tuple):
|
|
308
305
|
intervals.append(Interval.from_atomic(*value))
|
|
309
306
|
else:
|
|
310
|
-
raise TypeError(
|
|
307
|
+
raise TypeError("Unsupported return type {} for {}".format(type(value), value))
|
|
311
308
|
|
|
312
309
|
return Interval(*intervals)
|
|
313
310
|
|
|
@@ -350,7 +347,7 @@ class Interval:
|
|
|
350
347
|
return True
|
|
351
348
|
return False
|
|
352
349
|
else:
|
|
353
|
-
raise TypeError(
|
|
350
|
+
raise TypeError("Unsupported type {} for {}".format(type(other), other))
|
|
354
351
|
|
|
355
352
|
def intersection(self, other):
|
|
356
353
|
"""
|
|
@@ -468,14 +465,8 @@ class Interval:
|
|
|
468
465
|
if item.empty:
|
|
469
466
|
return True
|
|
470
467
|
elif self.atomic:
|
|
471
|
-
left = item.lower > self.lower or (
|
|
472
|
-
|
|
473
|
-
(item.left == self.left or self.left == Bound.CLOSED)
|
|
474
|
-
)
|
|
475
|
-
right = item.upper < self.upper or (
|
|
476
|
-
item.upper == self.upper and
|
|
477
|
-
(item.right == self.right or self.right == Bound.CLOSED)
|
|
478
|
-
)
|
|
468
|
+
left = item.lower > self.lower or (item.lower == self.lower and (item.left == self.left or self.left == Bound.CLOSED))
|
|
469
|
+
right = item.upper < self.upper or (item.upper == self.upper and (item.right == self.right or self.right == Bound.CLOSED))
|
|
479
470
|
return left and right
|
|
480
471
|
else:
|
|
481
472
|
selfiter = iter(self)
|
|
@@ -504,13 +495,11 @@ class Interval:
|
|
|
504
495
|
def __invert__(self):
|
|
505
496
|
complements = [
|
|
506
497
|
Interval.from_atomic(Bound.OPEN, -inf, self.lower, ~self.left),
|
|
507
|
-
Interval.from_atomic(~self.right, self.upper, inf, Bound.OPEN)
|
|
498
|
+
Interval.from_atomic(~self.right, self.upper, inf, Bound.OPEN),
|
|
508
499
|
]
|
|
509
500
|
|
|
510
501
|
for i, j in zip(self._intervals[:-1], self._intervals[1:]):
|
|
511
|
-
complements.append(
|
|
512
|
-
Interval.from_atomic(~i.right, i.upper, j.lower, ~j.left)
|
|
513
|
-
)
|
|
502
|
+
complements.append(Interval.from_atomic(~i.right, i.upper, j.lower, ~j.left))
|
|
514
503
|
|
|
515
504
|
return Interval(*complements)
|
|
516
505
|
|
|
@@ -526,12 +515,7 @@ class Interval:
|
|
|
526
515
|
return False
|
|
527
516
|
|
|
528
517
|
for a, b in zip(self._intervals, other._intervals):
|
|
529
|
-
eq =
|
|
530
|
-
a.left == b.left and
|
|
531
|
-
a.lower == b.lower and
|
|
532
|
-
a.upper == b.upper and
|
|
533
|
-
a.right == b.right
|
|
534
|
-
)
|
|
518
|
+
eq = a.left == b.left and a.lower == b.lower and a.upper == b.upper and a.right == b.right
|
|
535
519
|
if not eq:
|
|
536
520
|
return False
|
|
537
521
|
return True
|
|
@@ -543,8 +527,7 @@ class Interval:
|
|
|
543
527
|
if self.right == Bound.OPEN:
|
|
544
528
|
return self.upper <= other.lower
|
|
545
529
|
else:
|
|
546
|
-
return self.upper < other.lower or
|
|
547
|
-
(self.upper == other.lower and other.left == Bound.OPEN)
|
|
530
|
+
return self.upper < other.lower or (self.upper == other.lower and other.left == Bound.OPEN)
|
|
548
531
|
else:
|
|
549
532
|
return self.upper < other or (self.right == Bound.OPEN and self.upper == other)
|
|
550
533
|
|
|
@@ -553,8 +536,7 @@ class Interval:
|
|
|
553
536
|
if self.left == Bound.OPEN:
|
|
554
537
|
return self.lower >= other.upper
|
|
555
538
|
else:
|
|
556
|
-
return self.lower > other.upper or
|
|
557
|
-
(self.lower == other.upper and other.right == Bound.OPEN)
|
|
539
|
+
return self.lower > other.upper or (self.lower == other.upper and other.right == Bound.OPEN)
|
|
558
540
|
else:
|
|
559
541
|
return self.lower > other or (self.left == Bound.OPEN and self.lower == other)
|
|
560
542
|
|
|
@@ -563,8 +545,7 @@ class Interval:
|
|
|
563
545
|
if self.right == Bound.OPEN:
|
|
564
546
|
return self.upper <= other.upper
|
|
565
547
|
else:
|
|
566
|
-
return self.upper < other.upper or
|
|
567
|
-
(self.upper == other.upper and other.right == Bound.CLOSED)
|
|
548
|
+
return self.upper < other.upper or (self.upper == other.upper and other.right == Bound.CLOSED)
|
|
568
549
|
else:
|
|
569
550
|
return self.lower < other or (self.left == Bound.CLOSED and self.lower == other)
|
|
570
551
|
|
|
@@ -573,8 +554,7 @@ class Interval:
|
|
|
573
554
|
if self.left == Bound.OPEN:
|
|
574
555
|
return self.lower >= other.lower
|
|
575
556
|
else:
|
|
576
|
-
return self.lower > other.lower or
|
|
577
|
-
(self.lower == other.lower and other.left == Bound.CLOSED)
|
|
557
|
+
return self.lower > other.lower or (self.lower == other.lower and other.left == Bound.CLOSED)
|
|
578
558
|
else:
|
|
579
559
|
return self.upper > other or (self.right == Bound.CLOSED and self.upper == other)
|
|
580
560
|
|
|
@@ -586,16 +566,16 @@ class Interval:
|
|
|
586
566
|
|
|
587
567
|
for interval in self:
|
|
588
568
|
if interval.empty:
|
|
589
|
-
intervals.append(
|
|
569
|
+
intervals.append("()")
|
|
590
570
|
elif interval.lower == interval.upper:
|
|
591
|
-
intervals.append(
|
|
571
|
+
intervals.append("[{}]".format(repr(interval.lower)))
|
|
592
572
|
else:
|
|
593
573
|
intervals.append(
|
|
594
|
-
|
|
595
|
-
|
|
574
|
+
"{}{},{}{}".format(
|
|
575
|
+
"[" if interval.left == Bound.CLOSED else "(",
|
|
596
576
|
repr(interval.lower),
|
|
597
577
|
repr(interval.upper),
|
|
598
|
-
|
|
578
|
+
"]" if interval.right == Bound.CLOSED else ")",
|
|
599
579
|
)
|
|
600
580
|
)
|
|
601
|
-
return
|
|
581
|
+
return " | ".join(intervals)
|
boris/portion/io.py
CHANGED
|
@@ -4,9 +4,20 @@ from .const import Bound, inf
|
|
|
4
4
|
from .interval import Interval
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def from_string(
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
def from_string(
|
|
8
|
+
string,
|
|
9
|
+
conv,
|
|
10
|
+
*,
|
|
11
|
+
bound=r".+?",
|
|
12
|
+
disj=r" ?\| ?",
|
|
13
|
+
sep=r", ?",
|
|
14
|
+
left_open=r"\(",
|
|
15
|
+
left_closed=r"\[",
|
|
16
|
+
right_open=r"\)",
|
|
17
|
+
right_closed=r"\]",
|
|
18
|
+
pinf=r"\+inf",
|
|
19
|
+
ninf=r"-inf",
|
|
20
|
+
):
|
|
10
21
|
"""
|
|
11
22
|
Parse given string and create an Interval instance.
|
|
12
23
|
A converter function has to be provided to convert a bound (as string) to a value.
|
|
@@ -25,11 +36,11 @@ def from_string(string, conv, *, bound=r'.+?', disj=r' ?\| ?', sep=r', ?',
|
|
|
25
36
|
:return: an Interval instance.
|
|
26
37
|
"""
|
|
27
38
|
|
|
28
|
-
re_left_boundary = r
|
|
29
|
-
re_right_boundary = r
|
|
30
|
-
re_bounds = r
|
|
31
|
-
re_interval = r
|
|
32
|
-
re_intervals = r
|
|
39
|
+
re_left_boundary = r"(?P<left>{}|{})".format(left_open, left_closed)
|
|
40
|
+
re_right_boundary = r"(?P<right>{}|{})".format(right_open, right_closed)
|
|
41
|
+
re_bounds = r"(?P<lower>{bound})({sep}(?P<upper>{bound}))?".format(bound=bound, sep=sep)
|
|
42
|
+
re_interval = r"{}(|{}){}".format(re_left_boundary, re_bounds, re_right_boundary)
|
|
43
|
+
re_intervals = r"{}(?P<disj>{})?".format(re_interval, disj)
|
|
33
44
|
|
|
34
45
|
intervals = []
|
|
35
46
|
has_more = True
|
|
@@ -49,22 +60,23 @@ def from_string(string, conv, *, bound=r'.+?', disj=r' ?\| ?', sep=r', ?',
|
|
|
49
60
|
else:
|
|
50
61
|
group = match.groupdict()
|
|
51
62
|
|
|
52
|
-
left = Bound.CLOSED if re.match(left_closed +
|
|
53
|
-
right = Bound.CLOSED if re.match(right_closed +
|
|
63
|
+
left = Bound.CLOSED if re.match(left_closed + "$", group["left"]) else Bound.OPEN
|
|
64
|
+
right = Bound.CLOSED if re.match(right_closed + "$", group["right"]) else Bound.OPEN
|
|
54
65
|
|
|
55
|
-
lower = group.get(
|
|
56
|
-
upper = group.get(
|
|
66
|
+
lower = group.get("lower", None)
|
|
67
|
+
upper = group.get("upper", None)
|
|
57
68
|
lower = _convert(lower) if lower is not None else inf
|
|
58
69
|
upper = _convert(upper) if upper is not None else lower
|
|
59
70
|
|
|
60
71
|
intervals.append(Interval.from_atomic(left, lower, upper, right))
|
|
61
|
-
string = string[match.end():]
|
|
72
|
+
string = string[match.end() :]
|
|
62
73
|
|
|
63
74
|
return Interval(*intervals)
|
|
64
75
|
|
|
65
76
|
|
|
66
|
-
def to_string(
|
|
67
|
-
|
|
77
|
+
def to_string(
|
|
78
|
+
interval, conv=repr, *, disj=" | ", sep=",", left_open="(", left_closed="[", right_open=")", right_closed="]", pinf="+inf", ninf="-inf"
|
|
79
|
+
):
|
|
68
80
|
"""
|
|
69
81
|
Export given interval to string.
|
|
70
82
|
|
|
@@ -81,7 +93,7 @@ def to_string(interval, conv=repr, *, disj=' | ', sep=',', left_open='(',
|
|
|
81
93
|
:return: a string representation for given interval.
|
|
82
94
|
"""
|
|
83
95
|
if interval.empty:
|
|
84
|
-
return
|
|
96
|
+
return "{}{}".format(left_open, right_open)
|
|
85
97
|
|
|
86
98
|
def _convert(bound):
|
|
87
99
|
if bound == inf:
|
|
@@ -100,14 +112,14 @@ def to_string(interval, conv=repr, *, disj=' | ', sep=',', left_open='(',
|
|
|
100
112
|
upper = _convert(item.upper)
|
|
101
113
|
|
|
102
114
|
if item.lower == item.upper:
|
|
103
|
-
exported_intervals.append(
|
|
115
|
+
exported_intervals.append("{}{}{}".format(left, lower, right))
|
|
104
116
|
else:
|
|
105
|
-
exported_intervals.append(
|
|
117
|
+
exported_intervals.append("{}{}{}{}{}".format(left, lower, sep, upper, right))
|
|
106
118
|
|
|
107
119
|
return disj.join(exported_intervals)
|
|
108
120
|
|
|
109
121
|
|
|
110
|
-
def from_data(data, conv=None, *, pinf=float(
|
|
122
|
+
def from_data(data, conv=None, *, pinf=float("inf"), ninf=float("-inf")):
|
|
111
123
|
"""
|
|
112
124
|
Import an interval from a piece of data.
|
|
113
125
|
|
|
@@ -130,16 +142,18 @@ def from_data(data, conv=None, *, pinf=float('inf'), ninf=float('-inf')):
|
|
|
130
142
|
|
|
131
143
|
for item in data:
|
|
132
144
|
left, lower, upper, right = item
|
|
133
|
-
intervals.append(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
intervals.append(
|
|
146
|
+
Interval.from_atomic(
|
|
147
|
+
Bound(left),
|
|
148
|
+
_convert(lower),
|
|
149
|
+
_convert(upper),
|
|
150
|
+
Bound(right),
|
|
151
|
+
)
|
|
152
|
+
)
|
|
139
153
|
return Interval(*intervals)
|
|
140
154
|
|
|
141
155
|
|
|
142
|
-
def to_data(interval, conv=None, *, pinf=float(
|
|
156
|
+
def to_data(interval, conv=None, *, pinf=float("inf"), ninf=float("-inf")):
|
|
143
157
|
"""
|
|
144
158
|
Export given interval to a list of 4-uples (left, lower,
|
|
145
159
|
upper, right).
|
|
@@ -163,10 +177,5 @@ def to_data(interval, conv=None, *, pinf=float('inf'), ninf=float('-inf')):
|
|
|
163
177
|
return conv(bound)
|
|
164
178
|
|
|
165
179
|
for item in interval:
|
|
166
|
-
data.append((
|
|
167
|
-
item.left.value,
|
|
168
|
-
_convert(item.lower),
|
|
169
|
-
_convert(item.upper),
|
|
170
|
-
item.right.value
|
|
171
|
-
))
|
|
180
|
+
data.append((item.left.value, _convert(item.lower), _convert(item.upper), item.right.value))
|
|
172
181
|
return data
|
boris/project_functions.py
CHANGED
|
@@ -1539,7 +1539,8 @@ def open_project_json(project_file_name: str) -> tuple:
|
|
|
1539
1539
|
# sort events by time asc
|
|
1540
1540
|
for obs_id in pj[cfg.OBSERVATIONS]:
|
|
1541
1541
|
if pj[cfg.OBSERVATIONS][obs_id][cfg.TYPE] in (cfg.LIVE, cfg.MEDIA):
|
|
1542
|
-
|
|
1542
|
+
# sort events list using the first 3 items (time, subject, behavior)
|
|
1543
|
+
pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
1543
1544
|
|
|
1544
1545
|
return project_file_name, projectChanged, pj, msg
|
|
1545
1546
|
|
boris/state_events.py
CHANGED
|
@@ -136,7 +136,7 @@ def fix_unpaired_events(self, silent_mode: bool = False):
|
|
|
136
136
|
|
|
137
137
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].extend(events_to_add)
|
|
138
138
|
self.project_changed()
|
|
139
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort()
|
|
139
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
140
140
|
self.load_tw_events(self.observationId)
|
|
141
141
|
|
|
142
142
|
index = self.tv_events.model().index(
|
boris/time_budget_functions.py
CHANGED
|
@@ -1021,15 +1021,6 @@ def time_budget_analysis(
|
|
|
1021
1021
|
continue
|
|
1022
1022
|
|
|
1023
1023
|
if len(rows) % 2: # unpaired events
|
|
1024
|
-
"""
|
|
1025
|
-
print()
|
|
1026
|
-
print(f"{subject=}")
|
|
1027
|
-
print(f"{behavior=}")
|
|
1028
|
-
print()
|
|
1029
|
-
for row in rows:
|
|
1030
|
-
print(f"{row['observation']=} {row['occurence']=}")
|
|
1031
|
-
print()
|
|
1032
|
-
"""
|
|
1033
1024
|
out_cat.append(
|
|
1034
1025
|
{
|
|
1035
1026
|
"subject": subject,
|
boris/transitions.py
CHANGED
|
@@ -354,7 +354,7 @@ def transitions_flow_diagram():
|
|
|
354
354
|
with open(tempfile.gettempdir() + os.sep + os.path.basename(file_name) + ".tmp.gv", "w") as f:
|
|
355
355
|
f.write(gv)
|
|
356
356
|
result = subprocess.getoutput(
|
|
357
|
-
(f'dot -Tpng -o "{file_name}.png"
|
|
357
|
+
(f'dot -Tpng -o "{file_name}.png" "{tempfile.gettempdir() + os.sep + os.path.basename(file_name)}.tmp.gv"')
|
|
358
358
|
)
|
|
359
359
|
if not result:
|
|
360
360
|
out += f"<b>{file_name}.png</b> created<br>"
|
boris/version.py
CHANGED
boris/video_equalizer.py
CHANGED
|
@@ -60,8 +60,6 @@ class Video_equalizer(QDialog, Ui_Equalizer):
|
|
|
60
60
|
if n_player not in self.equalizer:
|
|
61
61
|
return
|
|
62
62
|
|
|
63
|
-
print(self.equalizer)
|
|
64
|
-
|
|
65
63
|
self.hs_brightness.setValue(self.equalizer[n_player]["hs_brightness"])
|
|
66
64
|
self.lb_brightness.setText(str(self.equalizer[n_player]["hs_brightness"]))
|
|
67
65
|
|
boris/view_df.py
CHANGED
|
@@ -49,8 +49,6 @@ class View_df(QWidget, Ui_Form):
|
|
|
49
49
|
self.lb_plugin_info.setText(f"{plugin_name} v. {plugin_version}")
|
|
50
50
|
self.setWindowTitle(f"{plugin_name} v. {plugin_version}")
|
|
51
51
|
|
|
52
|
-
# print(f"{self.df=}")
|
|
53
|
-
|
|
54
52
|
self.pb_close.clicked.connect(self.close)
|
|
55
53
|
self.pb_save.clicked.connect(self.save)
|
|
56
54
|
|
boris/write_event.py
CHANGED
|
@@ -68,8 +68,6 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
68
68
|
)
|
|
69
69
|
return 1
|
|
70
70
|
|
|
71
|
-
print(f"{mem_time=}")
|
|
72
|
-
|
|
73
71
|
if mem_time < self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])[0]:
|
|
74
72
|
_ = dialog.MessageDialog(
|
|
75
73
|
cfg.programName,
|
|
@@ -104,12 +102,6 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
104
102
|
|
|
105
103
|
# add media creation date/time
|
|
106
104
|
|
|
107
|
-
"""
|
|
108
|
-
print(f"{media_file_name=}")
|
|
109
|
-
print(f"{mem_time=}")
|
|
110
|
-
"""
|
|
111
|
-
print(f"{self.pj[cfg.OBSERVATIONS][self.observationId][cfg.MEDIA_INFO]=}")
|
|
112
|
-
|
|
113
105
|
mem_time += dec(
|
|
114
106
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.MEDIA_INFO][cfg.MEDIA_CREATION_TIME][media_file_name_posix]
|
|
115
107
|
)
|
|
@@ -413,7 +405,6 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
413
405
|
r = modifiers_selector.exec_()
|
|
414
406
|
if r:
|
|
415
407
|
selected_modifiers = modifiers_selector.get_modifiers()
|
|
416
|
-
# print(f"{selected_modifiers=}")
|
|
417
408
|
|
|
418
409
|
behavior_to_stop_modifier_str: str = ""
|
|
419
410
|
for idx in util.sorted_keys(selected_modifiers):
|
|
@@ -472,8 +463,8 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
472
463
|
comment,
|
|
473
464
|
frame_idx,
|
|
474
465
|
]
|
|
475
|
-
# order
|
|
476
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort()
|
|
466
|
+
# order events list using time, subject, behavior
|
|
467
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
477
468
|
|
|
478
469
|
elif self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.LIVE:
|
|
479
470
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][event["row"]] = [
|
|
@@ -483,8 +474,8 @@ def write_event(self, event: dict, mem_time: dec) -> int:
|
|
|
483
474
|
modifier_str,
|
|
484
475
|
comment,
|
|
485
476
|
]
|
|
486
|
-
# order
|
|
487
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort()
|
|
477
|
+
# order events list using time, subject, behavior
|
|
478
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS].sort(key=lambda x: x[:3])
|
|
488
479
|
|
|
489
480
|
elif self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.IMAGES:
|
|
490
481
|
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][event["row"]] = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: boris-behav-obs
|
|
3
|
-
Version: 9.6.
|
|
3
|
+
Version: 9.6.3
|
|
4
4
|
Summary: BORIS - Behavioral Observation Research Interactive Software
|
|
5
5
|
Author-email: Olivier Friard <olivier.friard@unito.it>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -19,7 +19,7 @@ Requires-Python: >=3.12
|
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.TXT
|
|
21
21
|
Requires-Dist: exifread>=3.0.0
|
|
22
|
-
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: numpy==2.3.2
|
|
23
23
|
Requires-Dist: matplotlib>=3.3.3
|
|
24
24
|
Requires-Dist: pandas>=2.2.2
|
|
25
25
|
Requires-Dist: tablib[cli,html,ods,pandas,xls,xlsx]>=3
|
|
@@ -27,6 +27,7 @@ Requires-Dist: pyreadr
|
|
|
27
27
|
Requires-Dist: pyside6==6.9
|
|
28
28
|
Requires-Dist: hachoir>=3.3.0
|
|
29
29
|
Requires-Dist: scipy>=1.15.3
|
|
30
|
+
Requires-Dist: scikit-learn>=1.7.1
|
|
30
31
|
Provides-Extra: dev
|
|
31
32
|
Requires-Dist: ruff; extra == "dev"
|
|
32
33
|
Requires-Dist: pytest; extra == "dev"
|
|
@@ -48,7 +49,7 @@ You can not longer run BORIS natively on MacOS (since v.8). Some alternatives to
|
|
|
48
49
|
|
|
49
50
|
It provides also some analysis tools like time budget and some plotting functions.
|
|
50
51
|
|
|
51
|
-
The BORIS paper has more than [ citations](https://www.boris.unito.it/citations) in peer-reviewed scientific publications.
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
|
|
@@ -58,13 +59,17 @@ See the official [BORIS web site](https://www.boris.unito.it).
|
|
|
58
59
|
[](https://www.python.org)
|
|
59
60
|

|
|
60
61
|

|
|
62
|
+
[](https://pypi.org/project/boris-behav-obs/)
|
|
63
|
+
|
|
61
64
|
[](https://pepy.tech/project/boris-behav-obs)
|
|
62
65
|

|
|
63
|
-
|
|
64
|
-

|
|
66
|
+

|
|
65
67
|
|
|
68
|
+

|
|
66
69
|
|
|
67
70
|
|
|
71
|
+

|
|
72
|
+
[](https://github.com/olivierfriard/BORIS/stargazers)
|
|
68
73
|
|
|
69
74
|
# Documentation
|
|
70
75
|
|
|
@@ -81,8 +86,7 @@ Some [video tutorials](https://www.boris.unito.it/video_tutorials/) are availabl
|
|
|
81
86
|
# Bug reports and feature requests
|
|
82
87
|
|
|
83
88
|
|
|
84
|
-
To search for bugs, report them or request a feature, please use the
|
|
85
|
-
https://github.com/olivierfriard/BORIS/issues
|
|
89
|
+
To search for bugs, report them or request a feature, please use the [GitHub issues tracker](https://github.com/olivierfriard/BORIS/issues)
|
|
86
90
|
|
|
87
91
|
|
|
88
92
|
|
|
@@ -128,7 +132,7 @@ GNU General Public License for more details.
|
|
|
128
132
|
|
|
129
133
|
Distributed with a [GPL v.3 license](LICENSE.TXT).
|
|
130
134
|
|
|
131
|
-
Copyright (C) 2012-
|
|
135
|
+
Copyright (C) 2012-2025 Olivier Friard
|
|
132
136
|
|
|
133
137
|
|
|
134
138
|
|