mesofield 0.3.2b0__py3-none-any.whl
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.
- docs/_static/custom.css +40 -0
- docs/_static/favicon.png +0 -0
- docs/_static/logo.png +0 -0
- docs/api/index.md +70 -0
- docs/conf.py +200 -0
- docs/developer_guide.md +303 -0
- docs/index.md +25 -0
- docs/tutorial.md +4 -0
- docs/user_guide.md +172 -0
- examples/teensy_pulse_generator.py +320 -0
- experiments/pipeline_demo/experiment.json +24 -0
- experiments/pipeline_demo/hardware.yaml +23 -0
- experiments/pipeline_demo/procedure.py +50 -0
- experiments/two_cam_demo/experiment.json +24 -0
- experiments/two_cam_demo/hardware.yaml +58 -0
- experiments/two_cam_demo/load_dataset.py +213 -0
- experiments/two_cam_demo/procedure.py +87 -0
- external/video-codecs/openh264-1.8.0-win64.dll +0 -0
- mesofield/__init__.py +45 -0
- mesofield/__main__.py +11 -0
- mesofield/_version.py +24 -0
- mesofield/base.py +750 -0
- mesofield/cli/__init__.py +57 -0
- mesofield/cli/_richhelp.py +100 -0
- mesofield/cli/acquire.py +254 -0
- mesofield/cli/datakit.py +165 -0
- mesofield/cli/process.py +376 -0
- mesofield/cli/rig.py +108 -0
- mesofield/cli/tools.py +347 -0
- mesofield/config.py +751 -0
- mesofield/data/__init__.py +23 -0
- mesofield/data/batch.py +633 -0
- mesofield/data/manager.py +388 -0
- mesofield/data/writer.py +289 -0
- mesofield/datakit/__init__.py +44 -0
- mesofield/datakit/__main__.py +35 -0
- mesofield/datakit/_utils/_logger.py +5 -0
- mesofield/datakit/_version.py +141 -0
- mesofield/datakit/config.py +50 -0
- mesofield/datakit/core.py +783 -0
- mesofield/datakit/datamodel.py +200 -0
- mesofield/datakit/discover.py +124 -0
- mesofield/datakit/explore.py +651 -0
- mesofield/datakit/notebooks/pupil_dlc.ipynb +2445 -0
- mesofield/datakit/profile.py +535 -0
- mesofield/datakit/shell.py +83 -0
- mesofield/datakit/sources/__init__.py +65 -0
- mesofield/datakit/sources/analysis/mesomap.py +194 -0
- mesofield/datakit/sources/analysis/mesoscope.py +77 -0
- mesofield/datakit/sources/analysis/pupil.py +246 -0
- mesofield/datakit/sources/behavior/__init__.py +0 -0
- mesofield/datakit/sources/behavior/dataqueue.py +281 -0
- mesofield/datakit/sources/behavior/psychopy.py +364 -0
- mesofield/datakit/sources/behavior/treadmill.py +323 -0
- mesofield/datakit/sources/behavior/wheel.py +277 -0
- mesofield/datakit/sources/camera/mesoscope.py +32 -0
- mesofield/datakit/sources/camera/metadata_json.py +130 -0
- mesofield/datakit/sources/camera/pupil.py +28 -0
- mesofield/datakit/sources/camera/suite2p.py +547 -0
- mesofield/datakit/sources/register.py +204 -0
- mesofield/datakit/sources/session/config.py +130 -0
- mesofield/datakit/sources/session/notes.py +63 -0
- mesofield/datakit/sources/session/timestamps.py +58 -0
- mesofield/datakit/timeline.py +306 -0
- mesofield/devices/__init__.py +42 -0
- mesofield/devices/base.py +498 -0
- mesofield/devices/base_camera.py +295 -0
- mesofield/devices/cameras.py +740 -0
- mesofield/devices/daq.py +151 -0
- mesofield/devices/encoder.py +384 -0
- mesofield/devices/mocks.py +275 -0
- mesofield/devices/psychopy_device.py +455 -0
- mesofield/devices/subprocesses/__init__.py +0 -0
- mesofield/devices/subprocesses/psychopy.py +133 -0
- mesofield/devices/treadmill.py +318 -0
- mesofield/engines.py +380 -0
- mesofield/gui/Mesofield_icon.png +0 -0
- mesofield/gui/__init__.py +76 -0
- mesofield/gui/config_wizard.py +724 -0
- mesofield/gui/controller.py +535 -0
- mesofield/gui/dynamic_controller.py +78 -0
- mesofield/gui/maingui.py +427 -0
- mesofield/gui/mdagui.py +285 -0
- mesofield/gui/qt_device_adapter.py +109 -0
- mesofield/gui/speedplotter.py +152 -0
- mesofield/gui/theme.py +445 -0
- mesofield/gui/tiff_viewer.py +1050 -0
- mesofield/gui/viewer.py +691 -0
- mesofield/hardware.py +549 -0
- mesofield/playback.py +1298 -0
- mesofield/processing/__init__.py +12 -0
- mesofield/processing/runner.py +237 -0
- mesofield/processors/__init__.py +13 -0
- mesofield/processors/base.py +287 -0
- mesofield/processors/frame_mean.py +19 -0
- mesofield/protocols.py +378 -0
- mesofield/scaffold/__init__.py +34 -0
- mesofield/scaffold/experiment.py +400 -0
- mesofield/scaffold/rigs.py +121 -0
- mesofield/signals.py +85 -0
- mesofield/utils/__init__.py +0 -0
- mesofield/utils/_logger.py +156 -0
- mesofield/utils/retrofit.py +309 -0
- mesofield/utils/utils.py +217 -0
- mesofield-0.3.2b0.dist-info/METADATA +178 -0
- mesofield-0.3.2b0.dist-info/RECORD +111 -0
- mesofield-0.3.2b0.dist-info/WHEEL +5 -0
- mesofield-0.3.2b0.dist-info/entry_points.txt +2 -0
- mesofield-0.3.2b0.dist-info/licenses/LICENSE +21 -0
- mesofield-0.3.2b0.dist-info/top_level.txt +6 -0
- scripts/bench_frame_processor.py +103 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Datakit package entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
if os.getenv("DATAKIT_SAFE_MODE") == "1":
|
|
8
|
+
for var in ("OMP_NUM_THREADS", "MKL_NUM_THREADS", "OPENBLAS_NUM_THREADS",
|
|
9
|
+
"NUMEXPR_NUM_THREADS", "VECLIB_MAXIMUM_THREADS"):
|
|
10
|
+
os.environ.setdefault(var, "1")
|
|
11
|
+
|
|
12
|
+
from ._utils._logger import get_logger
|
|
13
|
+
|
|
14
|
+
logger = get_logger("datakit")
|
|
15
|
+
|
|
16
|
+
from ._version import __version__, build_meta, get_version # noqa: E402
|
|
17
|
+
from .config import settings # noqa: E402
|
|
18
|
+
from .core import Dataset, inspect_sources, load, load_dataset, load_path # noqa: E402
|
|
19
|
+
from .datamodel import LoadedStream # noqa: E402
|
|
20
|
+
from .explore import explore # noqa: E402
|
|
21
|
+
from .profile import MaterializedMemoryReport, profile_materialized # noqa: E402
|
|
22
|
+
from .shell import open_shell # noqa: E402
|
|
23
|
+
from .sources.register import DataSource, LoadContext # noqa: E402
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"Dataset",
|
|
27
|
+
"DataSource",
|
|
28
|
+
"LoadContext",
|
|
29
|
+
"LoadedStream",
|
|
30
|
+
"MaterializedMemoryReport",
|
|
31
|
+
"__version__",
|
|
32
|
+
"build_meta",
|
|
33
|
+
"explore",
|
|
34
|
+
"get_logger",
|
|
35
|
+
"get_version",
|
|
36
|
+
"inspect_sources",
|
|
37
|
+
"load",
|
|
38
|
+
"load_dataset",
|
|
39
|
+
"load_path",
|
|
40
|
+
"logger",
|
|
41
|
+
"open_shell",
|
|
42
|
+
"profile_materialized",
|
|
43
|
+
"settings",
|
|
44
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""``python -m mesofield.datakit`` — open an interactive datakit shell.
|
|
2
|
+
|
|
3
|
+
Thin wrapper around :func:`mesofield.datakit.shell.open_shell`; the same
|
|
4
|
+
behaviour is exposed as ``mesofield datakit shell``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from .shell import open_shell
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main() -> int:
|
|
16
|
+
parser = argparse.ArgumentParser(prog="python -m mesofield.datakit")
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"target",
|
|
19
|
+
type=Path,
|
|
20
|
+
nargs="?",
|
|
21
|
+
default=None,
|
|
22
|
+
help="Experiment directory, or a materialized .pkl/.h5 dataset file. "
|
|
23
|
+
"Omit to open a bare datakit shell.",
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--hdf-key",
|
|
27
|
+
default="dataset",
|
|
28
|
+
help="HDF5 key to read when target is an .h5/.hdf5 file (default: dataset).",
|
|
29
|
+
)
|
|
30
|
+
args = parser.parse_args()
|
|
31
|
+
return open_shell(args.target, hdf_key=args.hdf_key)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Version + provenance metadata for ``datakit``.
|
|
2
|
+
|
|
3
|
+
This module resolves the running package's version from git tags when the
|
|
4
|
+
package is being executed from a working tree, and falls back to installed
|
|
5
|
+
package metadata or the on-disk ``VERSION`` file otherwise. The
|
|
6
|
+
``build_meta()`` helper produces a small dictionary that is embedded into
|
|
7
|
+
materialized datasets so that pickled artefacts can be traced back to the
|
|
8
|
+
exact source revision that produced them.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import datetime as _dt
|
|
14
|
+
import platform as _platform
|
|
15
|
+
import re
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
from functools import lru_cache
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Dict, Optional
|
|
21
|
+
|
|
22
|
+
_PACKAGE_NAME = "datakit"
|
|
23
|
+
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
24
|
+
_VERSION_FILE = _REPO_ROOT / "VERSION"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _run_git(*args: str) -> Optional[str]:
|
|
28
|
+
try:
|
|
29
|
+
out = subprocess.run(
|
|
30
|
+
["git", "-C", str(_REPO_ROOT), *args],
|
|
31
|
+
capture_output=True,
|
|
32
|
+
text=True,
|
|
33
|
+
timeout=2.0,
|
|
34
|
+
check=False,
|
|
35
|
+
)
|
|
36
|
+
except (FileNotFoundError, subprocess.SubprocessError, OSError):
|
|
37
|
+
return None
|
|
38
|
+
if out.returncode != 0:
|
|
39
|
+
return None
|
|
40
|
+
value = out.stdout.strip()
|
|
41
|
+
return value or None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _installed_version() -> Optional[str]:
|
|
45
|
+
try:
|
|
46
|
+
from importlib.metadata import PackageNotFoundError, version as _v
|
|
47
|
+
except ImportError: # pragma: no cover - py<3.8
|
|
48
|
+
return None
|
|
49
|
+
try:
|
|
50
|
+
return _v(_PACKAGE_NAME)
|
|
51
|
+
except PackageNotFoundError:
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _file_version() -> Optional[str]:
|
|
56
|
+
try:
|
|
57
|
+
return _VERSION_FILE.read_text(encoding="utf-8").strip() or None
|
|
58
|
+
except OSError:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _normalise_remote_url(url: str) -> str:
|
|
63
|
+
"""Convert a git remote URL to an https:// browsable form when possible."""
|
|
64
|
+
|
|
65
|
+
if url.startswith(("http://", "https://")):
|
|
66
|
+
return url[:-4] if url.endswith(".git") else url
|
|
67
|
+
# git@github.com:owner/repo.git -> https://github.com/owner/repo
|
|
68
|
+
m = re.match(r"^[^@]+@([^:]+):(.+?)(\.git)?$", url)
|
|
69
|
+
if m:
|
|
70
|
+
host, path, _ = m.groups()
|
|
71
|
+
return f"https://{host}/{path}"
|
|
72
|
+
return url
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@lru_cache(maxsize=1)
|
|
76
|
+
def _git_info() -> Dict[str, Any]:
|
|
77
|
+
if not (_REPO_ROOT / ".git").exists():
|
|
78
|
+
return {}
|
|
79
|
+
commit = _run_git("rev-parse", "HEAD")
|
|
80
|
+
if commit is None:
|
|
81
|
+
return {}
|
|
82
|
+
short = _run_git("rev-parse", "--short", "HEAD")
|
|
83
|
+
branch = _run_git("rev-parse", "--abbrev-ref", "HEAD")
|
|
84
|
+
describe = _run_git("describe", "--tags", "--always", "--dirty")
|
|
85
|
+
tag = _run_git("describe", "--tags", "--exact-match")
|
|
86
|
+
status = _run_git("status", "--porcelain")
|
|
87
|
+
remote = _run_git("config", "--get", "remote.origin.url")
|
|
88
|
+
dirty = bool(status)
|
|
89
|
+
|
|
90
|
+
info: Dict[str, Any] = {
|
|
91
|
+
"git_commit": commit,
|
|
92
|
+
"git_commit_short": short or (commit[:7] if commit else None),
|
|
93
|
+
"git_branch": None if branch == "HEAD" else branch,
|
|
94
|
+
"git_describe": describe,
|
|
95
|
+
"git_tag": tag,
|
|
96
|
+
"git_dirty": dirty,
|
|
97
|
+
}
|
|
98
|
+
if remote:
|
|
99
|
+
url = _normalise_remote_url(remote)
|
|
100
|
+
info["git_url"] = url
|
|
101
|
+
info["git_link"] = f"{url}/commit/{commit}" if commit else url
|
|
102
|
+
return info
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@lru_cache(maxsize=1)
|
|
106
|
+
def get_version() -> str:
|
|
107
|
+
"""Best-effort version string for the running ``datakit`` package."""
|
|
108
|
+
|
|
109
|
+
info = _git_info()
|
|
110
|
+
describe = info.get("git_describe")
|
|
111
|
+
if describe:
|
|
112
|
+
suffix = "+dirty" if info.get("git_dirty") and not describe.endswith("-dirty") else ""
|
|
113
|
+
return f"{describe}{suffix}"
|
|
114
|
+
return _installed_version() or _file_version() or "0.0.0+unknown"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def build_meta() -> Dict[str, Any]:
|
|
118
|
+
"""Return a snapshot of provenance metadata for the running package.
|
|
119
|
+
|
|
120
|
+
``built_at`` is regenerated on every call so embedded copies record the
|
|
121
|
+
moment a dataset was materialized rather than when the module was imported.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
meta: Dict[str, Any] = {
|
|
125
|
+
"package": _PACKAGE_NAME,
|
|
126
|
+
"version": get_version(),
|
|
127
|
+
"version_file": _file_version(),
|
|
128
|
+
"version_installed": _installed_version(),
|
|
129
|
+
"python": sys.version.split()[0],
|
|
130
|
+
"python_implementation": _platform.python_implementation(),
|
|
131
|
+
"platform": _platform.platform(),
|
|
132
|
+
"built_at": _dt.datetime.now(_dt.timezone.utc).isoformat(timespec="seconds"),
|
|
133
|
+
}
|
|
134
|
+
meta.update(_git_info())
|
|
135
|
+
return meta
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
__version__ = get_version()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
__all__ = ["__version__", "get_version", "build_meta"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Centralized configuration for ``datakit``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class SourceMetaDefaults:
|
|
11
|
+
"""Keys used to annotate stream metadata for all data sources."""
|
|
12
|
+
|
|
13
|
+
meta_camera_key: str = "camera_tag"
|
|
14
|
+
meta_timeseries_key: str = "is_timeseries"
|
|
15
|
+
meta_source_key: str = "source_tag"
|
|
16
|
+
meta_interval_key: str = "is_interval"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class DatasetLayout:
|
|
21
|
+
"""Shape and naming expectations for materialized experiment datasets."""
|
|
22
|
+
|
|
23
|
+
index_names: Tuple[str, str, str] = ("Subject", "Session", "Task")
|
|
24
|
+
scope_key: str = "scope"
|
|
25
|
+
session_scope: str = "session"
|
|
26
|
+
experiment_scope: str = "experiment"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class TimelineDefaults:
|
|
31
|
+
"""Options that describe how we discover and parse timeline CSV files."""
|
|
32
|
+
|
|
33
|
+
dataqueue_glob: str = "*_dataqueue.csv"
|
|
34
|
+
queue_column: str = "queue_elapsed"
|
|
35
|
+
window_device_patterns: Tuple[str, ...] = ("dhyana", "mesoscope")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class Settings:
|
|
40
|
+
"""Container aggregating all configuration namespaces for callers."""
|
|
41
|
+
|
|
42
|
+
sources: SourceMetaDefaults = field(default_factory=SourceMetaDefaults)
|
|
43
|
+
dataset: DatasetLayout = field(default_factory=DatasetLayout)
|
|
44
|
+
timeline: TimelineDefaults = field(default_factory=TimelineDefaults)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
settings = Settings()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = ["settings"]
|