Skip to content

Commit

Permalink
Merge pull request #25 from AllenNeuralDynamics/rel-0.7.0
Browse files Browse the repository at this point in the history
Release 0.7.0
  • Loading branch information
bruno-f-cruz authored May 9, 2024
2 parents 71a0366 + e65b855 commit 7fa78dc
Show file tree
Hide file tree
Showing 45 changed files with 1,665 additions and 437 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ These should only need to be installed once on a fresh new system, and are not r
- [FTDI CDM Driver 2.12.28](https://www.ftdichip.com/Drivers/CDM/CDM21228_Setup.zip) (serial port drivers for HARP devices)
- [Spinnaker SDK 1.29.0.5](https://www.flir.co.uk/support/products/spinnaker-sdk/#Downloads) (device drivers for FLIR cameras)
- On FLIR website: `Download > archive > 1.29.0.5 > SpinnakerSDK_FULL_1.29.0.5_x64.exe`

- [ffmpeg 7.0](https://www.gyan.dev/ffmpeg/builds/) (necessary to run GPU-accelerated video encoding)
- run `winget install ffmpeg -v 7.0` to install.
---

## Generating valid JSON input files
Expand Down
15 changes: 6 additions & 9 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
SOURCE_ROOT = "https://github.com/AllenNeuralDynamics/Aind.Behavior.Services/tree/main/src/DataSchemas/"


project = 'AIND Behavior Services'
project = "AIND Behavior Services"
copyright = "2024, Allen Institute for Neural Dynamics"
author = 'Bruno Cruz'
author = "Bruno Cruz"
release = __version__


Expand Down Expand Up @@ -53,13 +53,12 @@
json_file_name = os.path.basename(json_file)
root_template += f" json-schemas/{json_file_name.replace('.json', '')}\n"
with open(os.path.join(rst_target_path, f"{json_file_name.replace('.json', '')}.rst"), "w") as f:
f.write(leaf_template.format(json_file_name=json_file_name.replace('.json', '')))
f.write(leaf_template.format(json_file_name=json_file_name.replace(".json", "")))

with open("json-schemas.rst", "w") as f:
f.write(root_template)



# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

Expand All @@ -75,8 +74,8 @@
"myst_parser",
"sphinxcontrib.autodoc_pydantic",
]
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

autosummary_generate = True

Expand All @@ -103,13 +102,11 @@

# -- Options for linkcode extension ---------------------------------------


def linkcode_resolve(domain, info):
if domain != "py":
return None
if not info["module"]:
return None
filename = info["module"].replace(".", "/")
return f"{SOURCE_ROOT}/{filename}.py"



2 changes: 1 addition & 1 deletion examples/aind_manipulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
calibration_date=datetime.datetime.now(),
)

calibration_logic = m.CalibrationLogic()
calibration_logic = m.CalibrationLogic(task_parameters=m.CalibrationParameters())


calibration_session = AindBehaviorSessionModel(
Expand Down
4 changes: 3 additions & 1 deletion examples/load_cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
calibration_date=datetime.datetime.now(),
)

calibration_logic = lc.CalibrationLogic(channels=[0, 1], offset_buffer_size=10)
calibration_logic = lc.CalibrationLogic(
task_parameters=lc.CalibrationParameters(channels=[0, 1], offset_buffer_size=10)
)

calibration_session = AindBehaviorSessionModel(
root_path="C:\\Data",
Expand Down
28 changes: 14 additions & 14 deletions examples/olfactometer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,45 @@
from aind_behavior_services.calibration import olfactometer as olf
from aind_behavior_services.session import AindBehaviorSessionModel

channels_config = [
olf.OlfactometerChannelConfig(
channels_config = {
olf.OlfactometerChannel.Channel0: olf.OlfactometerChannelConfig(
channel_index=olf.OlfactometerChannel.Channel0,
channel_type=olf.OlfactometerChannelType.ODOR,
flow_rate=100,
odorant="Banana",
odorant_dilution=0.1,
),
olf.OlfactometerChannelConfig(
olf.OlfactometerChannel.Channel1: olf.OlfactometerChannelConfig(
channel_index=olf.OlfactometerChannel.Channel1,
channel_type=olf.OlfactometerChannelType.ODOR,
flow_rate=100,
odorant="Strawberry",
odorant_dilution=0.1,
),
olf.OlfactometerChannelConfig(
olf.OlfactometerChannel.Channel2: olf.OlfactometerChannelConfig(
channel_index=olf.OlfactometerChannel.Channel2,
channel_type=olf.OlfactometerChannelType.ODOR,
flow_rate=100,
odorant="Apple",
odorant_dilution=0.1,
),
olf.OlfactometerChannelConfig(
olf.OlfactometerChannel.Channel3: olf.OlfactometerChannelConfig(
channel_index=olf.OlfactometerChannel.Channel3, channel_type=olf.OlfactometerChannelType.CARRIER, odorant="Air"
),
]

channels_config = {channel.channel_index: channel for channel in channels_config}
)
}

calibration = olf.OlfactometerCalibration(
input=olf.OlfactometerCalibrationInput(), output=olf.OlfactometerCalibrationOutput()
)

calibration_logic = olf.CalibrationLogic(
channel_config=channels_config,
full_flow_rate=1000,
n_repeats_per_stimulus=10,
time_on=1,
time_off=1,
task_parameters=olf.CalibrationParameters(
channel_config=channels_config,
full_flow_rate=1000,
n_repeats_per_stimulus=10,
time_on=1,
time_off=1,
)
)

calibration_session = AindBehaviorSessionModel(
Expand Down
4 changes: 3 additions & 1 deletion examples/water_valve.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ def linear_model(time, slope, offset):
calibration_date=datetime.datetime.now(),
)

calibration_logic = wv.CalibrationLogic(valve_open_time=_delta_times, valve_open_interval=0.5, repeat_count=200)
calibration_logic = wv.CalibrationLogic(
task_parameters=wv.CalibrationParameters(valve_open_time=_delta_times, valve_open_interval=0.5, repeat_count=200)
)

calibration_session = AindBehaviorSessionModel(
root_path="C:\\Data",
Expand Down
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ dependencies = [
'harp-python>=0.2',
'gitpython>=3.1, <4.0',
'scikit-learn',
'semver'
'semver',
'aind-behavior-curriculum@git+https://github.com/AllenNeuralDynamics/[email protected]',

]

[project.optional-dependencies]
dev = [
'aind_behavior_services[linters]'

aind-data-schema-mapper = [
'aind-data-schema==0.33.9'
]

linters = [
Expand Down
32 changes: 32 additions & 0 deletions scripts/install_dependencies.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Write-Host "Installing dependencies..." -ForegroundColor White
$autoaccept = @("--accept-package-agreements", "--accept-source-agreements")

winget install -e --id 7zip.7zip @autoaccept
winget install ffmpeg -v 7.0 @autoaccept
winget install -e --id Git.Git @autoaccept
winget install -e --id Python.Python.3.11 --scope user @autoaccept
winget install -e --id Microsoft.VisualStudioCode --scope user @autoaccept --override '/SILENT /mergetasks="!runcode,addcontextmenufiles,addcontextmenufolders"'
winget install -e --id Microsoft.DotNet.Framework.DeveloperPack_4 @autoaccept
Winget install "Microsoft Visual C++ 2012 Redistributable (x64)" --force @autoaccept
winget install -e --id Nvidia.GeForceExperience -v 3.26.0.160 @autoaccept
winget install -e --id Notepad++.Notepad++ @autoaccept
winget install -e --id dotPDNLLC.paintdotnet @autoaccept

## Install vscode extensions
$extensions =
"eamodio.gitlens",
"donjayamanne.python-extension-pack"
"redhat.vscode-yaml"

$cmd = "code --list-extensions"
Invoke-Expression $cmd -OutVariable output | Out-Null
$installed = $output -split "\s"

foreach ($ext in $extensions) {
if ($installed.Contains($ext)) {
Write-Host $ext "already installed." -ForegroundColor Gray
} else {
Write-Host "Installing" $ext "..." -ForegroundColor White
code --install-extension $ext
}
}
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from setuptools import setup

if __name__ == "__main__":
setup()
setup()
2 changes: 1 addition & 1 deletion src/DataSchemas/aind_behavior_services/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.6.1"
__version__ = "0.7.0"

from .rig import AindBehaviorRigModel
from .session import AindBehaviorSessionModel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
try:
import aind_data_schema
except ImportError as e:
e.add_note(
"The 'aind-data-schema' package is required to use this module. Install the optional dependencies defined in `project.toml' by running `pip install .[aind-data-schema-mapper]`"
)
raise
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import json
import os
from pathlib import Path
from typing import Any, Dict, Optional, Type, TypeVar, Union

from aind_behavior_services import AindBehaviorRigModel, AindBehaviorSessionModel, AindBehaviorTaskLogicModel
from aind_data_schema.core.session import Modality, Session, Software, StimulusEpoch, Stream

TSession = TypeVar("TSession", bound=AindBehaviorSessionModel)
TRig = TypeVar("TRig", bound=AindBehaviorRigModel)
TTaskLogic = TypeVar("TTaskLogic", bound=AindBehaviorTaskLogicModel)
TSchema = TypeVar("TSchema", bound=Union[AindBehaviorSessionModel, AindBehaviorRigModel, AindBehaviorTaskLogicModel])


def map_to_aind_data_schema(
session_root: Path,
session: Type[TSession] = AindBehaviorSessionModel,
rig: Type[TRig] = AindBehaviorRigModel,
task_logic: Optional[type[TTaskLogic]] = None,
) -> Session:
"""
Maps the input session found in `session_root` to the aind-data-schema Session object.
Args:
session_root (Path): The root path of the session.
session (Type[TSession]): The session class.
rig (Type[TRig]): The rig class.
task_logic (Type[TTaskLogic]): The task logic class.
Returns:
Session: The mapped aind-data-schema session object.
"""

session_path = session_root / "Config" / "session_input.json"
task_logic_path = session_root / "Config" / "tasklogic_input.json"
rig_path = session_root / "Config" / "rig_input.json"

if task_logic is None:
return mapper(
model_from_json_file(session_path, session),
model_from_json_file(rig_path, rig),
json.loads((task_logic_path.read_text())),
)
else:
return mapper(
model_from_json_file(session_path, session),
model_from_json_file(rig_path, rig),
model_from_json_file(task_logic_path, task_logic),
)


def mapper(
session: TSession,
rig: TRig,
task_logic: TTaskLogic | Dict[str, Any],
) -> Session:
"""
Maps the input session, rig, and task logic to an aind-data-schema Session object.
Args:
session (TSession): The input session object.
rig (TRig): The input rig object.
task_logic (TTaskLogic): The input task logic object.
Returns:
Session: The mapped aind-data-schema Session object.
"""

if isinstance(task_logic, dict):
pass
elif isinstance(task_logic, AindBehaviorTaskLogicModel):
task_logic = task_logic.model_dump()
else:
raise ValueError("task_logic must be a dict or AindBehaviorTaskLogicModel")

ads_session = Session(
experimenter_full_name=session.experimenter,
session_start_time=session.date,
session_type=session.experiment,
rig_id=rig.rig_name,
subject_id=session.subject,
notes=session.notes,
data_streams=[
Stream(
stream_modalities=[Modality.BEHAVIOR, Modality.BEHAVIOR_VIDEOS],
stream_start_time=session.date,
stream_end_time=session.date,
camera_names=["NULL"],
),
],
mouse_platform_name="Mouse platform",
active_mouse_platform=True,
stimulus_epochs=[
StimulusEpoch(
stimulus_name="vr-foraging task",
stimulus_start_time=session.date,
stimulus_end_time=session.date,
stimulus_modalities=["None"],
software=[
Software(
name="Bonsai",
version=session.commit_hash,
url="https://github.com/AllenNeuralDynamics/Aind.Behavior.VrForaging/blob/{sha}/bonsai/Bonsai.config".format(
sha=session.commit_hash
),
)
],
script=Software(
name="vr-foraging.bonsai",
version=session.commit_hash,
url="https://github.com/AllenNeuralDynamics/Aind.Behavior.VrForaging/blob/{sha}/src/vr-foraging.bonsai".format(
sha=session.commit_hash
),
parameters=task_logic,
),
)
],
)
return ads_session


def model_from_json_file(json_path: os.PathLike | str, model_class: Type[TSchema]) -> TSchema:
with open(json_path, encoding="utf-8") as f:
return model_class.model_validate_json(f.read())


def model_to_json_file(json_path: os.PathLike | str, model: TSchema) -> None:
with open(json_path, "w", encoding="utf-8") as f:
f.write(model.model_dump_json(indent=4))
Loading

0 comments on commit 7fa78dc

Please sign in to comment.