Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
eba6406
adds acquisition mapping
micahwoodard Mar 30, 2026
441ad11
adds rig schema
micahwoodard Mar 30, 2026
a32a842
adds data description
micahwoodard Mar 30, 2026
3fef59b
updates __init__
micahwoodard Mar 30, 2026
062b917
fixes init
micahwoodard Mar 30, 2026
6d0f998
Merge branch 'feat-adding-curriculum' into feat-metadata-mapping
micahwoodard Mar 31, 2026
6dbe0f0
adds cli for mapping
micahwoodard Mar 31, 2026
91febd4
Merge branch 'feat-adding-curriculum' into feat-metadata-mapping
micahwoodard Apr 1, 2026
79dc6f9
refrences current metrics in mapping
micahwoodard Apr 1, 2026
9e03163
dumps metrics
micahwoodard Apr 1, 2026
963292f
prints json
micahwoodard Apr 1, 2026
062aa2f
removes return
micahwoodard Apr 1, 2026
1f5a2fc
lints
micahwoodard Apr 1, 2026
ba96b5f
adds logging
micahwoodard Apr 2, 2026
2f0a446
converts end condition time to seconds
micahwoodard Apr 2, 2026
382ae85
streams logs to stdout
micahwoodard Apr 2, 2026
f160fbd
moves logging before import
micahwoodard Apr 2, 2026
f1fff6d
log fixes
micahwoodard Apr 2, 2026
f7502b1
push fixes for baiting
micahwoodard Apr 7, 2026
37f9c84
add cli
micahwoodard Apr 8, 2026
690982d
adds project script
micahwoodard Apr 8, 2026
c9ee84a
updates args
micahwoodard Apr 8, 2026
e68fe44
removes suffix
micahwoodard Apr 8, 2026
a107aa8
lints
micahwoodard Apr 9, 2026
6ef9ed3
removes data description
micahwoodard Apr 13, 2026
0a7e2f4
configures logger to json and removes cluttered log mesages
micahwoodard Apr 16, 2026
2c411f9
lints
micahwoodard Apr 16, 2026
96ea485
adds name to log schema
micahwoodard Apr 16, 2026
4a305d1
cleans up baiting logic
micahwoodard Apr 17, 2026
80080af
removes test
micahwoodard Apr 17, 2026
0e982ac
merges with main
micahwoodard Apr 29, 2026
7632a11
bumps version
micahwoodard Apr 29, 2026
588e98e
merges with main
micahwoodard May 1, 2026
f402215
lints
micahwoodard May 1, 2026
2509705
lints
micahwoodard May 1, 2026
3637f52
Merge branch 'main' into feat-metadata-mapping
micahwoodard May 19, 2026
442104a
Merge branch 'main' into feat-metadata-mapping
micahwoodard May 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions scripts/walk_through_session.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import logging
import os

