From 21b12d9e68281a47629bc69afe6c014e29b5b2b8 Mon Sep 17 00:00:00 2001 From: NeuroThom Date: Thu, 15 Oct 2026 12:15:33 +0100 Subject: [PATCH 1/5] Return empty metrics from integration generator The generator inherited the protocol's docstring-only get_metrics, which returns None and becomes a null TrialMetrics in Bonsai. Return TrialMetrics() and add a test. Closes #206. --- .../integration_test_trial_generator.py | 12 +++++++++++- .../test_integration_test_trial_generator.py | 13 ++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/integration_test_trial_generator.py b/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/integration_test_trial_generator.py index 7023604a..56ba65de 100644 --- a/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/integration_test_trial_generator.py +++ b/src/aind_behavior_dynamic_foraging/task_logic/trial_generators/integration_test_trial_generator.py @@ -2,7 +2,7 @@ from aind_behavior_services.task import distributions -from ..trial_models import QuickRetractSettings, Trial, TrialOutcome +from ..trial_models import QuickRetractSettings, Trial, TrialMetrics, TrialOutcome from ._base import BaseTrialGeneratorSpecModel, ITrialGenerator @@ -82,3 +82,13 @@ def next(self) -> Trial | None: def update(self, outcome: TrialOutcome | str) -> None: self._idx += 1 + + def get_metrics(self) -> TrialMetrics: + """Return metrics at current state of the trial generator. + + The integration test generator does not track bias, so the metrics are empty. + Without this override, the class inherits ITrialGenerator.get_metrics directly, + which is an empty stub for now. + Bonsai deserialised this into a null TrialMetrics and the visualizers throws. + """ + return TrialMetrics() diff --git a/tests/trial_generators/test_integration_test_trial_generator.py b/tests/trial_generators/test_integration_test_trial_generator.py index fab593e6..dc27a7ad 100644 --- a/tests/trial_generators/test_integration_test_trial_generator.py +++ b/tests/trial_generators/test_integration_test_trial_generator.py @@ -1,7 +1,7 @@ import unittest from aind_behavior_dynamic_foraging.task_logic.trial_generators import IntegrationTestTrialGeneratorSpec -from aind_behavior_dynamic_foraging.task_logic.trial_models import Trial, TrialOutcome +from aind_behavior_dynamic_foraging.task_logic.trial_models import Trial, TrialMetrics, TrialOutcome class TestIntegrationTestTrialGenerator(unittest.TestCase): @@ -54,6 +54,17 @@ def test_sequential_access(self): self.assertEqual(trials_count, expected_count) + def test_get_metrics_returns_empty_metrics(self): + spec = IntegrationTestTrialGeneratorSpec() + generator = spec.create_generator() + + metrics = generator.get_metrics() + self.assertIsInstance(metrics, TrialMetrics) + self.assertIsNone(metrics.bias) + + generator.update(TrialOutcome(trial=generator.next(), is_right_choice=True, is_rewarded=True)) + self.assertIsInstance(generator.get_metrics(), TrialMetrics) + if __name__ == "__main__": unittest.main() From 56663f6b62e835105b2157fae3a70a9c1fef998f Mon Sep 17 00:00:00 2001 From: NeuroThom Date: Thu, 15 Oct 2026 12:15:37 +0100 Subject: [PATCH 2/5] Guard null stage name and experimenter fields FilterNotNull ran on the metadata tuple before StageName was selected, and the Experimenter branch had no guard. Move the include after the selector and add one before the experimenter Count check. Closes #205. --- src/Extensions/MetadataVisualizer.bonsai | 58 ++++++++++++------------ 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/Extensions/MetadataVisualizer.bonsai b/src/Extensions/MetadataVisualizer.bonsai index 45d98424..192b5639 100644 --- a/src/Extensions/MetadataVisualizer.bonsai +++ b/src/Extensions/MetadataVisualizer.bonsai @@ -82,10 +82,10 @@ CurrentMetadata - Item2.StageName + @@ -104,6 +104,7 @@ Item1.Experimenter + @@ -301,52 +302,53 @@ - - - - + + + + - + - - - + + + - - - + + + - - + + - - - - - + + + + + - + - - - + + + - - - - - + + + + + + From ff7354c3f08caa39a84a76abd20680f6e14b3ba7 Mon Sep 17 00:00:00 2001 From: NeuroThom Date: Thu, 15 Oct 2026 13:02:01 +0100 Subject: [PATCH 3/5] Open the visualizer window maximized Closes #210. --- src/Extensions/Visualizers.bonsai | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extensions/Visualizers.bonsai b/src/Extensions/Visualizers.bonsai index e4788271..761c1ab2 100644 --- a/src/Extensions/Visualizers.bonsai +++ b/src/Extensions/Visualizers.bonsai @@ -38,7 +38,7 @@ true - + Maximized From b8acf703cb3cd0da58b5044704600b2a2f869a81 Mon Sep 17 00:00:00 2001 From: NeuroThom Date: Thu, 15 Oct 2026 13:02:01 +0100 Subject: [PATCH 4/5] Close manually opened valves after 3 minutes The Keep Open loops stop on a 3 min timer and report completion to the toggle buttons the same way the drain loops do. Closes #140. --- src/Extensions/PreSessionVisualizer.bonsai | 352 ++++++++++++--------- src/Extensions/Visualizers.bonsai | 350 ++++++++++---------- src/test_visualizer.bonsai | 85 +---- 3 files changed, 403 insertions(+), 384 deletions(-) diff --git a/src/Extensions/PreSessionVisualizer.bonsai b/src/Extensions/PreSessionVisualizer.bonsai index ae31e7ec..8bbe4dde 100644 --- a/src/Extensions/PreSessionVisualizer.bonsai +++ b/src/Extensions/PreSessionVisualizer.bonsai @@ -1,5 +1,5 @@  - + + PreSessionVisualizer @@ -84,6 +86,17 @@ + + + + + KeepLeftOpenComplete + + + + + + KeepLeftOpen true @@ -107,6 +120,17 @@ + + + + + KeepRightOpenComplete + + + + + + KeepRightOpen true @@ -141,6 +165,14 @@ + + + + + + + + KeepBothOpen true @@ -711,182 +743,197 @@ + + LeftOpenState + + + RightOpenState + + + BothOpenState + - + - - - - + + + + - - + + - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + - - - + + + - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - + + + + + - - - - + + - - - - + + + + + + + + + - + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + + + - + + - - - - - - - - - - - - - + + + + + - - - - - - - - + + + + - - - - - + + + + + + + + + + + + + + + + + + @@ -906,10 +953,17 @@ - - + + - + + + + + + + + diff --git a/src/Extensions/Visualizers.bonsai b/src/Extensions/Visualizers.bonsai index 761c1ab2..2623babd 100644 --- a/src/Extensions/Visualizers.bonsai +++ b/src/Extensions/Visualizers.bonsai @@ -1121,16 +1121,16 @@ IsFlushingValveRight - UiKeepLeftOpen + UiDrainLeft - UiKeepBothOpen + UiDrainBoth - KeepLeftOpen + FlushLeft @@ -1144,7 +1144,7 @@ - DefaultRewardAmount + DefaultVolume Source1 @@ -1177,7 +1177,7 @@ - DefaultRewardAmount + DefaultVolume @@ -1196,17 +1196,63 @@ GiveRewardRight + + DefaultVolume + + + RigSchema + + + Calibration.WaterValveLeft + + + + + + RewardToTime + ((Item1 * 0.001) - Item2.Offset) / Item2.Slope + + + + + + + + + - PT0.5S + PT1.288S + + DefaultVolume + + + DivideByEachOpen + Math.Ceiling(5000 / it) + + + + + + - + + 100 + + + + false + + + + IsFlushingValveLeft + @@ -1222,7 +1268,7 @@ - + @@ -1231,26 +1277,41 @@ - - + + + + + + + + + + + + + + + + DrainLeftComplete + - UiKeepRightOpen + UiDrainRight - KeepRightOpen + FlushRight @@ -1264,7 +1325,7 @@ - DefaultRewardAmount + DefaultVolume Source1 @@ -1297,7 +1358,7 @@ - DefaultRewardAmount + DefaultVolume @@ -1316,17 +1377,63 @@ GiveRewardRight + + DefaultVolume + + + RigSchema + + + Calibration.WaterValveRight + + + + + + RewardToTime + ((Item1 * 0.001) - Item2.Offset) / Item2.Slope + + + + + + + + + - PT0.5S + PT0.888S + + DefaultVolume + + + DivideByEachOpen + Math.Ceiling(5000 / it) + + + + + + - + + 100 + + + + false + + + + IsFlushingValveRight + @@ -1342,7 +1449,7 @@ - + @@ -1351,29 +1458,44 @@ - - + + + + + + + + + + + + + + + + DrainRightComplete + - UiDrainLeft + UiKeepLeftOpen - UiDrainBoth + UiKeepBothOpen - FlushLeft + KeepLeftOpen @@ -1387,7 +1509,7 @@ - DefaultVolume + DefaultRewardAmount Source1 @@ -1420,7 +1542,7 @@ - DefaultVolume + DefaultRewardAmount @@ -1439,62 +1561,24 @@ GiveRewardRight - - DefaultVolume - - - RigSchema - - - Calibration.WaterValveLeft - - - - - - RewardToTime - ((Item1 * 0.001) - Item2.Offset) / Item2.Slope - - - - - - - - - - PT1.288S + PT0.5S - - DefaultVolume - - - DivideByEachOpen - Math.Ceiling(5000 / it) - - - - - + + - - 100 + + PT3M - + - - false - - - - IsFlushingValveLeft + @@ -1511,7 +1595,7 @@ - + @@ -1520,24 +1604,14 @@ - - - + + + - + - - - - - - - - - - - + @@ -1545,16 +1619,16 @@ - DrainLeftComplete + KeepLeftOpenComplete - UiDrainRight + UiKeepRightOpen - FlushRight + KeepRightOpen @@ -1568,7 +1642,7 @@ - DefaultVolume + DefaultRewardAmount Source1 @@ -1601,7 +1675,7 @@ - DefaultVolume + DefaultRewardAmount @@ -1620,62 +1694,24 @@ GiveRewardRight - - DefaultVolume - - - RigSchema - - - Calibration.WaterValveRight - - - - - - RewardToTime - ((Item1 * 0.001) - Item2.Offset) / Item2.Slope - - - - - - - - - - PT0.888S + PT0.5S - - DefaultVolume - - - DivideByEachOpen - Math.Ceiling(5000 / it) - - - - - + + - - 100 + + PT3M - + - - false - - - - IsFlushingValveRight + @@ -1692,7 +1728,7 @@ - + @@ -1701,24 +1737,14 @@ - - - + + + - + - - - - - - - - - - - + @@ -1726,7 +1752,7 @@ - DrainRightComplete + KeepRightOpenComplete @@ -1749,22 +1775,24 @@ - + - + - - - - - + + + + + + - + + @@ -2142,6 +2170,8 @@ UiManipulatorInitialize DrainLeftComplete DrainRightComplete + KeepLeftOpenComplete + KeepRightOpenComplete diff --git a/src/test_visualizer.bonsai b/src/test_visualizer.bonsai index 80d9447b..77e175eb 100644 --- a/src/test_visualizer.bonsai +++ b/src/test_visualizer.bonsai @@ -1,5 +1,5 @@  - DrainRightComplete + + KeepLeftOpenComplete + + + KeepRightOpenComplete + @@ -1994,80 +2000,7 @@ it.Index as TrialIndex true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Break 200 @@ -2995,6 +2928,8 @@ it.Index as TrialIndex UiManipulatorInitialize DrainLeftComplete DrainRightComplete + KeepLeftOpenComplete + KeepRightOpenComplete IntraSession From 187c092c5c3067424eacaab8b4e359c100332438 Mon Sep 17 00:00:00 2001 From: NeuroThom Date: Thu, 15 Oct 2026 13:02:01 +0100 Subject: [PATCH 5/5] Show remaining drain time on the drain buttons Each drain iteration publishes the time left from the repeat index, period and count; the Cancel labels display it. Closes #209. --- src/Extensions/PreSessionVisualizer.bonsai | 352 +++++++++++++-------- src/Extensions/Visualizers.bonsai | 54 ++++ src/test_visualizer.bonsai | 8 + 3 files changed, 287 insertions(+), 127 deletions(-) diff --git a/src/Extensions/PreSessionVisualizer.bonsai b/src/Extensions/PreSessionVisualizer.bonsai index 8bbe4dde..bd67eda9 100644 --- a/src/Extensions/PreSessionVisualizer.bonsai +++ b/src/Extensions/PreSessionVisualizer.bonsai @@ -31,6 +31,8 @@ + + PreSessionVisualizer @@ -208,6 +210,32 @@ + + + + + DrainLeftCountdown + + + Cancel Drain Left ({0:N0} s) + it + + + DrainLeftComplete + + + + Cancel Drain Left + + + + + + + + + + DrainLeft true @@ -234,6 +262,32 @@ + + + + + DrainRightCountdown + + + Cancel Drain Right ({0:N0} s) + it + + + DrainRightComplete + + + + Cancel Drain Right + + + + + + + + + + DrainRight true @@ -257,6 +311,26 @@ + + + + + Cancel Drain Both (L {0:N0} s, R {1:N0} s) + Item1,Item2 + + + + Cancel Drain Both + + + + + + + + + + DrainBoth true @@ -757,7 +831,7 @@ - + @@ -771,7 +845,7 @@ - + @@ -781,7 +855,7 @@ - + @@ -794,176 +868,200 @@ - + - - - - + + + + + - - - - - + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + - - - - - - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + - + + + + - - - - - - - - - - - - + + + - - - - + + - + - - - - - + + + + + + - + - - - + + + + + + + + + + + + - - - - - + + + + + + + + - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + diff --git a/src/Extensions/Visualizers.bonsai b/src/Extensions/Visualizers.bonsai index 2623babd..36e66dc6 100644 --- a/src/Extensions/Visualizers.bonsai +++ b/src/Extensions/Visualizers.bonsai @@ -1262,6 +1262,22 @@ + + + + + + + + + + + RemainingSeconds + (Item2 - Item1.Item1.Index - 1) * (Item1.Item2 + 0.12) + + + DrainLeftCountdown + @@ -1283,18 +1299,25 @@ + + + + + + + @@ -1443,6 +1466,22 @@ + + + + + + + + + + + RemainingSeconds + (Item2 - Item1.Item1.Index - 1) * (Item1.Item2 + 0.12) + + + DrainRightCountdown + @@ -1464,18 +1503,25 @@ + + + + + + + @@ -1754,6 +1800,12 @@ KeepRightOpenComplete + + DrainLeftCountdown + + + DrainRightCountdown + @@ -2172,6 +2224,8 @@ DrainRightComplete KeepLeftOpenComplete KeepRightOpenComplete + DrainLeftCountdown + DrainRightCountdown diff --git a/src/test_visualizer.bonsai b/src/test_visualizer.bonsai index 77e175eb..4f125a2e 100644 --- a/src/test_visualizer.bonsai +++ b/src/test_visualizer.bonsai @@ -1980,6 +1980,12 @@ it.Index as TrialIndex KeepRightOpenComplete + + DrainLeftCountdown + + + DrainRightCountdown + @@ -2930,6 +2936,8 @@ it.Index as TrialIndex DrainRightComplete KeepLeftOpenComplete KeepRightOpenComplete + DrainLeftCountdown + DrainRightCountdown IntraSession