aind-clabe 0.5.0__tar.gz
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.
- aind_clabe-0.5.0/.flake8 +8 -0
- aind_clabe-0.5.0/.gitattributes +4 -0
- aind_clabe-0.5.0/.github/workflows/ci.yml +26 -0
- aind_clabe-0.5.0/.github/workflows/docs.yml +31 -0
- aind_clabe-0.5.0/.github/workflows/tag_and_publish.yml +68 -0
- aind_clabe-0.5.0/.gitignore +82 -0
- aind_clabe-0.5.0/.gitmodules +3 -0
- aind_clabe-0.5.0/.python-version +3 -0
- aind_clabe-0.5.0/LICENSE +21 -0
- aind_clabe-0.5.0/PKG-INFO +121 -0
- aind_clabe-0.5.0/README.md +76 -0
- aind_clabe-0.5.0/docs/_regenerate_api.py +159 -0
- aind_clabe-0.5.0/docs/api/index.md +3 -0
- aind_clabe-0.5.0/docs/dark-logo.svg +129 -0
- aind_clabe-0.5.0/docs/docs_examples/behavior_launcher.md +9 -0
- aind_clabe-0.5.0/docs/docs_examples/slims_launcher.md +9 -0
- aind_clabe-0.5.0/docs/favicon.ico +0 -0
- aind_clabe-0.5.0/docs/index.md +1 -0
- aind_clabe-0.5.0/docs/light-logo.svg +128 -0
- aind_clabe-0.5.0/docs/logo.svg +42 -0
- aind_clabe-0.5.0/examples/_._ +0 -0
- aind_clabe-0.5.0/examples/behavior_launcher.py +143 -0
- aind_clabe-0.5.0/examples/slims_launcher.py +126 -0
- aind_clabe-0.5.0/mkdocs.yml +125 -0
- aind_clabe-0.5.0/pyproject.toml +101 -0
- aind_clabe-0.5.0/scripts/_._ +0 -0
- aind_clabe-0.5.0/setup.cfg +4 -0
- aind_clabe-0.5.0/setup.py +4 -0
- aind_clabe-0.5.0/src/aind_clabe.egg-info/PKG-INFO +121 -0
- aind_clabe-0.5.0/src/aind_clabe.egg-info/SOURCES.txt +81 -0
- aind_clabe-0.5.0/src/aind_clabe.egg-info/dependency_links.txt +1 -0
- aind_clabe-0.5.0/src/aind_clabe.egg-info/requires.txt +31 -0
- aind_clabe-0.5.0/src/aind_clabe.egg-info/top_level.txt +1 -0
- aind_clabe-0.5.0/src/clabe/__init__.py +14 -0
- aind_clabe-0.5.0/src/clabe/apps/__init__.py +5 -0
- aind_clabe-0.5.0/src/clabe/apps/_base.py +98 -0
- aind_clabe-0.5.0/src/clabe/apps/_bonsai.py +342 -0
- aind_clabe-0.5.0/src/clabe/apps/_python_script.py +280 -0
- aind_clabe-0.5.0/src/clabe/behavior_launcher/__init__.py +24 -0
- aind_clabe-0.5.0/src/clabe/behavior_launcher/_aind_auth.py +92 -0
- aind_clabe-0.5.0/src/clabe/behavior_launcher/_cli.py +32 -0
- aind_clabe-0.5.0/src/clabe/behavior_launcher/_launcher.py +594 -0
- aind_clabe-0.5.0/src/clabe/behavior_launcher/_model_modifiers.py +108 -0
- aind_clabe-0.5.0/src/clabe/behavior_launcher/_services.py +316 -0
- aind_clabe-0.5.0/src/clabe/behavior_launcher/slims_picker.py +607 -0
- aind_clabe-0.5.0/src/clabe/data_mapper/__init__.py +10 -0
- aind_clabe-0.5.0/src/clabe/data_mapper/_base.py +95 -0
- aind_clabe-0.5.0/src/clabe/data_mapper/aind_data_schema.py +77 -0
- aind_clabe-0.5.0/src/clabe/data_mapper/helpers.py +105 -0
- aind_clabe-0.5.0/src/clabe/data_transfer/__init__.py +2 -0
- aind_clabe-0.5.0/src/clabe/data_transfer/_base.py +63 -0
- aind_clabe-0.5.0/src/clabe/data_transfer/aind_watchdog.py +704 -0
- aind_clabe-0.5.0/src/clabe/data_transfer/robocopy.py +211 -0
- aind_clabe-0.5.0/src/clabe/git_manager/__init__.py +1 -0
- aind_clabe-0.5.0/src/clabe/git_manager/_git.py +258 -0
- aind_clabe-0.5.0/src/clabe/launcher/__init__.py +11 -0
- aind_clabe-0.5.0/src/clabe/launcher/_base.py +690 -0
- aind_clabe-0.5.0/src/clabe/launcher/cli.py +129 -0
- aind_clabe-0.5.0/src/clabe/logging_helper.py +164 -0
- aind_clabe-0.5.0/src/clabe/py.typed +0 -0
- aind_clabe-0.5.0/src/clabe/resource_monitor/__init__.py +9 -0
- aind_clabe-0.5.0/src/clabe/resource_monitor/_base.py +256 -0
- aind_clabe-0.5.0/src/clabe/resource_monitor/_constraints.py +92 -0
- aind_clabe-0.5.0/src/clabe/services.py +358 -0
- aind_clabe-0.5.0/src/clabe/ui/__init__.py +4 -0
- aind_clabe-0.5.0/src/clabe/ui/picker.py +295 -0
- aind_clabe-0.5.0/src/clabe/ui/ui_helper.py +372 -0
- aind_clabe-0.5.0/tests/__init__.py +67 -0
- aind_clabe-0.5.0/tests/assets/bonsai.config +23 -0
- aind_clabe-0.5.0/tests/assets/expected_curriculum_metrics.json +1 -0
- aind_clabe-0.5.0/tests/assets/expected_curriculum_suggestion.json +1 -0
- aind_clabe-0.5.0/tests/test_app.py +171 -0
- aind_clabe-0.5.0/tests/test_behavior_launcher.py +198 -0
- aind_clabe-0.5.0/tests/test_curriculum.py +67 -0
- aind_clabe-0.5.0/tests/test_data_mapper.py +44 -0
- aind_clabe-0.5.0/tests/test_data_transfer.py +333 -0
- aind_clabe-0.5.0/tests/test_launcher.py +88 -0
- aind_clabe-0.5.0/tests/test_logging.py +28 -0
- aind_clabe-0.5.0/tests/test_resource_monitor.py +92 -0
- aind_clabe-0.5.0/tests/test_services.py +65 -0
- aind_clabe-0.5.0/tests/test_slims_picker.py +224 -0
- aind_clabe-0.5.0/tests/test_ui.py +95 -0
- aind_clabe-0.5.0/uv.lock +1699 -0
aind_clabe-0.5.0/.flake8
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: ci/cd
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
pull_request:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
- dev
|
|
9
|
+
push:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
- dev
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
|
|
16
|
+
python-linting:
|
|
17
|
+
uses: AllenNeuralDynamics/Aind.Behavior.GitHubActions/.github/workflows/python-linting.yml@main
|
|
18
|
+
with:
|
|
19
|
+
runs-on: windows-latest
|
|
20
|
+
|
|
21
|
+
aind-behavior-framework-testing:
|
|
22
|
+
uses: AllenNeuralDynamics/Aind.Behavior.GitHubActions/.github/workflows/python-unit-test.yml@main
|
|
23
|
+
with:
|
|
24
|
+
python-version-path: .python-version
|
|
25
|
+
runs-on: windows-latest
|
|
26
|
+
run-coverage: true
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: deploy-docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
deploy:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v6
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.11"
|
|
20
|
+
enable-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Install dependencies
|
|
23
|
+
run: uv sync --extra docs
|
|
24
|
+
|
|
25
|
+
- name: Configure Git user
|
|
26
|
+
run: |
|
|
27
|
+
git config user.name "github-actions[bot]"
|
|
28
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
29
|
+
|
|
30
|
+
- name: Deploy documentation
|
|
31
|
+
run: uv run mkdocs gh-deploy --force
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Tag and Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
publish:
|
|
7
|
+
type: boolean
|
|
8
|
+
description: 'Whether to publish the package to PyPI'
|
|
9
|
+
required: false
|
|
10
|
+
default: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
tag:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: astral-sh/setup-uv@v3
|
|
18
|
+
with:
|
|
19
|
+
enable-cache: true
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
run: uv python install
|
|
22
|
+
|
|
23
|
+
- name: Extract version from __init__.py and package name from pyproject.toml
|
|
24
|
+
id: get_version_and_name
|
|
25
|
+
run: |
|
|
26
|
+
version=$(uv run python -c "import re;
|
|
27
|
+
with open(f'./src/clabe/__init__.py', 'r') as f:
|
|
28
|
+
content = f.read();
|
|
29
|
+
match = re.search(r'__version__\s*=\s*[\'\"]([^\'\"]+)[\'\"]', content);
|
|
30
|
+
print(match.group(1)) if match else exit(1)")
|
|
31
|
+
echo "PACKAGE_NAME=aind-clabe" >> $GITHUB_ENV
|
|
32
|
+
echo "PACKAGE_VERSION=$version" >> $GITHUB_ENV
|
|
33
|
+
shell: bash
|
|
34
|
+
|
|
35
|
+
- name: Create Git tag
|
|
36
|
+
run: |
|
|
37
|
+
git config --global user.name "github-actions[bot]"
|
|
38
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
39
|
+
git tag -a v${{ env.PACKAGE_VERSION }} -m "v${{ env.PACKAGE_VERSION }}"
|
|
40
|
+
git push origin v${{ env.PACKAGE_VERSION }}
|
|
41
|
+
|
|
42
|
+
- name: Create GitHub Release
|
|
43
|
+
uses: softprops/action-gh-release@v2
|
|
44
|
+
with:
|
|
45
|
+
tag_name: v${{ env.PACKAGE_VERSION }}
|
|
46
|
+
name: Release v${{ env.PACKAGE_VERSION }}
|
|
47
|
+
generate_release_notes: true
|
|
48
|
+
|
|
49
|
+
publish:
|
|
50
|
+
needs: tag
|
|
51
|
+
if: github.event_name == 'workflow_dispatch' && inputs.publish || github.event_name == 'push'
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@v4
|
|
55
|
+
- uses: astral-sh/setup-uv@v3
|
|
56
|
+
with:
|
|
57
|
+
enable-cache: true
|
|
58
|
+
|
|
59
|
+
- name: Set up Python
|
|
60
|
+
run: uv python install
|
|
61
|
+
|
|
62
|
+
- name: Build
|
|
63
|
+
run: uv build
|
|
64
|
+
|
|
65
|
+
- name: Publish
|
|
66
|
+
run: uv publish --token ${{ secrets.AIND_PYPI_TOKEN }}
|
|
67
|
+
- name: Publish
|
|
68
|
+
run: uv publish --token ${{ secrets.AIND_PYPI_TOKEN }}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Sphinx documentation
|
|
55
|
+
docs/_build/
|
|
56
|
+
|
|
57
|
+
# Mkdocs
|
|
58
|
+
docs/site/
|
|
59
|
+
site/
|
|
60
|
+
|
|
61
|
+
# Jupyter Notebook
|
|
62
|
+
.ipynb_checkpoints
|
|
63
|
+
|
|
64
|
+
# IPython
|
|
65
|
+
profile_default/
|
|
66
|
+
ipython_config.py
|
|
67
|
+
|
|
68
|
+
# pyenv
|
|
69
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
70
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
71
|
+
# .python-version
|
|
72
|
+
|
|
73
|
+
# vscode
|
|
74
|
+
.vscode/
|
|
75
|
+
|
|
76
|
+
# venv
|
|
77
|
+
.venv/
|
|
78
|
+
|
|
79
|
+
# pycharm
|
|
80
|
+
.idea/
|
|
81
|
+
|
|
82
|
+
local
|
aind_clabe-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Allen Institute for Neural Dynamics
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aind-clabe
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: A library for a minimal framework that can be used to build experimental interfaces.
|
|
5
|
+
Author-email: Bruno Cruz <bruno.cruz@alleninstitute.org>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Documentation, https://allenneuraldynamics.github.io/clabe/
|
|
8
|
+
Project-URL: Repository, https://github.com/AllenNeuralDynamics/clabe/
|
|
9
|
+
Project-URL: Issues, https://github.com/AllenNeuralDynamics/clabe/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/AllenNeuralDynamics/clabe/releases
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: pydantic>=2.7
|
|
19
|
+
Requires-Dist: pydantic-settings
|
|
20
|
+
Requires-Dist: gitpython
|
|
21
|
+
Requires-Dist: semver
|
|
22
|
+
Requires-Dist: rich
|
|
23
|
+
Requires-Dist: aind_behavior_services<1
|
|
24
|
+
Provides-Extra: aind-services
|
|
25
|
+
Requires-Dist: aind-slims-api; extra == "aind-services"
|
|
26
|
+
Requires-Dist: aind-watchdog-service; extra == "aind-services"
|
|
27
|
+
Requires-Dist: aind-data-schema<2; extra == "aind-services"
|
|
28
|
+
Requires-Dist: ms-active-directory; extra == "aind-services"
|
|
29
|
+
Requires-Dist: cryptography; extra == "aind-services"
|
|
30
|
+
Requires-Dist: winkerberos; sys_platform == "win32" and extra == "aind-services"
|
|
31
|
+
Requires-Dist: ldap3; sys_platform == "win32" and extra == "aind-services"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: ruff; extra == "dev"
|
|
34
|
+
Requires-Dist: codespell; extra == "dev"
|
|
35
|
+
Requires-Dist: coverage; extra == "dev"
|
|
36
|
+
Requires-Dist: aind-clabe[aind-services]; extra == "dev"
|
|
37
|
+
Provides-Extra: docs
|
|
38
|
+
Requires-Dist: aind-clabe[aind-services]; extra == "docs"
|
|
39
|
+
Requires-Dist: mkdocs; extra == "docs"
|
|
40
|
+
Requires-Dist: mkdocs-material; extra == "docs"
|
|
41
|
+
Requires-Dist: mkdocstrings[python]; extra == "docs"
|
|
42
|
+
Requires-Dist: pymdown-extensions; extra == "docs"
|
|
43
|
+
Requires-Dist: ruff; extra == "docs"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
<div align="center">
|
|
47
|
+
|
|
48
|
+
<pre>
|
|
49
|
+
██████╗██╗ █████╗ ██████╗ ███████╗
|
|
50
|
+
██╔════╝██║ ██╔══██╗██╔══██╗██╔════╝
|
|
51
|
+
██║ ██║ ███████║██████╔╝█████╗
|
|
52
|
+
██║ ██║ ██╔══██║██╔══██╗██╔══╝
|
|
53
|
+
╚██████╗███████╗██║ ██║██████╔╝███████╗
|
|
54
|
+
╚═════╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝
|
|
55
|
+
|
|
56
|
+
Command-line-interface Launcher for AIND Behavior Experiments
|
|
57
|
+
</pre>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
[](https://pypi.org/project/aind-clabe/)
|
|
64
|
+
[](LICENSE)
|
|
65
|
+
[](https://github.com/astral-sh/ruff)
|
|
66
|
+
[](https://github.com/astral-sh/uv)
|
|
67
|
+
|
|
68
|
+
# clabe
|
|
69
|
+
|
|
70
|
+
A library for building workflows for behavior experiments.
|
|
71
|
+
|
|
72
|
+
> ⚠️ **Caution:**
|
|
73
|
+
> This repository is currently under active development and is subject to frequent changes. Features and APIs may evolve without prior notice.
|
|
74
|
+
|
|
75
|
+
## Installing and Upgrading
|
|
76
|
+
|
|
77
|
+
If you choose to clone the repository, you can install the package by running the following command from the root directory of the repository:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install .
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Otherwise, you can use pip:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install aind-clabe
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Getting started and API usage
|
|
90
|
+
|
|
91
|
+
The library provides a main class "Launcher" that can be used to create a linear workflow for behavior experiments. These workflows rely on modular interfaces that can be used to interact with various components of the experiment and other services.
|
|
92
|
+
Some of these services are specific for AIND:
|
|
93
|
+
|
|
94
|
+
- [aind-data-schema](https://github.com/AllenNeuralDynamics/aind-data-schema)
|
|
95
|
+
- [aind-data-schema-models](https://github.com/AllenNeuralDynamics/aind-data-schema-models)
|
|
96
|
+
- [aind-watchdog-service](https://github.com/AllenNeuralDynamics/aind-watchdog-service)
|
|
97
|
+
- [aind-slims-api](https://github.com/AllenNeuralDynamics/aind-slims-api)
|
|
98
|
+
- [aind-data-mapper](https://github.com/AllenNeuralDynamics/aind-metadata-mapper)
|
|
99
|
+
|
|
100
|
+
We will also try to scope all dependencies of the related to AIND Services to its own optional dependency list in the `./pyproject.toml` file of this repository. Therefore, in order to use this module, you will need to install these optional dependencies by running:
|
|
101
|
+
|
|
102
|
+
```uv sync --extra aind-services```
|
|
103
|
+
|
|
104
|
+
A basic example of how to use the Launcher class can be found in the `examples` directory of this repository.
|
|
105
|
+
|
|
106
|
+
## Contributors
|
|
107
|
+
|
|
108
|
+
Contributions to this repository are welcome! However, please ensure that your code adheres to the recommended DevOps practices below:
|
|
109
|
+
|
|
110
|
+
### Linting
|
|
111
|
+
|
|
112
|
+
We use [ruff](https://docs.astral.sh/ruff/) as our primary linting tool.
|
|
113
|
+
|
|
114
|
+
### Testing
|
|
115
|
+
|
|
116
|
+
Attempt to add tests when new features are added.
|
|
117
|
+
To run the currently available tests, run `uv run -m unittest` from the root of the repository.
|
|
118
|
+
|
|
119
|
+
### Lock files
|
|
120
|
+
|
|
121
|
+
We use [uv](https://docs.astral.sh/uv/) to manage our lock files and therefore encourage everyone to use uv as a package manager as well.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<pre>
|
|
4
|
+
██████╗██╗ █████╗ ██████╗ ███████╗
|
|
5
|
+
██╔════╝██║ ██╔══██╗██╔══██╗██╔════╝
|
|
6
|
+
██║ ██║ ███████║██████╔╝█████╗
|
|
7
|
+
██║ ██║ ██╔══██║██╔══██╗██╔══╝
|
|
8
|
+
╚██████╗███████╗██║ ██║██████╔╝███████╗
|
|
9
|
+
╚═════╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝
|
|
10
|
+
|
|
11
|
+
Command-line-interface Launcher for AIND Behavior Experiments
|
|
12
|
+
</pre>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
[](https://pypi.org/project/aind-clabe/)
|
|
19
|
+
[](LICENSE)
|
|
20
|
+
[](https://github.com/astral-sh/ruff)
|
|
21
|
+
[](https://github.com/astral-sh/uv)
|
|
22
|
+
|
|
23
|
+
# clabe
|
|
24
|
+
|
|
25
|
+
A library for building workflows for behavior experiments.
|
|
26
|
+
|
|
27
|
+
> ⚠️ **Caution:**
|
|
28
|
+
> This repository is currently under active development and is subject to frequent changes. Features and APIs may evolve without prior notice.
|
|
29
|
+
|
|
30
|
+
## Installing and Upgrading
|
|
31
|
+
|
|
32
|
+
If you choose to clone the repository, you can install the package by running the following command from the root directory of the repository:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Otherwise, you can use pip:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install aind-clabe
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Getting started and API usage
|
|
45
|
+
|
|
46
|
+
The library provides a main class "Launcher" that can be used to create a linear workflow for behavior experiments. These workflows rely on modular interfaces that can be used to interact with various components of the experiment and other services.
|
|
47
|
+
Some of these services are specific for AIND:
|
|
48
|
+
|
|
49
|
+
- [aind-data-schema](https://github.com/AllenNeuralDynamics/aind-data-schema)
|
|
50
|
+
- [aind-data-schema-models](https://github.com/AllenNeuralDynamics/aind-data-schema-models)
|
|
51
|
+
- [aind-watchdog-service](https://github.com/AllenNeuralDynamics/aind-watchdog-service)
|
|
52
|
+
- [aind-slims-api](https://github.com/AllenNeuralDynamics/aind-slims-api)
|
|
53
|
+
- [aind-data-mapper](https://github.com/AllenNeuralDynamics/aind-metadata-mapper)
|
|
54
|
+
|
|
55
|
+
We will also try to scope all dependencies of the related to AIND Services to its own optional dependency list in the `./pyproject.toml` file of this repository. Therefore, in order to use this module, you will need to install these optional dependencies by running:
|
|
56
|
+
|
|
57
|
+
```uv sync --extra aind-services```
|
|
58
|
+
|
|
59
|
+
A basic example of how to use the Launcher class can be found in the `examples` directory of this repository.
|
|
60
|
+
|
|
61
|
+
## Contributors
|
|
62
|
+
|
|
63
|
+
Contributions to this repository are welcome! However, please ensure that your code adheres to the recommended DevOps practices below:
|
|
64
|
+
|
|
65
|
+
### Linting
|
|
66
|
+
|
|
67
|
+
We use [ruff](https://docs.astral.sh/ruff/) as our primary linting tool.
|
|
68
|
+
|
|
69
|
+
### Testing
|
|
70
|
+
|
|
71
|
+
Attempt to add tests when new features are added.
|
|
72
|
+
To run the currently available tests, run `uv run -m unittest` from the root of the repository.
|
|
73
|
+
|
|
74
|
+
### Lock files
|
|
75
|
+
|
|
76
|
+
We use [uv](https://docs.astral.sh/uv/) to manage our lock files and therefore encourage everyone to use uv as a package manager as well.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict, List, Union
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
|
|
8
|
+
ROOT_DIR = Path(__file__).parent.parent
|
|
9
|
+
PACKAGE_NAME = "clabe"
|
|
10
|
+
SRC_DIR = ROOT_DIR / "src" / PACKAGE_NAME
|
|
11
|
+
DOCS_DIR = ROOT_DIR / "docs"
|
|
12
|
+
API_DIR = DOCS_DIR / "api"
|
|
13
|
+
MKDOCS_YML = ROOT_DIR / "mkdocs.yml"
|
|
14
|
+
API_LABEL = "API Reference"
|
|
15
|
+
INCLUDE_PRIVATE_MODULES = False
|
|
16
|
+
|
|
17
|
+
TO_COPY = ["assets", "examples", "LICENSE"]
|
|
18
|
+
log = logging.getLogger("mkdocs")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def discover_python_modules(package_root: Path, include_private: bool = False) -> List[str]:
|
|
22
|
+
modules = []
|
|
23
|
+
|
|
24
|
+
def _find_modules(current_path: Path, prefix: str = "") -> None:
|
|
25
|
+
if not current_path.exists() or not current_path.is_dir():
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
for item in current_path.iterdir():
|
|
29
|
+
if not item.is_dir():
|
|
30
|
+
continue
|
|
31
|
+
if not (item / "__init__.py").exists():
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
if item.name.startswith("_") and not include_private:
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
module_name = f"{prefix}{item.name}" if prefix else item.name
|
|
38
|
+
modules.append(module_name)
|
|
39
|
+
|
|
40
|
+
# Recursively search subdirectories for nested modules
|
|
41
|
+
_find_modules(item, f"{module_name}.")
|
|
42
|
+
|
|
43
|
+
_find_modules(package_root)
|
|
44
|
+
return sorted(modules)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def discover_module_files(module_path: Path, include_private: bool = False) -> List[str]:
|
|
48
|
+
files = []
|
|
49
|
+
|
|
50
|
+
def _find_files(current_path: Path, prefix: str = "") -> None:
|
|
51
|
+
if not current_path.exists() or not current_path.is_dir():
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
for item in current_path.iterdir():
|
|
55
|
+
if item.is_file() and item.suffix == ".py":
|
|
56
|
+
if item.name.startswith("_") and not include_private:
|
|
57
|
+
continue
|
|
58
|
+
file_name = f"{prefix}{item.stem}" if prefix else item.stem
|
|
59
|
+
files.append(file_name)
|
|
60
|
+
elif item.is_dir() and not item.name.startswith("__"):
|
|
61
|
+
if item.name.startswith("_") and not include_private:
|
|
62
|
+
continue
|
|
63
|
+
# Recursively search subdirectories
|
|
64
|
+
_find_files(item, f"{prefix}{item.name}." if prefix else f"{item.name}.")
|
|
65
|
+
|
|
66
|
+
_find_files(module_path)
|
|
67
|
+
return sorted(files)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def on_pre_build(config: Dict[str, Any]) -> None:
|
|
71
|
+
"""Mkdocs pre-build hook."""
|
|
72
|
+
for file_or_dir in TO_COPY:
|
|
73
|
+
src: Path = ROOT_DIR / file_or_dir
|
|
74
|
+
dest: Path = DOCS_DIR / file_or_dir
|
|
75
|
+
|
|
76
|
+
if src.exists():
|
|
77
|
+
log.info(f"Copying {file_or_dir} to docs...")
|
|
78
|
+
|
|
79
|
+
if src.is_file():
|
|
80
|
+
print(f"Copying file {src} to {dest}")
|
|
81
|
+
shutil.copy(src, dest)
|
|
82
|
+
else:
|
|
83
|
+
if dest.exists():
|
|
84
|
+
shutil.rmtree(dest)
|
|
85
|
+
shutil.copytree(src, dest)
|
|
86
|
+
log.info(f"{file_or_dir} copied successfully.")
|
|
87
|
+
else:
|
|
88
|
+
log.warning(f"Source: {file_or_dir} not found, skipping.")
|
|
89
|
+
|
|
90
|
+
main()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def generate_api_structure() -> Dict[str, List[Dict[str, str]]]:
|
|
94
|
+
api_structure: Dict[str, List[Dict[str, str]]] = {}
|
|
95
|
+
modules = discover_python_modules(SRC_DIR, INCLUDE_PRIVATE_MODULES)
|
|
96
|
+
|
|
97
|
+
for module_name in modules:
|
|
98
|
+
module_structure: List[Dict[str, str]] = []
|
|
99
|
+
module_path = SRC_DIR / module_name.replace(".", "/")
|
|
100
|
+
|
|
101
|
+
# Add the module's __init__.py as the main module entry
|
|
102
|
+
safe_module_name = module_name.replace(".", "_")
|
|
103
|
+
module_structure.append({module_name: f"api/{safe_module_name}/{safe_module_name}.md"})
|
|
104
|
+
|
|
105
|
+
module_files = discover_module_files(module_path, INCLUDE_PRIVATE_MODULES)
|
|
106
|
+
for file_name in module_files:
|
|
107
|
+
safe_file_name = file_name.replace(".", "_")
|
|
108
|
+
module_structure.append({file_name: f"api/{safe_module_name}/{safe_file_name}.md"})
|
|
109
|
+
|
|
110
|
+
(API_DIR / safe_module_name).mkdir(parents=True, exist_ok=True)
|
|
111
|
+
|
|
112
|
+
with open(DOCS_DIR / f"api/{safe_module_name}/{safe_module_name}.md", "w") as f:
|
|
113
|
+
f.write(f"# {module_name}\n\n")
|
|
114
|
+
f.write(f"::: {PACKAGE_NAME}.{module_name}\n")
|
|
115
|
+
|
|
116
|
+
for file_name in module_files:
|
|
117
|
+
safe_file_name = file_name.replace(".", "_")
|
|
118
|
+
with open(DOCS_DIR / f"api/{safe_module_name}/{safe_file_name}.md", "w") as f:
|
|
119
|
+
f.write(f"# {module_name}.{file_name}\n\n")
|
|
120
|
+
f.write(f"::: {PACKAGE_NAME}.{module_name}.{file_name}\n")
|
|
121
|
+
|
|
122
|
+
api_structure[module_name] = module_structure
|
|
123
|
+
|
|
124
|
+
return api_structure
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def update_mkdocs_yml(api_structure: Dict[str, List[Dict[str, str]]]) -> None:
|
|
128
|
+
with open(MKDOCS_YML, "r") as f:
|
|
129
|
+
config: Dict[str, Any] = yaml.safe_load(f)
|
|
130
|
+
|
|
131
|
+
nav: List[Union[str, Dict[str, Any]]] = config.get("nav", [])
|
|
132
|
+
|
|
133
|
+
for entry in nav:
|
|
134
|
+
if isinstance(entry, dict) and API_LABEL in entry:
|
|
135
|
+
api_ref: List[Union[str, Dict[str, List[Dict[str, str]]]]] = ["api/index.md"]
|
|
136
|
+
|
|
137
|
+
for module_name, module_content in api_structure.items():
|
|
138
|
+
api_ref.append({module_name.capitalize().replace("_", " "): module_content})
|
|
139
|
+
|
|
140
|
+
entry[API_LABEL] = api_ref
|
|
141
|
+
|
|
142
|
+
with open(MKDOCS_YML, "w") as f:
|
|
143
|
+
yaml.dump(config, f, sort_keys=False, default_flow_style=False)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def main() -> None:
|
|
147
|
+
log.info("Regenerating API documentation...")
|
|
148
|
+
|
|
149
|
+
# Generate API structure
|
|
150
|
+
api_structure: Dict[str, List[Dict[str, str]]] = generate_api_structure()
|
|
151
|
+
|
|
152
|
+
# Update mkdocs.yml
|
|
153
|
+
update_mkdocs_yml(api_structure)
|
|
154
|
+
|
|
155
|
+
log.info("API documentation regenerated successfully.")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if __name__ == "__main__":
|
|
159
|
+
main()
|