msw-core 0.2.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.
- msw_core-0.2.0/.forgejo/workflows/ci.yml +41 -0
- msw_core-0.2.0/.githooks/fix_commit_msg.py +52 -0
- msw_core-0.2.0/.github/workflows/ci.yml +41 -0
- msw_core-0.2.0/.github/workflows/release.yml +73 -0
- msw_core-0.2.0/.gitignore +72 -0
- msw_core-0.2.0/.pre-commit-config.yaml +49 -0
- msw_core-0.2.0/LICENSE +0 -0
- msw_core-0.2.0/PKG-INFO +47 -0
- msw_core-0.2.0/README.md +11 -0
- msw_core-0.2.0/VERSION +1 -0
- msw_core-0.2.0/pyproject.toml +112 -0
- msw_core-0.2.0/src/murineshiftwork/_version.py +24 -0
- msw_core-0.2.0/src/murineshiftwork/cli/__init__.py +68 -0
- msw_core-0.2.0/src/murineshiftwork/cli/__main__.py +4 -0
- msw_core-0.2.0/src/murineshiftwork/cli/defaults.py +44 -0
- msw_core-0.2.0/src/murineshiftwork/cli/evaluate.py +541 -0
- msw_core-0.2.0/src/murineshiftwork/cli/execute.py +460 -0
- msw_core-0.2.0/src/murineshiftwork/cli/parser.py +734 -0
- msw_core-0.2.0/src/murineshiftwork/cli/post.py +113 -0
- msw_core-0.2.0/src/murineshiftwork/cli/preflight.py +75 -0
- msw_core-0.2.0/src/murineshiftwork/cli/tasks.py +243 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/__init__.py +0 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/__init__.py +19 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/actions.py +96 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/device.py +54 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/factory.py +205 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/override.py +130 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/sim.py +150 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/ttl.py +35 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/user_settings.py +22 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/user_settings_8port.py +7 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/bpod/valve.py +79 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/camera/__init__.py +3 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/camera/client.py +303 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/host_session.py +70 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/manager.py +98 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/pulsepal/__init__.py +1 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/pulsepal/device.py +62 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/scale.py +182 -0
- msw_core-0.2.0/src/murineshiftwork/hardware/stimulation.py +507 -0
- msw_core-0.2.0/src/murineshiftwork/hooks/__init__.py +167 -0
- msw_core-0.2.0/src/murineshiftwork/logic/__init__.py +0 -0
- msw_core-0.2.0/src/murineshiftwork/logic/barcode.py +59 -0
- msw_core-0.2.0/src/murineshiftwork/logic/calibration.py +608 -0
- msw_core-0.2.0/src/murineshiftwork/logic/config/__init__.py +43 -0
- msw_core-0.2.0/src/murineshiftwork/logic/config/_defaults.py +15 -0
- msw_core-0.2.0/src/murineshiftwork/logic/config/ini.py +75 -0
- msw_core-0.2.0/src/murineshiftwork/logic/config/io.py +241 -0
- msw_core-0.2.0/src/murineshiftwork/logic/config/models.py +399 -0
- msw_core-0.2.0/src/murineshiftwork/logic/log.py +139 -0
- msw_core-0.2.0/src/murineshiftwork/logic/machine_config.py +171 -0
- msw_core-0.2.0/src/murineshiftwork/logic/maths.py +44 -0
- msw_core-0.2.0/src/murineshiftwork/logic/misc.py +71 -0
- msw_core-0.2.0/src/murineshiftwork/logic/paths.py +55 -0
- msw_core-0.2.0/src/murineshiftwork/logic/sounds.py +267 -0
- msw_core-0.2.0/src/murineshiftwork/logic/task_process.py +498 -0
- msw_core-0.2.0/src/murineshiftwork/logic/task_settings.py +99 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags-ignore: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
name: Lint
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
if: "!startsWith(github.event.head_commit.message, 'bump:')"
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- uses: astral-sh/setup-uv@v7
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
|
|
27
|
+
- name: Run pre-commit (ruff + commitizen + gitleaks)
|
|
28
|
+
run: uvx pre-commit run --all-files --show-diff-on-failure
|
|
29
|
+
|
|
30
|
+
ci:
|
|
31
|
+
name: CI
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
needs: [lint]
|
|
34
|
+
if: always()
|
|
35
|
+
steps:
|
|
36
|
+
- name: Check required jobs
|
|
37
|
+
run: |
|
|
38
|
+
if [[ "${{ needs.lint.result }}" != "success" && "${{ needs.lint.result }}" != "skipped" ]]; then
|
|
39
|
+
echo "lint failed" && exit 1
|
|
40
|
+
fi
|
|
41
|
+
echo "All required checks passed."
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Auto-fix mechanical commit message formatting issues before linting."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def fix(path: Path) -> None:
|
|
9
|
+
text = path.read_text()
|
|
10
|
+
lines = text.splitlines(keepends=True)
|
|
11
|
+
|
|
12
|
+
fixed = []
|
|
13
|
+
for i, line in enumerate(lines):
|
|
14
|
+
if line.startswith("#"):
|
|
15
|
+
fixed.append(line)
|
|
16
|
+
continue
|
|
17
|
+
|
|
18
|
+
# Strip trailing whitespace on every line
|
|
19
|
+
line = line.rstrip() + "\n"
|
|
20
|
+
|
|
21
|
+
# Strip leading whitespace from subject line (first content line)
|
|
22
|
+
if i == 0 or all(ln.startswith("#") for ln in lines[:i]):
|
|
23
|
+
line = line.lstrip()
|
|
24
|
+
|
|
25
|
+
# Convert hard tabs to four spaces
|
|
26
|
+
line = line.replace("\t", " ")
|
|
27
|
+
|
|
28
|
+
fixed.append(line)
|
|
29
|
+
|
|
30
|
+
# Ensure blank line between subject and body if body is present
|
|
31
|
+
first = next((i for i, ln in enumerate(fixed) if not ln.startswith("#")), None)
|
|
32
|
+
if first is not None and first + 1 < len(fixed):
|
|
33
|
+
next_content = next(
|
|
34
|
+
(
|
|
35
|
+
i
|
|
36
|
+
for i, ln in enumerate(fixed[first + 1 :], first + 1)
|
|
37
|
+
if not ln.startswith("#")
|
|
38
|
+
),
|
|
39
|
+
None,
|
|
40
|
+
)
|
|
41
|
+
if (
|
|
42
|
+
next_content is not None
|
|
43
|
+
and next_content == first + 1
|
|
44
|
+
and fixed[first + 1].strip()
|
|
45
|
+
):
|
|
46
|
+
fixed.insert(first + 1, "\n")
|
|
47
|
+
|
|
48
|
+
path.write_text("".join(fixed))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
fix(Path(sys.argv[1]))
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
tags-ignore: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
name: Lint
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
if: "!startsWith(github.event.head_commit.message, 'bump:')"
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v6
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
|
|
23
|
+
- uses: astral-sh/setup-uv@v7
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
|
|
27
|
+
- name: Run pre-commit (ruff + commitizen + gitleaks)
|
|
28
|
+
run: uvx pre-commit run --all-files --show-diff-on-failure
|
|
29
|
+
|
|
30
|
+
ci:
|
|
31
|
+
name: CI
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
needs: [lint]
|
|
34
|
+
if: always()
|
|
35
|
+
steps:
|
|
36
|
+
- name: Check required jobs
|
|
37
|
+
run: |
|
|
38
|
+
if [[ "${{ needs.lint.result }}" != "success" && "${{ needs.lint.result }}" != "skipped" ]]; then
|
|
39
|
+
echo "lint failed" && exit 1
|
|
40
|
+
fi
|
|
41
|
+
echo "All required checks passed."
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
publish_only:
|
|
9
|
+
description: 'Skip version bump and publish the current VERSION tag'
|
|
10
|
+
type: boolean
|
|
11
|
+
default: false
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
release:
|
|
19
|
+
name: Bump, build, publish
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
if: "!startsWith(github.event.head_commit.message, 'bump:')"
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
|
|
27
|
+
- name: Configure git identity
|
|
28
|
+
run: |
|
|
29
|
+
git config user.name "github-actions[bot]"
|
|
30
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
31
|
+
|
|
32
|
+
- uses: astral-sh/setup-uv@v7
|
|
33
|
+
with:
|
|
34
|
+
python-version: "3.11"
|
|
35
|
+
|
|
36
|
+
- name: Bump version
|
|
37
|
+
id: bump
|
|
38
|
+
if: inputs.publish_only != true
|
|
39
|
+
run: |
|
|
40
|
+
uvx --from commitizen cz bump --yes || EXIT=$?
|
|
41
|
+
if [ "${EXIT:-0}" -eq 21 ]; then
|
|
42
|
+
echo "No bumpable commits since last tag — skipping."
|
|
43
|
+
echo "skipped=true" >> "$GITHUB_OUTPUT"
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
[ "${EXIT:-0}" -eq 0 ] || exit $EXIT
|
|
47
|
+
VERSION=$(cat VERSION)
|
|
48
|
+
git push origin HEAD:main
|
|
49
|
+
git push origin "v${VERSION}"
|
|
50
|
+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
51
|
+
|
|
52
|
+
- name: Read version (publish-only mode)
|
|
53
|
+
id: read_version
|
|
54
|
+
if: inputs.publish_only == true
|
|
55
|
+
run: |
|
|
56
|
+
VERSION=$(cat VERSION)
|
|
57
|
+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
58
|
+
|
|
59
|
+
- name: Build
|
|
60
|
+
if: steps.bump.outputs.skipped != 'true'
|
|
61
|
+
run: uv build
|
|
62
|
+
|
|
63
|
+
- name: Create GitHub release
|
|
64
|
+
if: steps.bump.outputs.skipped != 'true' && inputs.publish_only != true
|
|
65
|
+
uses: softprops/action-gh-release@v3
|
|
66
|
+
with:
|
|
67
|
+
tag_name: "v${{ steps.bump.outputs.version || steps.read_version.outputs.version }}"
|
|
68
|
+
files: dist/*
|
|
69
|
+
generate_release_notes: false
|
|
70
|
+
|
|
71
|
+
- name: Publish to PyPI
|
|
72
|
+
if: steps.bump.outputs.skipped != 'true'
|
|
73
|
+
run: uv publish --trusted-publishing automatic
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.so
|
|
7
|
+
*.egg
|
|
8
|
+
*.egg-info/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
eggs/
|
|
12
|
+
parts/
|
|
13
|
+
var/
|
|
14
|
+
sdist/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
.installed.cfg
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
wheels/
|
|
20
|
+
|
|
21
|
+
# Virtual environments
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
env/
|
|
25
|
+
ENV/
|
|
26
|
+
.python-version
|
|
27
|
+
|
|
28
|
+
# uv
|
|
29
|
+
uv.lock
|
|
30
|
+
|
|
31
|
+
# Distribution / packaging
|
|
32
|
+
MANIFEST
|
|
33
|
+
|
|
34
|
+
# Testing
|
|
35
|
+
.pytest_cache/
|
|
36
|
+
.coverage
|
|
37
|
+
.coverage.*
|
|
38
|
+
coverage.xml
|
|
39
|
+
htmlcov/
|
|
40
|
+
*.cover
|
|
41
|
+
.hypothesis/
|
|
42
|
+
|
|
43
|
+
# Type checking
|
|
44
|
+
.mypy_cache/
|
|
45
|
+
.dmypy.json
|
|
46
|
+
dmypy.json
|
|
47
|
+
.pytype/
|
|
48
|
+
.pyre/
|
|
49
|
+
|
|
50
|
+
# Build artifacts (hatch-vcs generated)
|
|
51
|
+
src/*/_version.py
|
|
52
|
+
|
|
53
|
+
# Jupyter
|
|
54
|
+
.ipynb_checkpoints
|
|
55
|
+
*.ipynb
|
|
56
|
+
|
|
57
|
+
# IDEs
|
|
58
|
+
.idea/
|
|
59
|
+
.vscode/
|
|
60
|
+
*.swp
|
|
61
|
+
*.swo
|
|
62
|
+
*~
|
|
63
|
+
|
|
64
|
+
# OS
|
|
65
|
+
.DS_Store
|
|
66
|
+
Thumbs.db
|
|
67
|
+
|
|
68
|
+
# Secrets / local config
|
|
69
|
+
.env
|
|
70
|
+
.env.*
|
|
71
|
+
!.env.example
|
|
72
|
+
site/
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-merge-conflict
|
|
10
|
+
- id: mixed-line-ending
|
|
11
|
+
|
|
12
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
13
|
+
rev: v0.15.14
|
|
14
|
+
hooks:
|
|
15
|
+
- id: ruff
|
|
16
|
+
args: [--fix]
|
|
17
|
+
- id: ruff-format
|
|
18
|
+
|
|
19
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
20
|
+
rev: v2.1.0
|
|
21
|
+
hooks:
|
|
22
|
+
- id: mypy
|
|
23
|
+
additional_dependencies: []
|
|
24
|
+
|
|
25
|
+
# commit-msg stage: fix first, then validate
|
|
26
|
+
- repo: local
|
|
27
|
+
hooks:
|
|
28
|
+
- id: fix-commit-msg
|
|
29
|
+
name: Auto-fix commit message formatting
|
|
30
|
+
language: system
|
|
31
|
+
entry: python3 .githooks/fix_commit_msg.py
|
|
32
|
+
stages: [commit-msg]
|
|
33
|
+
|
|
34
|
+
- repo: https://github.com/commitizen-tools/commitizen
|
|
35
|
+
rev: v4.16.2
|
|
36
|
+
hooks:
|
|
37
|
+
- id: commitizen
|
|
38
|
+
stages: [commit-msg]
|
|
39
|
+
|
|
40
|
+
- repo: https://github.com/jorisroovers/gitlint
|
|
41
|
+
rev: v0.19.1
|
|
42
|
+
hooks:
|
|
43
|
+
- id: gitlint
|
|
44
|
+
stages: [commit-msg]
|
|
45
|
+
|
|
46
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
47
|
+
rev: v8.30.0
|
|
48
|
+
hooks:
|
|
49
|
+
- id: gitleaks
|
msw_core-0.2.0/LICENSE
ADDED
|
File without changes
|
msw_core-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: msw-core
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Murine Shift Work acquisition stack: CLI, hardware, hooks, and logic.
|
|
5
|
+
Project-URL: Homepage, https://github.com/MurineShiftWork/msw-core
|
|
6
|
+
Project-URL: Issue Tracker, https://github.com/MurineShiftWork/msw-core/issues
|
|
7
|
+
Author-email: "Lars B. Rollik" <lars@rollik.me>
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
|
+
Requires-Dist: matplotlib
|
|
11
|
+
Requires-Dist: msw-io>=1.0.2
|
|
12
|
+
Requires-Dist: msw-plugin-api>=0.1.0
|
|
13
|
+
Requires-Dist: numpy
|
|
14
|
+
Requires-Dist: one-axis-stage
|
|
15
|
+
Requires-Dist: pandas
|
|
16
|
+
Requires-Dist: pybpod-api
|
|
17
|
+
Requires-Dist: pydantic>=2.0
|
|
18
|
+
Requires-Dist: pyserial
|
|
19
|
+
Requires-Dist: pyyaml
|
|
20
|
+
Requires-Dist: rich
|
|
21
|
+
Requires-Dist: scipy
|
|
22
|
+
Requires-Dist: seaborn
|
|
23
|
+
Requires-Dist: sounddevice
|
|
24
|
+
Requires-Dist: ttl-barcoder>=0.4.1
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: commitizen; extra == 'dev'
|
|
27
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
28
|
+
Requires-Dist: pre-commit; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
31
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
32
|
+
Provides-Extra: docs
|
|
33
|
+
Requires-Dist: mkdocs-material; extra == 'docs'
|
|
34
|
+
Requires-Dist: mkdocstrings[python]; extra == 'docs'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# msw-core
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/msw-core)
|
|
40
|
+
|
|
41
|
+
Murine Shift Work acquisition stack: CLI, hardware drivers, session hooks, and task logic.
|
|
42
|
+
|
|
43
|
+
Provides the `murineshiftwork.cli`, `murineshiftwork.hardware`, `murineshiftwork.hooks`,
|
|
44
|
+
and `murineshiftwork.logic` namespace sub-packages. Installs the `msw` / `murineshiftwork`
|
|
45
|
+
CLI entry points.
|
|
46
|
+
|
|
47
|
+
Part of the [MurineShiftWork](https://github.com/MurineShiftWork) namespace package ecosystem.
|
msw_core-0.2.0/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# msw-core
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/msw-core)
|
|
4
|
+
|
|
5
|
+
Murine Shift Work acquisition stack: CLI, hardware drivers, session hooks, and task logic.
|
|
6
|
+
|
|
7
|
+
Provides the `murineshiftwork.cli`, `murineshiftwork.hardware`, `murineshiftwork.hooks`,
|
|
8
|
+
and `murineshiftwork.logic` namespace sub-packages. Installs the `msw` / `murineshiftwork`
|
|
9
|
+
CLI entry points.
|
|
10
|
+
|
|
11
|
+
Part of the [MurineShiftWork](https://github.com/MurineShiftWork) namespace package ecosystem.
|
msw_core-0.2.0/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.0
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "msw-core"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Murine Shift Work acquisition stack: CLI, hardware, hooks, and logic."
|
|
9
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "Lars B. Rollik", email = "lars@rollik.me" },
|
|
13
|
+
]
|
|
14
|
+
requires-python = ">=3.12"
|
|
15
|
+
dependencies = [
|
|
16
|
+
"msw-io>=1.0.2",
|
|
17
|
+
"msw-plugin-api>=0.1.0",
|
|
18
|
+
"ttl-barcoder>=0.4.1",
|
|
19
|
+
"pydantic>=2.0",
|
|
20
|
+
"rich",
|
|
21
|
+
"pyyaml",
|
|
22
|
+
"numpy",
|
|
23
|
+
"scipy",
|
|
24
|
+
"pandas",
|
|
25
|
+
"matplotlib",
|
|
26
|
+
"seaborn",
|
|
27
|
+
"pyserial",
|
|
28
|
+
"pybpod-api",
|
|
29
|
+
"one-axis-stage",
|
|
30
|
+
"sounddevice",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/MurineShiftWork/msw-core"
|
|
35
|
+
"Issue Tracker" = "https://github.com/MurineShiftWork/msw-core/issues"
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
murineshiftwork = "murineshiftwork.cli:run_cli"
|
|
39
|
+
msw = "murineshiftwork.cli:run_cli"
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
dev = [
|
|
43
|
+
"commitizen",
|
|
44
|
+
"pytest>=8",
|
|
45
|
+
"pytest-cov",
|
|
46
|
+
"pre-commit",
|
|
47
|
+
"mypy",
|
|
48
|
+
"types-PyYAML",
|
|
49
|
+
]
|
|
50
|
+
docs = [
|
|
51
|
+
"mkdocs-material",
|
|
52
|
+
"mkdocstrings[python]",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.hatch.version]
|
|
56
|
+
source = "vcs"
|
|
57
|
+
fallback-version = "0.1.0"
|
|
58
|
+
|
|
59
|
+
[tool.hatch.build.hooks.vcs]
|
|
60
|
+
version-file = "src/murineshiftwork/_version.py"
|
|
61
|
+
|
|
62
|
+
[tool.hatch.build.targets.wheel]
|
|
63
|
+
packages = ["src/murineshiftwork"]
|
|
64
|
+
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
# Pytest
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
addopts = "--cov=src/murineshiftwork --durations=0"
|
|
72
|
+
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
# Ruff
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
[tool.ruff]
|
|
78
|
+
line-length = 88
|
|
79
|
+
indent-width = 4
|
|
80
|
+
src = ["src"]
|
|
81
|
+
|
|
82
|
+
[tool.ruff.lint]
|
|
83
|
+
select = ["E", "W", "F", "I", "B", "N", "UP", "SIM", "PTH"]
|
|
84
|
+
ignore = ["E501", "B017", "N817"]
|
|
85
|
+
fixable = ["ALL"]
|
|
86
|
+
|
|
87
|
+
[tool.ruff.format]
|
|
88
|
+
quote-style = "double"
|
|
89
|
+
indent-style = "space"
|
|
90
|
+
line-ending = "auto"
|
|
91
|
+
docstring-code-format = true
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# Mypy
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
[tool.mypy]
|
|
98
|
+
python_version = "3.12"
|
|
99
|
+
warn_return_any = false
|
|
100
|
+
ignore_missing_imports = true
|
|
101
|
+
|
|
102
|
+
# ---------------------------------------------------------------------------
|
|
103
|
+
# Commitizen
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
[tool.commitizen]
|
|
107
|
+
name = "cz_conventional_commits"
|
|
108
|
+
version_provider = "commitizen"
|
|
109
|
+
version = "0.2.0"
|
|
110
|
+
tag_format = "v$version"
|
|
111
|
+
update_changelog_on_bump = false
|
|
112
|
+
version_files = ["VERSION"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.2.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from murineshiftwork.cli.evaluate import evaluate_args
|
|
5
|
+
from murineshiftwork.cli.parser import parse_args
|
|
6
|
+
from murineshiftwork.hardware.bpod import patch_user_settings
|
|
7
|
+
from murineshiftwork.logic.log import patch_logging_levels
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _print_run_banner():
|
|
11
|
+
from importlib.metadata import version as _v
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
ver = _v("murineshiftwork")
|
|
15
|
+
except Exception:
|
|
16
|
+
ver = "unknown"
|
|
17
|
+
print(f"msw {ver} | © Lars B. Rollik | PolyForm Internal Use 1.0.0")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def run_cli(*args):
|
|
21
|
+
"""Command line interface for Murine Shift Work."""
|
|
22
|
+
patch_logging_levels()
|
|
23
|
+
patch_user_settings()
|
|
24
|
+
|
|
25
|
+
if not args:
|
|
26
|
+
args = sys.argv[1:]
|
|
27
|
+
|
|
28
|
+
if len(args) > 0 and not isinstance(args[0], str):
|
|
29
|
+
args = args[0]
|
|
30
|
+
|
|
31
|
+
if len(args) > 0 and str(args[0]).endswith(".py"):
|
|
32
|
+
_, args = args[0], args[1:]
|
|
33
|
+
|
|
34
|
+
if len(args) <= 1:
|
|
35
|
+
args = args + ["-h"]
|
|
36
|
+
|
|
37
|
+
args_dict = parse_args(args=args)
|
|
38
|
+
|
|
39
|
+
# These subcommands bypass evaluate_args (no hardware/subject/task context needed)
|
|
40
|
+
if args_dict.get("command") in (
|
|
41
|
+
"init",
|
|
42
|
+
"setup",
|
|
43
|
+
"subject",
|
|
44
|
+
"calibration",
|
|
45
|
+
"action",
|
|
46
|
+
"tasks",
|
|
47
|
+
"post",
|
|
48
|
+
):
|
|
49
|
+
args_dict["func"](**args_dict)
|
|
50
|
+
logging.debug("EXITING CLI.")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
if args_dict.get("command") == "run":
|
|
54
|
+
_print_run_banner()
|
|
55
|
+
|
|
56
|
+
args_dict = evaluate_args(args_dict=args_dict)
|
|
57
|
+
|
|
58
|
+
if "exit_flag" in args_dict:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
# Call module
|
|
62
|
+
args_dict["func"](**args_dict)
|
|
63
|
+
|
|
64
|
+
logging.debug("EXITING CLI.")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
run_cli()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Module-load-time defaults for the CLI.
|
|
2
|
+
|
|
3
|
+
Factored out of evaluate.py so parser.py can import them without importing the
|
|
4
|
+
full evaluate pipeline, avoiding any risk of a circular dependency.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from murineshiftwork.cli.tasks import list_available_tasks
|
|
10
|
+
from murineshiftwork.logic.machine_config import (
|
|
11
|
+
resolve_config_dir,
|
|
12
|
+
resolve_data_dir,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
default_out_path = resolve_data_dir()
|
|
16
|
+
default_config_dir = resolve_config_dir()
|
|
17
|
+
|
|
18
|
+
CALIBRATION_FILE_PATH = Path("~/.murineshiftwork").expanduser()
|
|
19
|
+
DEFAULT_CALIBRATION_FILE_LIQUID = str(
|
|
20
|
+
CALIBRATION_FILE_PATH / "calibration.liquid.default.csv"
|
|
21
|
+
)
|
|
22
|
+
DEFAULT_CALIBRATION_FILE_SOUND = "calibration.sound.default.csv"
|
|
23
|
+
DEFAULT_CALIBRATION_FILE_STAGE = str(
|
|
24
|
+
CALIBRATION_FILE_PATH / "calibration.stage.default.yaml"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _build_task_list() -> str:
|
|
29
|
+
all_tasks = list_available_tasks()
|
|
30
|
+
main = sorted(t for t in all_tasks if not t.startswith("_"))
|
|
31
|
+
calibration = sorted(t for t in all_tasks if t.startswith("_calibration"))
|
|
32
|
+
tests = sorted(t for t in all_tasks if t.startswith("_test"))
|
|
33
|
+
lines = []
|
|
34
|
+
for heading, group in (
|
|
35
|
+
("Tasks", main),
|
|
36
|
+
("Calibration", calibration),
|
|
37
|
+
("Tests", tests),
|
|
38
|
+
):
|
|
39
|
+
lines.append(f" {heading}:")
|
|
40
|
+
lines.extend(f" - {t}" for t in group)
|
|
41
|
+
return "\n".join(lines)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
available_tasks = _build_task_list()
|