np-workflows 1.6.89__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.
- np_workflows/__init__.py +7 -0
- np_workflows/assets/images/logo_np_hab.png +0 -0
- np_workflows/assets/images/logo_np_vis.png +0 -0
- np_workflows/experiments/__init__.py +1 -0
- np_workflows/experiments/dynamic_routing/__init__.py +2 -0
- np_workflows/experiments/dynamic_routing/main.py +117 -0
- np_workflows/experiments/dynamic_routing/widgets.py +82 -0
- np_workflows/experiments/openscope_P3/P3_workflow_widget.py +83 -0
- np_workflows/experiments/openscope_P3/__init__.py +2 -0
- np_workflows/experiments/openscope_P3/main_P3_pilot.py +217 -0
- np_workflows/experiments/openscope_barcode/__init__.py +2 -0
- np_workflows/experiments/openscope_barcode/barcode_workflow_widget.py +83 -0
- np_workflows/experiments/openscope_barcode/camstim_scripts/barcode_mapping_script.py +138 -0
- np_workflows/experiments/openscope_barcode/camstim_scripts/barcode_opto_script.py +219 -0
- np_workflows/experiments/openscope_barcode/main_barcode_pilot.py +217 -0
- np_workflows/experiments/openscope_loop/__init__.py +2 -0
- np_workflows/experiments/openscope_loop/camstim_scripts/barcode_mapping_script.py +138 -0
- np_workflows/experiments/openscope_loop/camstim_scripts/barcode_opto_script.py +219 -0
- np_workflows/experiments/openscope_loop/loop_workflow_widget.py +83 -0
- np_workflows/experiments/openscope_loop/main_loop_pilot.py +217 -0
- np_workflows/experiments/openscope_psycode/__init__.py +2 -0
- np_workflows/experiments/openscope_psycode/main_psycode_pilot.py +217 -0
- np_workflows/experiments/openscope_psycode/psycode_workflow_widget.py +83 -0
- np_workflows/experiments/openscope_v2/__init__.py +2 -0
- np_workflows/experiments/openscope_v2/main_v2_pilot.py +217 -0
- np_workflows/experiments/openscope_v2/v2_workflow_widget.py +83 -0
- np_workflows/experiments/openscope_vippo/__init__.py +2 -0
- np_workflows/experiments/openscope_vippo/main_vippo_pilot.py +217 -0
- np_workflows/experiments/openscope_vippo/vippo_workflow_widget.py +83 -0
- np_workflows/experiments/task_trained_network/__init__.py +2 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/make_tt_stims.py +23 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/oct22_tt_stim_script.py +69 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_00.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_01.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_02.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_03.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_04.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_05.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_06.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_07.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_08.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_09.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_10.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_11.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_12.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_13.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_14.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_15.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_16.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_17.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_18.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/flash_250ms.stim +20 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/gabor_20_deg_250ms.stim +30 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/old_stim.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed_1st.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed_2nd.stim +5 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_main_script.py +130 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_mapping_script.py +138 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_opto_script.py +219 -0
- np_workflows/experiments/task_trained_network/main_ttn_pilot.py +263 -0
- np_workflows/experiments/task_trained_network/ttn_session_widget.py +83 -0
- np_workflows/experiments/task_trained_network/ttn_stim_config.py +213 -0
- np_workflows/experiments/templeton/__init__.py +2 -0
- np_workflows/experiments/templeton/main.py +105 -0
- np_workflows/experiments/templeton/widgets.py +82 -0
- np_workflows/shared/__init__.py +3 -0
- np_workflows/shared/base_experiments.py +826 -0
- np_workflows/shared/camstim_scripts/flash_250ms.stim +20 -0
- np_workflows/shared/camstim_scripts/gabor_20_deg_250ms.stim +30 -0
- np_workflows/shared/npxc.py +187 -0
- np_workflows/shared/widgets.py +705 -0
- np_workflows-1.6.89.dist-info/METADATA +85 -0
- np_workflows-1.6.89.dist-info/RECORD +76 -0
- np_workflows-1.6.89.dist-info/WHEEL +4 -0
- np_workflows-1.6.89.dist-info/entry_points.txt +4 -0
np_workflows/__init__.py
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from np_workflows.experiments import *
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import configparser
|
|
4
|
+
import contextlib
|
|
5
|
+
import copy
|
|
6
|
+
import dataclasses
|
|
7
|
+
import datetime
|
|
8
|
+
import enum
|
|
9
|
+
import functools
|
|
10
|
+
import pathlib
|
|
11
|
+
import platform
|
|
12
|
+
import shutil
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
15
|
+
import zlib
|
|
16
|
+
from typing import ClassVar, Literal, NamedTuple, NoReturn, Optional, Type, TypedDict
|
|
17
|
+
|
|
18
|
+
import IPython
|
|
19
|
+
import IPython.display
|
|
20
|
+
import ipywidgets as ipw
|
|
21
|
+
import np_config
|
|
22
|
+
import np_logging
|
|
23
|
+
import np_services
|
|
24
|
+
import np_session
|
|
25
|
+
import np_workflows
|
|
26
|
+
import fabric
|
|
27
|
+
import PIL.Image
|
|
28
|
+
import pydantic
|
|
29
|
+
from pyparsing import Any
|
|
30
|
+
from np_services import (
|
|
31
|
+
Service,
|
|
32
|
+
Finalizable,
|
|
33
|
+
ScriptCamstim,
|
|
34
|
+
OpenEphys,
|
|
35
|
+
Sync,
|
|
36
|
+
ImageMVR,
|
|
37
|
+
VideoMVR,
|
|
38
|
+
NewScaleCoordinateRecorder,
|
|
39
|
+
MouseDirector,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
from np_workflows.shared import base_experiments
|
|
43
|
+
|
|
44
|
+
logger = np_logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
def new_experiment(
|
|
47
|
+
mouse: int | str | np_session.Mouse,
|
|
48
|
+
user: str | np_session.User,
|
|
49
|
+
workflow: base_experiments.DynamicRoutingExperiment.Workflow,
|
|
50
|
+
) -> DRTask:
|
|
51
|
+
"""Create a new experiment for the given mouse and user."""
|
|
52
|
+
experiment: DRTask
|
|
53
|
+
if any(tag in workflow.name for tag in ('EPHYS', 'PRETEST')):
|
|
54
|
+
experiment = Ephys(mouse, user)
|
|
55
|
+
elif 'HAB' in workflow.name:
|
|
56
|
+
experiment = Hab(mouse, user)
|
|
57
|
+
elif 'OPTO' in workflow.name:
|
|
58
|
+
experiment = Opto(mouse, user)
|
|
59
|
+
elif 'TRAINING' in workflow.name:
|
|
60
|
+
experiment = Training(mouse, user)
|
|
61
|
+
else:
|
|
62
|
+
raise ValueError(f"Unknown {workflow = }. Create an experiment with e.g.\n\n\texperiment = Ephys(mouse, user)\nexperiment.session.npexp_path.mkdir()")
|
|
63
|
+
experiment.workflow = workflow
|
|
64
|
+
experiment.log(f"{experiment} created")
|
|
65
|
+
experiment.session.npexp_path.mkdir(parents=True, exist_ok=True)
|
|
66
|
+
return experiment
|
|
67
|
+
|
|
68
|
+
class DRTask(base_experiments.DynamicRoutingExperiment):
|
|
69
|
+
"""Provides project-specific methods and attributes, mainly related to camstim scripts."""
|
|
70
|
+
|
|
71
|
+
default_session_subclass = np_session.DRPilotSession
|
|
72
|
+
|
|
73
|
+
workflow: base_experiments.DynamicRoutingExperiment.Workflow
|
|
74
|
+
"""Enum for workflow type, e.g. PRETEST, HAB_AUD, HAB_VIS, EPHYS_ etc."""
|
|
75
|
+
|
|
76
|
+
class Hab(DRTask):
|
|
77
|
+
def __init__(self, *args, **kwargs):
|
|
78
|
+
self.services = (
|
|
79
|
+
MouseDirector,
|
|
80
|
+
Sync,
|
|
81
|
+
VideoMVR,
|
|
82
|
+
ScriptCamstim,
|
|
83
|
+
)
|
|
84
|
+
super().__init__(*args, **kwargs)
|
|
85
|
+
|
|
86
|
+
class Opto(DRTask):
|
|
87
|
+
def __init__(self, *args, **kwargs):
|
|
88
|
+
self.services = (
|
|
89
|
+
MouseDirector,
|
|
90
|
+
Sync,
|
|
91
|
+
VideoMVR,
|
|
92
|
+
ScriptCamstim,
|
|
93
|
+
)
|
|
94
|
+
super().__init__(*args, **kwargs)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class Ephys(DRTask):
|
|
98
|
+
def __init__(self, *args, **kwargs):
|
|
99
|
+
self.services = (
|
|
100
|
+
MouseDirector,
|
|
101
|
+
Sync,
|
|
102
|
+
VideoMVR,
|
|
103
|
+
NewScaleCoordinateRecorder,
|
|
104
|
+
ScriptCamstim,
|
|
105
|
+
OpenEphys,
|
|
106
|
+
)
|
|
107
|
+
super().__init__(*args, **kwargs)
|
|
108
|
+
|
|
109
|
+
class Training(DRTask):
|
|
110
|
+
def __init__(self, *args, **kwargs):
|
|
111
|
+
self.services = (
|
|
112
|
+
MouseDirector,
|
|
113
|
+
Sync,
|
|
114
|
+
VideoMVR,
|
|
115
|
+
ScriptCamstim,
|
|
116
|
+
)
|
|
117
|
+
super().__init__(*args, **kwargs)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
import contextlib
|
|
3
|
+
import copy
|
|
4
|
+
import enum
|
|
5
|
+
import functools
|
|
6
|
+
from typing import ClassVar, Literal, NamedTuple, NoReturn, Optional, TypedDict
|
|
7
|
+
|
|
8
|
+
import IPython.display
|
|
9
|
+
import ipywidgets as ipw
|
|
10
|
+
import np_config
|
|
11
|
+
import np_logging
|
|
12
|
+
import np_session
|
|
13
|
+
import np_workflows
|
|
14
|
+
from pyparsing import Any
|
|
15
|
+
|
|
16
|
+
from np_workflows.experiments.dynamic_routing.main import Ephys, Hab, DRTask
|
|
17
|
+
from np_workflows.shared.base_experiments import DynamicRoutingExperiment
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# for widget, before creating a experiment --------------------------------------------- #
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SelectedWorkflow:
|
|
24
|
+
def __init__(self, workflow: str | DynamicRoutingExperiment.Workflow, mouse: str | int | np_session.Mouse):
|
|
25
|
+
if isinstance(workflow, str):
|
|
26
|
+
workflow = DynamicRoutingExperiment.Workflow[workflow] # uses enum name (not value)
|
|
27
|
+
self.workflow = workflow
|
|
28
|
+
self.mouse = str(mouse)
|
|
29
|
+
|
|
30
|
+
def __repr__(self) -> str:
|
|
31
|
+
return f"{self.__class__.__name__}({self.workflow}, {self.mouse})"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def workflow_select_widget(
|
|
35
|
+
mouse: str | int | np_session.Mouse,
|
|
36
|
+
) -> SelectedWorkflow:
|
|
37
|
+
"""Select a session type to run (hab, pretest, ephys).
|
|
38
|
+
|
|
39
|
+
An object with mutable attributes is returned, so the selected session can be
|
|
40
|
+
updated along with the GUI selection. (Preference would be to return an enum
|
|
41
|
+
directly, and change it's value, but that doesn't seem possible.)
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
# set default
|
|
45
|
+
selection = SelectedWorkflow(DynamicRoutingExperiment.Workflow.PRETEST, mouse)
|
|
46
|
+
|
|
47
|
+
workflow_dropdown = ipw.Select(
|
|
48
|
+
options=tuple(_.name for _ in DynamicRoutingExperiment.Workflow),
|
|
49
|
+
description="Workflow",
|
|
50
|
+
)
|
|
51
|
+
workflow_descriptions = ipw.Select(
|
|
52
|
+
options=tuple(_.value for _ in DynamicRoutingExperiment.Workflow),
|
|
53
|
+
disabled=True,
|
|
54
|
+
value=None,
|
|
55
|
+
)
|
|
56
|
+
console = ipw.Output()
|
|
57
|
+
with console:
|
|
58
|
+
if last_workflow := np_session.Mouse(selection.mouse).state.get('last_workflow'):
|
|
59
|
+
print(f"{mouse} last workflow: {last_workflow}\t({np_session.Mouse(selection.mouse).state.get('last_session')})")
|
|
60
|
+
print(f"Selected: {selection.workflow.name}")
|
|
61
|
+
|
|
62
|
+
def update(change):
|
|
63
|
+
if change["name"] != "value":
|
|
64
|
+
return
|
|
65
|
+
if (options := getattr(change["owner"], "options", None)) and change[
|
|
66
|
+
"new"
|
|
67
|
+
] not in options:
|
|
68
|
+
return
|
|
69
|
+
if change["new"] == change["old"]:
|
|
70
|
+
return
|
|
71
|
+
selection.__init__(str(workflow_dropdown.value), mouse.id if isinstance(mouse, np_session.Mouse) else str(mouse))
|
|
72
|
+
with console:
|
|
73
|
+
print(f"Selected: {selection.workflow}")
|
|
74
|
+
workflow_dropdown.observe(update, names='value')
|
|
75
|
+
|
|
76
|
+
IPython.display.display(ipw.VBox([ipw.HBox([workflow_dropdown, workflow_descriptions]), console]))
|
|
77
|
+
|
|
78
|
+
return selection
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def photodoc_widget(session: np_session.Session, reminder: str) -> None:
|
|
82
|
+
print(f'Take an image in Vimba Viewer and save as:\n{session.npexp_path.name}_{reminder}.png')
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
import contextlib
|
|
3
|
+
import copy
|
|
4
|
+
import enum
|
|
5
|
+
import functools
|
|
6
|
+
from typing import ClassVar, Literal, NamedTuple, NoReturn, Optional, TypedDict
|
|
7
|
+
|
|
8
|
+
import IPython.display
|
|
9
|
+
import ipywidgets as ipw
|
|
10
|
+
import np_config
|
|
11
|
+
import np_logging
|
|
12
|
+
import np_session
|
|
13
|
+
import np_workflows
|
|
14
|
+
from pyparsing import Any
|
|
15
|
+
|
|
16
|
+
from np_workflows.experiments.openscope_P3.main_P3_pilot import P3Session
|
|
17
|
+
|
|
18
|
+
global_state = {}
|
|
19
|
+
"""Global variable for persisting widget states."""
|
|
20
|
+
|
|
21
|
+
# for widget, before creating a experiment --------------------------------------------- #
|
|
22
|
+
|
|
23
|
+
class SelectedSession:
|
|
24
|
+
def __init__(self, session: str | P3Session, mouse: str | int | np_session.Mouse):
|
|
25
|
+
if isinstance(session, str):
|
|
26
|
+
session = P3Session(session)
|
|
27
|
+
self.session = session
|
|
28
|
+
self.mouse = str(mouse)
|
|
29
|
+
|
|
30
|
+
def __repr__(self) -> str:
|
|
31
|
+
return f"{self.__class__.__name__}({self.session}, {self.mouse})"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def P3_workflow_widget(
|
|
35
|
+
mouse: str | int | np_session.Mouse,
|
|
36
|
+
) -> SelectedSession:
|
|
37
|
+
"""Select a stimulus session (hab, pretest, ephys) to run.
|
|
38
|
+
|
|
39
|
+
An object with mutable attributes is returned, so the selected session can be
|
|
40
|
+
updated along with the GUI selection. (Preference would be to return an enum
|
|
41
|
+
directly, and change it's value, but that doesn't seem possible.)
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
selection = SelectedSession(P3Session.PRETEST, mouse)
|
|
46
|
+
|
|
47
|
+
session_dropdown = ipw.Select(
|
|
48
|
+
options=tuple(_.value for _ in P3Session),
|
|
49
|
+
description="Session",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def update_selection():
|
|
53
|
+
selection.__init__(str(session_dropdown.value), str(mouse))
|
|
54
|
+
|
|
55
|
+
if (previously_selected_value := global_state.get('selected_session')):
|
|
56
|
+
session_dropdown.value = previously_selected_value
|
|
57
|
+
update_selection()
|
|
58
|
+
|
|
59
|
+
console = ipw.Output()
|
|
60
|
+
with console:
|
|
61
|
+
if last_session := np_session.Mouse(selection.mouse).state.get('last_P3_session'):
|
|
62
|
+
print(f"{mouse} last session: {last_session}")
|
|
63
|
+
print(f"Selected: {selection.session}")
|
|
64
|
+
|
|
65
|
+
def update(change):
|
|
66
|
+
if change["name"] != "value":
|
|
67
|
+
return
|
|
68
|
+
if (options := getattr(change["owner"], "options", None)) and change[
|
|
69
|
+
"new"
|
|
70
|
+
] not in options:
|
|
71
|
+
return
|
|
72
|
+
if change["new"] == change["old"]:
|
|
73
|
+
return
|
|
74
|
+
update_selection()
|
|
75
|
+
with console:
|
|
76
|
+
print(f"Selected: {selection.session}")
|
|
77
|
+
global_state['selected_session'] = selection.session.value
|
|
78
|
+
|
|
79
|
+
session_dropdown.observe(update, names='value')
|
|
80
|
+
|
|
81
|
+
IPython.display.display(ipw.VBox([session_dropdown, console]))
|
|
82
|
+
|
|
83
|
+
return selection
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
import contextlib
|
|
3
|
+
import copy
|
|
4
|
+
import dataclasses
|
|
5
|
+
import datetime
|
|
6
|
+
import enum
|
|
7
|
+
import functools
|
|
8
|
+
import pathlib
|
|
9
|
+
import platform
|
|
10
|
+
import shutil
|
|
11
|
+
import threading
|
|
12
|
+
import time
|
|
13
|
+
import zlib
|
|
14
|
+
from typing import ClassVar, Literal, NamedTuple, NoReturn, Optional, TypedDict
|
|
15
|
+
|
|
16
|
+
import IPython
|
|
17
|
+
import IPython.display
|
|
18
|
+
import ipywidgets as ipw
|
|
19
|
+
import np_config
|
|
20
|
+
import np_logging
|
|
21
|
+
import np_services
|
|
22
|
+
import np_session
|
|
23
|
+
import np_workflows
|
|
24
|
+
import PIL.Image
|
|
25
|
+
import pydantic
|
|
26
|
+
from pyparsing import Any
|
|
27
|
+
from np_services import (
|
|
28
|
+
Service,
|
|
29
|
+
Finalizable,
|
|
30
|
+
ScriptCamstim, SessionCamstim,
|
|
31
|
+
SessionCamstim,
|
|
32
|
+
OpenEphys,
|
|
33
|
+
Sync,
|
|
34
|
+
VideoMVR,
|
|
35
|
+
NewScaleCoordinateRecorder,
|
|
36
|
+
MouseDirector,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
logger = np_logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class P3Session(enum.Enum):
|
|
43
|
+
"""Enum for the different sessions available, each with different param sets."""
|
|
44
|
+
|
|
45
|
+
PRETEST = "pretest"
|
|
46
|
+
HAB = "hab"
|
|
47
|
+
EPHYS = "ephys"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class P3Mixin:
|
|
51
|
+
"""Provides project-specific methods and attributes, mainly related to camstim scripts."""
|
|
52
|
+
|
|
53
|
+
workflow: P3Session
|
|
54
|
+
"""Enum for particular workflow/session, e.g. PRETEST, HAB_60, HAB_90,
|
|
55
|
+
EPHYS."""
|
|
56
|
+
|
|
57
|
+
session: np_session.PipelineSession
|
|
58
|
+
mouse: np_session.Mouse
|
|
59
|
+
user: np_session.User
|
|
60
|
+
platform_json: np_session.PlatformJson
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def recorders(self) -> tuple[Service, ...]:
|
|
64
|
+
"""Services to be started before stimuli run, and stopped after. Session-dependent."""
|
|
65
|
+
match self.workflow:
|
|
66
|
+
case P3Session.PRETEST | P3Session.EPHYS:
|
|
67
|
+
return (Sync, VideoMVR, OpenEphys)
|
|
68
|
+
case P3Session.HAB:
|
|
69
|
+
return (Sync, VideoMVR)
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def stims(self) -> tuple[Service, ...]:
|
|
73
|
+
return (SessionCamstim, )
|
|
74
|
+
|
|
75
|
+
def initialize_and_test_services(self) -> None:
|
|
76
|
+
"""Configure, initialize (ie. reset), then test all services."""
|
|
77
|
+
|
|
78
|
+
MouseDirector.user = self.user.id
|
|
79
|
+
MouseDirector.mouse = self.mouse.id
|
|
80
|
+
|
|
81
|
+
OpenEphys.folder = self.session.folder
|
|
82
|
+
|
|
83
|
+
NewScaleCoordinateRecorder.log_root = self.session.npexp_path
|
|
84
|
+
NewScaleCoordinateRecorder.log_name = self.platform_json.path.name
|
|
85
|
+
|
|
86
|
+
SessionCamstim.labtracks_mouse_id = self.mouse.id
|
|
87
|
+
SessionCamstim.lims_user_id = self.user.id
|
|
88
|
+
|
|
89
|
+
self.configure_services()
|
|
90
|
+
|
|
91
|
+
super().initialize_and_test_services()
|
|
92
|
+
|
|
93
|
+
def update_state(self) -> None:
|
|
94
|
+
"Store useful but non-essential info."
|
|
95
|
+
self.mouse.state['last_session'] = self.session.id
|
|
96
|
+
self.mouse.state['last_P3_session'] = str(self.workflow)
|
|
97
|
+
if self.mouse == 366122:
|
|
98
|
+
return
|
|
99
|
+
match self.workflow:
|
|
100
|
+
case P3Session.PRETEST:
|
|
101
|
+
return
|
|
102
|
+
case P3Session.HAB:
|
|
103
|
+
self.session.project.state['latest_hab'] = self.session.id
|
|
104
|
+
case P3Session.EPHYS:
|
|
105
|
+
self.session.project.state['latest_ephys'] = self.session.id
|
|
106
|
+
self.session.project.state['sessions'] = self.session.project.state.get('sessions', []) + [self.session.id]
|
|
107
|
+
|
|
108
|
+
def run_stim(self) -> None:
|
|
109
|
+
|
|
110
|
+
self.update_state()
|
|
111
|
+
|
|
112
|
+
if not SessionCamstim.is_ready_to_start():
|
|
113
|
+
raise RuntimeError("SessionCamstim is not ready to start.")
|
|
114
|
+
|
|
115
|
+
np_logging.web(f'P3_{self.workflow.name.lower()}').info(f"Started session {self.mouse.mtrain.stage['name']}")
|
|
116
|
+
SessionCamstim.start()
|
|
117
|
+
|
|
118
|
+
with contextlib.suppress(Exception):
|
|
119
|
+
while not SessionCamstim.is_ready_to_start():
|
|
120
|
+
time.sleep(2.5)
|
|
121
|
+
|
|
122
|
+
if isinstance(SessionCamstim, Finalizable):
|
|
123
|
+
SessionCamstim.finalize()
|
|
124
|
+
|
|
125
|
+
with contextlib.suppress(Exception):
|
|
126
|
+
np_logging.web(f'P3_{self.workflow.name.lower()}').info(f"Finished session {self.mouse.mtrain.stage['name']}")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def copy_data_files(self) -> None:
|
|
130
|
+
super().copy_data_files()
|
|
131
|
+
|
|
132
|
+
# When all processing completes, camstim Agent class passes data and uuid to
|
|
133
|
+
# /camstim/lims BehaviorSession class, and write_behavior_data() writes a
|
|
134
|
+
# final .pkl with default name YYYYMMDDSSSS_mouseID_foragingID.pkl
|
|
135
|
+
# - if we have a foraging ID, we can search for that
|
|
136
|
+
if None == (stim_pkl := next(self.session.npexp_path.glob(f'{self.session.date:%y%m%d}*_{self.session.mouse}_*.pkl'), None)):
|
|
137
|
+
logger.warning('Did not find stim file on npexp matching the format `YYYYMMDDSSSS_mouseID_foragingID.pkl`')
|
|
138
|
+
return
|
|
139
|
+
assert stim_pkl
|
|
140
|
+
if not self.session.platform_json.foraging_id:
|
|
141
|
+
self.session.platform_json.foraging_id = stim_pkl.stem.split('_')[-1]
|
|
142
|
+
new_stem = f'{self.session.folder}.stim'
|
|
143
|
+
logger.debug(f'Renaming stim file copied to npexp: {stim_pkl} -> {new_stem}')
|
|
144
|
+
stim_pkl = stim_pkl.rename(stim_pkl.with_stem(new_stem))
|
|
145
|
+
|
|
146
|
+
# remove other stim pkl, which is nearly identical, if it was also copied
|
|
147
|
+
for pkl in self.session.npexp_path.glob('*.pkl'):
|
|
148
|
+
if (
|
|
149
|
+
self.session.folder not in pkl.stem
|
|
150
|
+
and
|
|
151
|
+
abs(pkl.stat().st_size - stim_pkl.stat().st_size) < 1e6
|
|
152
|
+
):
|
|
153
|
+
logger.debug(f'Deleting extra stim pkl copied to npexp: {pkl.stem}')
|
|
154
|
+
pkl.unlink()
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def validate_selected_workflow(session: P3Session, mouse: np_session.Mouse) -> None:
|
|
158
|
+
for workflow in ('hab', 'ephys'):
|
|
159
|
+
if (
|
|
160
|
+
workflow in session.value.lower()
|
|
161
|
+
and workflow not in mouse.mtrain.stage['name'].lower()
|
|
162
|
+
) or (
|
|
163
|
+
session.value.lower() == 'ephys' and 'hab' in mouse.mtrain.stage['name'].lower()
|
|
164
|
+
):
|
|
165
|
+
raise ValueError(f"Workflow selected ({session.value}) does not match MTrain stage ({mouse.mtrain.stage['name']}): please check cells above.")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class Hab(P3Mixin, np_workflows.PipelineHab):
|
|
169
|
+
def __init__(self, *args, **kwargs):
|
|
170
|
+
self.services = (
|
|
171
|
+
MouseDirector,
|
|
172
|
+
Sync,
|
|
173
|
+
VideoMVR,
|
|
174
|
+
self.imager,
|
|
175
|
+
NewScaleCoordinateRecorder,
|
|
176
|
+
SessionCamstim,
|
|
177
|
+
)
|
|
178
|
+
super().__init__(*args, **kwargs)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class Ephys(P3Mixin, np_workflows.PipelineEphys):
|
|
182
|
+
def __init__(self, *args, **kwargs):
|
|
183
|
+
self.services = (
|
|
184
|
+
MouseDirector,
|
|
185
|
+
Sync,
|
|
186
|
+
VideoMVR,
|
|
187
|
+
self.imager,
|
|
188
|
+
NewScaleCoordinateRecorder,
|
|
189
|
+
SessionCamstim,
|
|
190
|
+
OpenEphys,
|
|
191
|
+
)
|
|
192
|
+
super().__init__(*args, **kwargs)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# --------------------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def new_experiment(
|
|
199
|
+
mouse: int | str | np_session.Mouse,
|
|
200
|
+
user: str | np_session.User,
|
|
201
|
+
workflow: P3Session,
|
|
202
|
+
) -> Ephys | Hab:
|
|
203
|
+
"""Create a new experiment for the given mouse and user."""
|
|
204
|
+
match workflow:
|
|
205
|
+
case P3Session.PRETEST | P3Session.EPHYS:
|
|
206
|
+
experiment = Ephys(mouse, user)
|
|
207
|
+
case P3Session.HAB:
|
|
208
|
+
experiment = Hab(mouse, user)
|
|
209
|
+
case _:
|
|
210
|
+
raise ValueError(f"Invalid workflow type: {workflow}")
|
|
211
|
+
experiment.workflow = workflow
|
|
212
|
+
|
|
213
|
+
with contextlib.suppress(Exception):
|
|
214
|
+
np_logging.web(f'P3_{experiment.workflow.name.lower()}').info(f"{experiment} created")
|
|
215
|
+
|
|
216
|
+
return experiment
|
|
217
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
import contextlib
|
|
3
|
+
import copy
|
|
4
|
+
import enum
|
|
5
|
+
import functools
|
|
6
|
+
from typing import ClassVar, Literal, NamedTuple, NoReturn, Optional, TypedDict
|
|
7
|
+
|
|
8
|
+
import IPython.display
|
|
9
|
+
import ipywidgets as ipw
|
|
10
|
+
import np_config
|
|
11
|
+
import np_logging
|
|
12
|
+
import np_session
|
|
13
|
+
import np_workflows
|
|
14
|
+
from pyparsing import Any
|
|
15
|
+
|
|
16
|
+
from np_workflows.experiments.openscope_barcode.main_barcode_pilot import BarcodeSession
|
|
17
|
+
|
|
18
|
+
global_state = {}
|
|
19
|
+
"""Global variable for persisting widget states."""
|
|
20
|
+
|
|
21
|
+
# for widget, before creating a experiment --------------------------------------------- #
|
|
22
|
+
|
|
23
|
+
class SelectedSession:
|
|
24
|
+
def __init__(self, session: str | BarcodeSession, mouse: str | int | np_session.Mouse):
|
|
25
|
+
if isinstance(session, str):
|
|
26
|
+
session = BarcodeSession(session)
|
|
27
|
+
self.session = session
|
|
28
|
+
self.mouse = str(mouse)
|
|
29
|
+
|
|
30
|
+
def __repr__(self) -> str:
|
|
31
|
+
return f"{self.__class__.__name__}({self.session}, {self.mouse})"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def barcode_workflow_widget(
|
|
35
|
+
mouse: str | int | np_session.Mouse,
|
|
36
|
+
) -> SelectedSession:
|
|
37
|
+
"""Select a stimulus session (hab, pretest, ephys) to run.
|
|
38
|
+
|
|
39
|
+
An object with mutable attributes is returned, so the selected session can be
|
|
40
|
+
updated along with the GUI selection. (Preference would be to return an enum
|
|
41
|
+
directly, and change it's value, but that doesn't seem possible.)
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
selection = SelectedSession(BarcodeSession.PRETEST, mouse)
|
|
46
|
+
|
|
47
|
+
session_dropdown = ipw.Select(
|
|
48
|
+
options=tuple(_.value for _ in BarcodeSession),
|
|
49
|
+
description="Session",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def update_selection():
|
|
53
|
+
selection.__init__(str(session_dropdown.value), str(mouse))
|
|
54
|
+
|
|
55
|
+
if (previously_selected_value := global_state.get('selected_session')):
|
|
56
|
+
session_dropdown.value = previously_selected_value
|
|
57
|
+
update_selection()
|
|
58
|
+
|
|
59
|
+
console = ipw.Output()
|
|
60
|
+
with console:
|
|
61
|
+
if last_session := np_session.Mouse(selection.mouse).state.get('last_barcode_session'):
|
|
62
|
+
print(f"{mouse} last session: {last_session}")
|
|
63
|
+
print(f"Selected: {selection.session}")
|
|
64
|
+
|
|
65
|
+
def update(change):
|
|
66
|
+
if change["name"] != "value":
|
|
67
|
+
return
|
|
68
|
+
if (options := getattr(change["owner"], "options", None)) and change[
|
|
69
|
+
"new"
|
|
70
|
+
] not in options:
|
|
71
|
+
return
|
|
72
|
+
if change["new"] == change["old"]:
|
|
73
|
+
return
|
|
74
|
+
update_selection()
|
|
75
|
+
with console:
|
|
76
|
+
print(f"Selected: {selection.session}")
|
|
77
|
+
global_state['selected_session'] = selection.session.value
|
|
78
|
+
|
|
79
|
+
session_dropdown.observe(update, names='value')
|
|
80
|
+
|
|
81
|
+
IPython.display.display(ipw.VBox([session_dropdown, console]))
|
|
82
|
+
|
|
83
|
+
return selection
|