from aind_behavior_dynamic_foraging.task_logic.trial_generators.coupled_warmup_trial_generator import (
from aind_behavior_dynamic_foraging.data_contract import dataset as df_foraging_dataset
from aind_behavior_dynamic_foraging.task_logic.trial_generators.coupled_trial_generators.coupled_warmup_trial_generator import (
CoupledWarmupTrialGeneratorSpec,
)

from aind_behavior_dynamic_foraging.data_contract import dataset as df_foraging_dataset
from aind_behavior_dynamic_foraging.task_logic.trial_models import TrialOutcome

logging.basicConfig(
Expand All @@ -20,10 +19,10 @@ def walk_through_session(data_directory: os.PathLike):
software_events.load_all()

trial_outcomes = software_events["TrialOutcome"].data["data"].iloc
warmup_trial_generator = CoupledWarmupTrialGeneratorSpec().create_generator()
trial_generator = CoupledWarmupTrialGeneratorSpec().create_generator()
for i, outcome in enumerate(trial_outcomes):
warmup_trial_generator.update(TrialOutcome.model_validate(outcome))
trial = warmup_trial_generator.next()
trial_generator.update(TrialOutcome.model_validate(outcome))
trial = trial_generator.next()

if not trial:
print(f"Session finished at trial {i}")
Expand Down
10 changes: 9 additions & 1 deletion src/Extensions/bonsai.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import logging
import sys
from typing import TYPE_CHECKING

from pydantic import TypeAdapter

from aind_behavior_dynamic_foraging.task_logic import TrialGeneratorSpec
logging.basicConfig(
stream=sys.stdout,
level=logging.DEBUG,
format='{"name": "%(name)s", "level": %(levelno)d, "msg": "%(message)s"}',
)

from aind_behavior_dynamic_foraging.task_logic import TrialGeneratorSpec # noqa
Comment thread
micahwoodard marked this conversation as resolved.

if TYPE_CHECKING:
from aind_behavior_dynamic_foraging.task_logic.trial_generators._base import ITrialGenerator
Expand Down
12 changes: 10 additions & 2 deletions src/aind_behavior_dynamic_foraging/data_contract/_dataset.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pathlib import Path

from aind_behavior_curriculum import TrainerState
from aind_behavior_curriculum import Metrics, TrainerState
from aind_behavior_services.session import Session
from contraqctor.contract import Dataset, DataStreamCollection
from contraqctor.contract.camera import Camera
Expand Down Expand Up @@ -61,10 +61,18 @@ def make_dataset(
data_streams=[
Json(
name="PreviousMetrics",
reader_params=Json.make_params(
reader_params=PydanticModel.make_params(
model=Metrics,
Comment thread
micahwoodard marked this conversation as resolved.
path=root_path / "behavior/previous_metrics.json",
),
),
PydanticModel(
name="Metrics",
reader_params=PydanticModel.make_params(
model=Metrics,
path=root_path / "behavior/metrics.json",
),
),
PydanticModel(
name="TrainerState",
reader_params=PydanticModel.make_params(
Expand Down
34 changes: 34 additions & 0 deletions src/aind_behavior_dynamic_foraging/data_contract/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import os
from typing import Optional

from aind_behavior_dynamic_foraging.data_contract import dataset
from aind_behavior_dynamic_foraging.task_logic import AindDynamicForagingTaskLogic


def calculate_consumed_water(session_path: os.PathLike) -> Optional[float]:
"""Calculate the total volume of water consumed during a session.

Args:
session_path (os.PathLike): Path to the session directory.

Returns:
Optional[float]: Total volume of water consumed in milliliters, or None if unavailable.
"""

trial_outcomes = dataset(session_path)["Behavior"]["SoftwareEvents"]["TrialOutcome"].load().data["data"]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can validate this with the TrialOutcome pydantic class to make your life easier

is_right_choice = [to["is_right_choice"] for to in trial_outcomes]
is_rewarded = [to["is_rewarded"] for to in trial_outcomes]

task_logic_data = dataset(session_path)["Behavior"]["InputSchemas"]["TaskLogic"].load().data
task_logic = AindDynamicForagingTaskLogic.model_validate(task_logic_data)
right_reward_size = task_logic.task_parameters.reward_size.right_value_volume
left_reward_size = task_logic.task_parameters.reward_size.left_value_volume

total = 0
for choice, rewarded in zip(is_right_choice, is_rewarded):
if rewarded:
if choice is True:
total += right_reward_size * 1e-3
if choice is False:
total += left_reward_size * 1e-3
return total
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class CoupledWarmupTrialGeneratorSpec(BaseCoupledTrialGeneratorSpec):
default=True, description="Whether uncollected rewards carry over to the next trial."
)

def create_generator(self) -> "CoupledWarmupTrialGeneratorSpec":
def create_generator(self) -> "CoupledWarmupTrialGenerator":
return CoupledWarmupTrialGenerator(self)


Expand Down
98 changes: 98 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ dependencies = [
"aind-behavior-curriculum >= 0.0.38",
"numpy>=2.4.2",
"pydantic-settings",
"aind-behavior-dynamic-foraging==0.0.2rc24"
"aind-behavior-dynamic-foraging==0.0.2rc30"
]

[tool.uv.sources]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def metrics_from_dataset(
logger.debug(f"Calculated foraging efficiency as {foraging_efficiency}")

try:
prev_metrics = DynamicForagingMetrics(**dataset["Behavior"]["PreviousMetrics"].data)
prev_metrics = DynamicForagingMetrics.model_validate(dataset["Behavior"]["PreviousMetrics"].data)
prev_stage = prev_metrics.stage_name
except FileNotFoundError:
logger.info("No previous metrics found.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
from typing import Optional
from unittest.mock import MagicMock, PropertyMock, patch

import numpy as np

from aind_behavior_dynamic_foraging_curricula.metrics import (
metrics_from_dataset,
)
Expand Down Expand Up @@ -110,17 +108,6 @@ def test_previous_metrics_accumulate(self):
self.assertEqual(len(result.foraging_efficiency_per_session), 2)
self.assertEqual(len(result.unignored_trials_per_session), 2)

def test_foraging_efficiency_is_finite_and_positive(self):
trials = [
_make_trial(True, True, 0.7, 0.3),
_make_trial(True, False, 0.7, 0.3),
_make_trial(False, True, 0.7, 0.3),
]
with _patch_dataset(trials):
result = metrics_from_dataset(self.tmp_path)
self.assertGreater(result.foraging_efficiency_per_session[-1], 0)
self.assertTrue(np.isfinite(result.foraging_efficiency_per_session[-1]))


if __name__ == "__main__":
unittest.main()
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
[build-system]
requires = ["uv_build>=0.8.22"]
build-backend = "uv_build"

[project]
name = "aind-behavior-dynamic-foraging-metadata-mapper"
description = "A library of mapping for the Dynamic Foraging task."
authors = [
{name = "Bruno Cruz", email = "bruno.cruz@alleninstitute.org"},
{name = "Micah Woodard", email = "micah.woodard@alleninstitute.org"}
]
license = "MIT"
version = "0.0.1"
requires-python = ">=3.11"
classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Operating System :: Microsoft :: Windows",
]
readme = {file = "README.md", content-type = "text/markdown"}

dependencies = [
"numpy>=2.4.2",
"pydantic-settings",
"aind-behavior-dynamic-foraging==0.0.2rc30",
"aind-data-schema>=2.6.0",
"cyclopts>=4.10.0"
]

[tool.uv.sources]
aind-behavior-dynamic-foraging = { workspace = true }

[dependency-groups]

dev = [
'ruff',
'pytest',
'pytest-cov',
'codespell',
]

docs = [
'mkdocs',
'mkdocs-material',
'mkdocstrings[python]',
'pymdown-extensions',
'ruff',
]

[tool.uv]
default-groups = ['dev']

[tool.ruff]
line-length = 120
target-version = 'py311'

[tool.ruff.lint]
extend-select = ['Q', 'RUF100', 'C90', 'I']
extend-ignore = []
mccabe = { max-complexity = 14 }
pydocstyle = { convention = 'google' }

[tool.codespell]
skip = '.git,*.pdf,*.svg,uv.lock'
ignore-words-list = 'nd'

[tool.pytest.ini_options]
addopts = "--strict-markers --tb=short --cov=src --cov-report=term-missing --cov-fail-under=70"
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]

[project.scripts]
acquisition = "aind_behavior_dynamic_foraging_metadata_mapper.acquisition:app"
instrument = "aind_behavior_dynamic_foraging_metadata_mapper.instrument:app"
mapper = "aind_behavior_dynamic_foraging_metadata_mapper.cli:main"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .acquisition import acqusition_from_dataset
from .instrument import instrument_from_dataset

__all__ = [
"acqusition_from_dataset",
"instrument_from_dataset",
]
Loading
Loading