np-workflows 1.6.89__py3-none-any.whl → 1.6.91__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 +3 -5
- np_workflows/experiments/dynamic_routing/main.py +20 -41
- np_workflows/experiments/dynamic_routing/widgets.py +29 -24
- np_workflows/experiments/openscope_P3/P3_workflow_widget.py +11 -19
- np_workflows/experiments/openscope_P3/__init__.py +1 -1
- np_workflows/experiments/openscope_P3/main_P3_pilot.py +66 -68
- np_workflows/experiments/openscope_barcode/__init__.py +1 -1
- np_workflows/experiments/openscope_barcode/barcode_workflow_widget.py +14 -20
- np_workflows/experiments/openscope_barcode/camstim_scripts/barcode_mapping_script.py +8 -14
- np_workflows/experiments/openscope_barcode/camstim_scripts/barcode_opto_script.py +121 -68
- np_workflows/experiments/openscope_barcode/main_barcode_pilot.py +69 -69
- np_workflows/experiments/openscope_loop/__init__.py +1 -1
- np_workflows/experiments/openscope_loop/camstim_scripts/barcode_mapping_script.py +8 -14
- np_workflows/experiments/openscope_loop/camstim_scripts/barcode_opto_script.py +121 -68
- np_workflows/experiments/openscope_loop/loop_workflow_widget.py +11 -19
- np_workflows/experiments/openscope_loop/main_loop_pilot.py +66 -68
- np_workflows/experiments/openscope_psycode/__init__.py +1 -1
- np_workflows/experiments/openscope_psycode/main_psycode_pilot.py +69 -69
- np_workflows/experiments/openscope_psycode/psycode_workflow_widget.py +14 -20
- np_workflows/experiments/openscope_v2/__init__.py +1 -1
- np_workflows/experiments/openscope_v2/main_v2_pilot.py +66 -68
- np_workflows/experiments/openscope_v2/v2_workflow_widget.py +11 -19
- np_workflows/experiments/openscope_vippo/__init__.py +1 -1
- np_workflows/experiments/openscope_vippo/main_vippo_pilot.py +66 -68
- np_workflows/experiments/openscope_vippo/vippo_workflow_widget.py +14 -20
- np_workflows/experiments/task_trained_network/__init__.py +1 -1
- np_workflows/experiments/task_trained_network/camstim_scripts/make_tt_stims.py +24 -14
- np_workflows/experiments/task_trained_network/camstim_scripts/oct22_tt_stim_script.py +54 -41
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_main_script.py +19 -22
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_mapping_script.py +8 -14
- np_workflows/experiments/task_trained_network/camstim_scripts/ttn_opto_script.py +121 -68
- np_workflows/experiments/task_trained_network/main_ttn_pilot.py +73 -68
- np_workflows/experiments/task_trained_network/ttn_session_widget.py +11 -19
- np_workflows/experiments/task_trained_network/ttn_stim_config.py +23 -19
- np_workflows/experiments/templeton/main.py +18 -41
- np_workflows/experiments/templeton/widgets.py +26 -23
- np_workflows/shared/__init__.py +1 -1
- np_workflows/shared/base_experiments.py +430 -308
- np_workflows/shared/npxc.py +85 -53
- np_workflows/shared/widgets.py +374 -224
- {np_workflows-1.6.89.dist-info → np_workflows-1.6.91.dist-info}/METADATA +7 -21
- np_workflows-1.6.91.dist-info/RECORD +48 -0
- {np_workflows-1.6.89.dist-info → np_workflows-1.6.91.dist-info}/WHEEL +2 -1
- np_workflows-1.6.91.dist-info/entry_points.txt +2 -0
- np_workflows-1.6.91.dist-info/top_level.txt +1 -0
- np_workflows/assets/images/logo_np_hab.png +0 -0
- np_workflows/assets/images/logo_np_vis.png +0 -0
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_00.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_01.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_02.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_03.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_04.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_05.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_06.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_07.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_08.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_09.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_10.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_11.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_12.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_13.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_14.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_15.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_16.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_17.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/densely_annotated_18.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/flash_250ms.stim +0 -20
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/gabor_20_deg_250ms.stim +0 -30
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/old_stim.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed_1st.stim +0 -5
- np_workflows/experiments/task_trained_network/camstim_scripts/stims/shuffle_reversed_2nd.stim +0 -5
- np_workflows/shared/camstim_scripts/flash_250ms.stim +0 -20
- np_workflows/shared/camstim_scripts/gabor_20_deg_250ms.stim +0 -30
- np_workflows-1.6.89.dist-info/RECORD +0 -76
- np_workflows-1.6.89.dist-info/entry_points.txt +0 -4
|
@@ -1,41 +1,22 @@
|
|
|
1
|
-
import configparser
|
|
2
1
|
import contextlib
|
|
3
|
-
import copy
|
|
4
|
-
import dataclasses
|
|
5
|
-
import datetime
|
|
6
2
|
import enum
|
|
7
|
-
import functools
|
|
8
|
-
import pathlib
|
|
9
|
-
import platform
|
|
10
|
-
import shutil
|
|
11
|
-
import threading
|
|
12
3
|
import time
|
|
13
|
-
import zlib
|
|
14
|
-
from typing import ClassVar, Literal, NamedTuple, NoReturn, Optional, TypedDict
|
|
15
4
|
|
|
16
|
-
import IPython
|
|
17
|
-
import IPython.display
|
|
18
|
-
import ipywidgets as ipw
|
|
19
|
-
import np_config
|
|
20
5
|
import np_logging
|
|
21
|
-
import np_services
|
|
22
6
|
import np_session
|
|
23
|
-
import np_workflows
|
|
24
|
-
import PIL.Image
|
|
25
|
-
import pydantic
|
|
26
|
-
from pyparsing import Any
|
|
27
7
|
from np_services import (
|
|
28
|
-
Service,
|
|
29
8
|
Finalizable,
|
|
30
|
-
|
|
31
|
-
|
|
9
|
+
MouseDirector,
|
|
10
|
+
NewScaleCoordinateRecorder,
|
|
32
11
|
OpenEphys,
|
|
12
|
+
Service,
|
|
13
|
+
SessionCamstim,
|
|
33
14
|
Sync,
|
|
34
15
|
VideoMVR,
|
|
35
|
-
NewScaleCoordinateRecorder,
|
|
36
|
-
MouseDirector,
|
|
37
16
|
)
|
|
38
17
|
|
|
18
|
+
import np_workflows
|
|
19
|
+
|
|
39
20
|
logger = np_logging.getLogger(__name__)
|
|
40
21
|
|
|
41
22
|
|
|
@@ -49,16 +30,16 @@ class PsyCodeSession(enum.Enum):
|
|
|
49
30
|
|
|
50
31
|
class PsyCodeMixin:
|
|
51
32
|
"""Provides project-specific methods and attributes, mainly related to camstim scripts."""
|
|
52
|
-
|
|
33
|
+
|
|
53
34
|
workflow: PsyCodeSession
|
|
54
35
|
"""Enum for particular workflow/session, e.g. PRETEST, HAB_60, HAB_90,
|
|
55
36
|
EPHYS."""
|
|
56
|
-
|
|
37
|
+
|
|
57
38
|
session: np_session.PipelineSession
|
|
58
39
|
mouse: np_session.Mouse
|
|
59
40
|
user: np_session.User
|
|
60
41
|
platform_json: np_session.PlatformJson
|
|
61
|
-
|
|
42
|
+
|
|
62
43
|
@property
|
|
63
44
|
def recorders(self) -> tuple[Service, ...]:
|
|
64
45
|
"""Services to be started before stimuli run, and stopped after. Session-dependent."""
|
|
@@ -70,11 +51,11 @@ class PsyCodeMixin:
|
|
|
70
51
|
|
|
71
52
|
@property
|
|
72
53
|
def stims(self) -> tuple[Service, ...]:
|
|
73
|
-
return (SessionCamstim,
|
|
74
|
-
|
|
54
|
+
return (SessionCamstim,)
|
|
55
|
+
|
|
75
56
|
def initialize_and_test_services(self) -> None:
|
|
76
57
|
"""Configure, initialize (ie. reset), then test all services."""
|
|
77
|
-
|
|
58
|
+
|
|
78
59
|
MouseDirector.user = self.user.id
|
|
79
60
|
MouseDirector.mouse = self.mouse.id
|
|
80
61
|
|
|
@@ -92,79 +73,97 @@ class PsyCodeMixin:
|
|
|
92
73
|
|
|
93
74
|
def update_state(self) -> None:
|
|
94
75
|
"Store useful but non-essential info."
|
|
95
|
-
self.mouse.state[
|
|
96
|
-
self.mouse.state[
|
|
76
|
+
self.mouse.state["last_session"] = self.session.id
|
|
77
|
+
self.mouse.state["last_PsyCode_session"] = str(self.workflow)
|
|
97
78
|
if self.mouse == 366122:
|
|
98
79
|
return
|
|
99
80
|
match self.workflow:
|
|
100
81
|
case PsyCodeSession.PRETEST:
|
|
101
82
|
return
|
|
102
83
|
case PsyCodeSession.HAB:
|
|
103
|
-
self.session.project.state[
|
|
84
|
+
self.session.project.state["latest_hab"] = self.session.id
|
|
104
85
|
case PsyCodeSession.EPHYS:
|
|
105
|
-
self.session.project.state[
|
|
106
|
-
self.session.project.state[
|
|
107
|
-
|
|
86
|
+
self.session.project.state["latest_ephys"] = self.session.id
|
|
87
|
+
self.session.project.state["sessions"] = self.session.project.state.get(
|
|
88
|
+
"sessions", []
|
|
89
|
+
) + [self.session.id]
|
|
90
|
+
|
|
108
91
|
def run_stim(self) -> None:
|
|
109
92
|
|
|
110
93
|
self.update_state()
|
|
111
|
-
|
|
94
|
+
|
|
112
95
|
if not SessionCamstim.is_ready_to_start():
|
|
113
96
|
raise RuntimeError("SessionCamstim is not ready to start.")
|
|
114
|
-
|
|
115
|
-
np_logging.web(f
|
|
97
|
+
|
|
98
|
+
np_logging.web(f"PsyCode_{self.workflow.name.lower()}").info(
|
|
99
|
+
f"Started session {self.mouse.mtrain.stage['name']}"
|
|
100
|
+
)
|
|
116
101
|
SessionCamstim.start()
|
|
117
|
-
|
|
102
|
+
|
|
118
103
|
with contextlib.suppress(Exception):
|
|
119
104
|
while not SessionCamstim.is_ready_to_start():
|
|
120
105
|
time.sleep(2.5)
|
|
121
|
-
|
|
106
|
+
|
|
122
107
|
if isinstance(SessionCamstim, Finalizable):
|
|
123
108
|
SessionCamstim.finalize()
|
|
124
109
|
|
|
125
110
|
with contextlib.suppress(Exception):
|
|
126
|
-
np_logging.web(f
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
111
|
+
np_logging.web(f"PsyCode_{self.workflow.name.lower()}").info(
|
|
112
|
+
f"Finished session {self.mouse.mtrain.stage['name']}"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def copy_data_files(self) -> None:
|
|
130
116
|
super().copy_data_files()
|
|
131
|
-
|
|
117
|
+
|
|
132
118
|
# When all processing completes, camstim Agent class passes data and uuid to
|
|
133
119
|
# /camstim/lims BehaviorSession class, and write_behavior_data() writes a
|
|
134
120
|
# final .pkl with default name YYYYMMDDSSSS_mouseID_foragingID.pkl
|
|
135
121
|
# - if we have a foraging ID, we can search for that
|
|
136
|
-
if None == (
|
|
137
|
-
|
|
122
|
+
if None == (
|
|
123
|
+
stim_pkl := next(
|
|
124
|
+
self.session.npexp_path.glob(
|
|
125
|
+
f"{self.session.date:%y%m%d}*_{self.session.mouse}_*.pkl"
|
|
126
|
+
),
|
|
127
|
+
None,
|
|
128
|
+
)
|
|
129
|
+
):
|
|
130
|
+
logger.warning(
|
|
131
|
+
"Did not find stim file on npexp matching the format `YYYYMMDDSSSS_mouseID_foragingID.pkl`"
|
|
132
|
+
)
|
|
138
133
|
return
|
|
139
134
|
assert stim_pkl
|
|
140
135
|
if not self.session.platform_json.foraging_id:
|
|
141
|
-
self.session.platform_json.foraging_id = stim_pkl.stem.split(
|
|
142
|
-
new_stem = f
|
|
143
|
-
logger.debug(f
|
|
136
|
+
self.session.platform_json.foraging_id = stim_pkl.stem.split("_")[-1]
|
|
137
|
+
new_stem = f"{self.session.folder}.stim"
|
|
138
|
+
logger.debug(f"Renaming stim file copied to npexp: {stim_pkl} -> {new_stem}")
|
|
144
139
|
stim_pkl = stim_pkl.rename(stim_pkl.with_stem(new_stem))
|
|
145
|
-
|
|
140
|
+
|
|
146
141
|
# remove other stim pkl, which is nearly identical, if it was also copied
|
|
147
|
-
for pkl in self.session.npexp_path.glob(
|
|
142
|
+
for pkl in self.session.npexp_path.glob("*.pkl"):
|
|
148
143
|
if (
|
|
149
144
|
self.session.folder not in pkl.stem
|
|
150
|
-
and
|
|
151
|
-
abs(pkl.stat().st_size - stim_pkl.stat().st_size) < 1e6
|
|
145
|
+
and abs(pkl.stat().st_size - stim_pkl.stat().st_size) < 1e6
|
|
152
146
|
):
|
|
153
|
-
logger.debug(f
|
|
147
|
+
logger.debug(f"Deleting extra stim pkl copied to npexp: {pkl.stem}")
|
|
154
148
|
pkl.unlink()
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def validate_selected_workflow(
|
|
158
|
-
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def validate_selected_workflow(
|
|
152
|
+
session: PsyCodeSession, mouse: np_session.Mouse
|
|
153
|
+
) -> None:
|
|
154
|
+
for workflow in ("hab", "ephys"):
|
|
159
155
|
if (
|
|
160
156
|
workflow in session.value.lower()
|
|
161
|
-
and workflow not in mouse.mtrain.stage[
|
|
157
|
+
and workflow not in mouse.mtrain.stage["name"].lower()
|
|
162
158
|
) or (
|
|
163
|
-
session.value.lower() ==
|
|
159
|
+
session.value.lower() == "ephys"
|
|
160
|
+
and "hab" in mouse.mtrain.stage["name"].lower()
|
|
164
161
|
):
|
|
165
|
-
raise ValueError(
|
|
162
|
+
raise ValueError(
|
|
163
|
+
f"Workflow selected ({session.value}) does not match MTrain stage ({mouse.mtrain.stage['name']}): please check cells above."
|
|
164
|
+
)
|
|
165
|
+
|
|
166
166
|
|
|
167
|
-
|
|
168
167
|
class Hab(PsyCodeMixin, np_workflows.PipelineHab):
|
|
169
168
|
def __init__(self, *args, **kwargs):
|
|
170
169
|
self.services = (
|
|
@@ -209,9 +208,10 @@ def new_experiment(
|
|
|
209
208
|
case _:
|
|
210
209
|
raise ValueError(f"Invalid workflow type: {workflow}")
|
|
211
210
|
experiment.workflow = workflow
|
|
212
|
-
|
|
211
|
+
|
|
213
212
|
with contextlib.suppress(Exception):
|
|
214
|
-
np_logging.web(f
|
|
215
|
-
|
|
216
|
-
|
|
213
|
+
np_logging.web(f"PsyCode_{experiment.workflow.name.lower()}").info(
|
|
214
|
+
f"{experiment} created"
|
|
215
|
+
)
|
|
217
216
|
|
|
217
|
+
return experiment
|
|
@@ -1,17 +1,6 @@
|
|
|
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
1
|
import IPython.display
|
|
9
2
|
import ipywidgets as ipw
|
|
10
|
-
import np_config
|
|
11
|
-
import np_logging
|
|
12
3
|
import np_session
|
|
13
|
-
import np_workflows
|
|
14
|
-
from pyparsing import Any
|
|
15
4
|
|
|
16
5
|
from np_workflows.experiments.openscope_psycode.main_psycode_pilot import PsyCodeSession
|
|
17
6
|
|
|
@@ -20,8 +9,11 @@ global_state = {}
|
|
|
20
9
|
|
|
21
10
|
# for widget, before creating a experiment --------------------------------------------- #
|
|
22
11
|
|
|
12
|
+
|
|
23
13
|
class SelectedSession:
|
|
24
|
-
def __init__(
|
|
14
|
+
def __init__(
|
|
15
|
+
self, session: str | PsyCodeSession, mouse: str | int | np_session.Mouse
|
|
16
|
+
):
|
|
25
17
|
if isinstance(session, str):
|
|
26
18
|
session = PsyCodeSession(session)
|
|
27
19
|
self.session = session
|
|
@@ -48,17 +40,19 @@ def PsyCode_workflow_widget(
|
|
|
48
40
|
options=tuple(_.value for _ in PsyCodeSession),
|
|
49
41
|
description="Session",
|
|
50
42
|
)
|
|
51
|
-
|
|
43
|
+
|
|
52
44
|
def update_selection():
|
|
53
45
|
selection.__init__(str(session_dropdown.value), str(mouse))
|
|
54
|
-
|
|
55
|
-
if
|
|
46
|
+
|
|
47
|
+
if previously_selected_value := global_state.get("selected_session"):
|
|
56
48
|
session_dropdown.value = previously_selected_value
|
|
57
49
|
update_selection()
|
|
58
|
-
|
|
50
|
+
|
|
59
51
|
console = ipw.Output()
|
|
60
52
|
with console:
|
|
61
|
-
if last_session := np_session.Mouse(selection.mouse).state.get(
|
|
53
|
+
if last_session := np_session.Mouse(selection.mouse).state.get(
|
|
54
|
+
"last_PsyCode_session"
|
|
55
|
+
):
|
|
62
56
|
print(f"{mouse} last session: {last_session}")
|
|
63
57
|
print(f"Selected: {selection.session}")
|
|
64
58
|
|
|
@@ -74,9 +68,9 @@ def PsyCode_workflow_widget(
|
|
|
74
68
|
update_selection()
|
|
75
69
|
with console:
|
|
76
70
|
print(f"Selected: {selection.session}")
|
|
77
|
-
global_state[
|
|
78
|
-
|
|
79
|
-
session_dropdown.observe(update, names=
|
|
71
|
+
global_state["selected_session"] = selection.session.value
|
|
72
|
+
|
|
73
|
+
session_dropdown.observe(update, names="value")
|
|
80
74
|
|
|
81
75
|
IPython.display.display(ipw.VBox([session_dropdown, console]))
|
|
82
76
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
from .main_V2_pilot import
|
|
1
|
+
from .main_V2_pilot import Ephys, Hab, new_experiment, validate_selected_workflow
|
|
2
2
|
from .V2_workflow_widget import V2_workflow_widget
|
|
@@ -1,41 +1,22 @@
|
|
|
1
|
-
import configparser
|
|
2
1
|
import contextlib
|
|
3
|
-
import copy
|
|
4
|
-
import dataclasses
|
|
5
|
-
import datetime
|
|
6
2
|
import enum
|
|
7
|
-
import functools
|
|
8
|
-
import pathlib
|
|
9
|
-
import platform
|
|
10
|
-
import shutil
|
|
11
|
-
import threading
|
|
12
3
|
import time
|
|
13
|
-
import zlib
|
|
14
|
-
from typing import ClassVar, Literal, NamedTuple, NoReturn, Optional, TypedDict
|
|
15
4
|
|
|
16
|
-
import IPython
|
|
17
|
-
import IPython.display
|
|
18
|
-
import ipywidgets as ipw
|
|
19
|
-
import np_config
|
|
20
5
|
import np_logging
|
|
21
|
-
import np_services
|
|
22
6
|
import np_session
|
|
23
|
-
import np_workflows
|
|
24
|
-
import PIL.Image
|
|
25
|
-
import pydantic
|
|
26
|
-
from pyparsing import Any
|
|
27
7
|
from np_services import (
|
|
28
|
-
Service,
|
|
29
8
|
Finalizable,
|
|
30
|
-
|
|
31
|
-
|
|
9
|
+
MouseDirector,
|
|
10
|
+
NewScaleCoordinateRecorder,
|
|
32
11
|
OpenEphys,
|
|
12
|
+
Service,
|
|
13
|
+
SessionCamstim,
|
|
33
14
|
Sync,
|
|
34
15
|
VideoMVR,
|
|
35
|
-
NewScaleCoordinateRecorder,
|
|
36
|
-
MouseDirector,
|
|
37
16
|
)
|
|
38
17
|
|
|
18
|
+
import np_workflows
|
|
19
|
+
|
|
39
20
|
logger = np_logging.getLogger(__name__)
|
|
40
21
|
|
|
41
22
|
|
|
@@ -49,16 +30,16 @@ class V2Session(enum.Enum):
|
|
|
49
30
|
|
|
50
31
|
class V2Mixin:
|
|
51
32
|
"""Provides project-specific methods and attributes, mainly related to camstim scripts."""
|
|
52
|
-
|
|
33
|
+
|
|
53
34
|
workflow: V2Session
|
|
54
35
|
"""Enum for particular workflow/session, e.g. PRETEST, HAB_60, HAB_90,
|
|
55
36
|
EPHYS."""
|
|
56
|
-
|
|
37
|
+
|
|
57
38
|
session: np_session.PipelineSession
|
|
58
39
|
mouse: np_session.Mouse
|
|
59
40
|
user: np_session.User
|
|
60
41
|
platform_json: np_session.PlatformJson
|
|
61
|
-
|
|
42
|
+
|
|
62
43
|
@property
|
|
63
44
|
def recorders(self) -> tuple[Service, ...]:
|
|
64
45
|
"""Services to be started before stimuli run, and stopped after. Session-dependent."""
|
|
@@ -70,11 +51,11 @@ class V2Mixin:
|
|
|
70
51
|
|
|
71
52
|
@property
|
|
72
53
|
def stims(self) -> tuple[Service, ...]:
|
|
73
|
-
return (SessionCamstim,
|
|
74
|
-
|
|
54
|
+
return (SessionCamstim,)
|
|
55
|
+
|
|
75
56
|
def initialize_and_test_services(self) -> None:
|
|
76
57
|
"""Configure, initialize (ie. reset), then test all services."""
|
|
77
|
-
|
|
58
|
+
|
|
78
59
|
MouseDirector.user = self.user.id
|
|
79
60
|
MouseDirector.mouse = self.mouse.id
|
|
80
61
|
|
|
@@ -92,79 +73,95 @@ class V2Mixin:
|
|
|
92
73
|
|
|
93
74
|
def update_state(self) -> None:
|
|
94
75
|
"Store useful but non-essential info."
|
|
95
|
-
self.mouse.state[
|
|
96
|
-
self.mouse.state[
|
|
76
|
+
self.mouse.state["last_session"] = self.session.id
|
|
77
|
+
self.mouse.state["last_V2_session"] = str(self.workflow)
|
|
97
78
|
if self.mouse == 366122:
|
|
98
79
|
return
|
|
99
80
|
match self.workflow:
|
|
100
81
|
case V2Session.PRETEST:
|
|
101
82
|
return
|
|
102
83
|
case V2Session.HAB:
|
|
103
|
-
self.session.project.state[
|
|
84
|
+
self.session.project.state["latest_hab"] = self.session.id
|
|
104
85
|
case V2Session.EPHYS:
|
|
105
|
-
self.session.project.state[
|
|
106
|
-
self.session.project.state[
|
|
107
|
-
|
|
86
|
+
self.session.project.state["latest_ephys"] = self.session.id
|
|
87
|
+
self.session.project.state["sessions"] = self.session.project.state.get(
|
|
88
|
+
"sessions", []
|
|
89
|
+
) + [self.session.id]
|
|
90
|
+
|
|
108
91
|
def run_stim(self) -> None:
|
|
109
92
|
|
|
110
93
|
self.update_state()
|
|
111
|
-
|
|
94
|
+
|
|
112
95
|
if not SessionCamstim.is_ready_to_start():
|
|
113
96
|
raise RuntimeError("SessionCamstim is not ready to start.")
|
|
114
|
-
|
|
115
|
-
np_logging.web(f
|
|
97
|
+
|
|
98
|
+
np_logging.web(f"V2_{self.workflow.name.lower()}").info(
|
|
99
|
+
f"Started session {self.mouse.mtrain.stage['name']}"
|
|
100
|
+
)
|
|
116
101
|
SessionCamstim.start()
|
|
117
|
-
|
|
102
|
+
|
|
118
103
|
with contextlib.suppress(Exception):
|
|
119
104
|
while not SessionCamstim.is_ready_to_start():
|
|
120
105
|
time.sleep(2.5)
|
|
121
|
-
|
|
106
|
+
|
|
122
107
|
if isinstance(SessionCamstim, Finalizable):
|
|
123
108
|
SessionCamstim.finalize()
|
|
124
109
|
|
|
125
110
|
with contextlib.suppress(Exception):
|
|
126
|
-
np_logging.web(f
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
111
|
+
np_logging.web(f"V2_{self.workflow.name.lower()}").info(
|
|
112
|
+
f"Finished session {self.mouse.mtrain.stage['name']}"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def copy_data_files(self) -> None:
|
|
130
116
|
super().copy_data_files()
|
|
131
|
-
|
|
117
|
+
|
|
132
118
|
# When all processing completes, camstim Agent class passes data and uuid to
|
|
133
119
|
# /camstim/lims BehaviorSession class, and write_behavior_data() writes a
|
|
134
120
|
# final .pkl with default name YYYYMMDDSSSS_mouseID_foragingID.pkl
|
|
135
121
|
# - if we have a foraging ID, we can search for that
|
|
136
|
-
if None == (
|
|
137
|
-
|
|
122
|
+
if None == (
|
|
123
|
+
stim_pkl := next(
|
|
124
|
+
self.session.npexp_path.glob(
|
|
125
|
+
f"{self.session.date:%y%m%d}*_{self.session.mouse}_*.pkl"
|
|
126
|
+
),
|
|
127
|
+
None,
|
|
128
|
+
)
|
|
129
|
+
):
|
|
130
|
+
logger.warning(
|
|
131
|
+
"Did not find stim file on npexp matching the format `YYYYMMDDSSSS_mouseID_foragingID.pkl`"
|
|
132
|
+
)
|
|
138
133
|
return
|
|
139
134
|
assert stim_pkl
|
|
140
135
|
if not self.session.platform_json.foraging_id:
|
|
141
|
-
self.session.platform_json.foraging_id = stim_pkl.stem.split(
|
|
142
|
-
new_stem = f
|
|
143
|
-
logger.debug(f
|
|
136
|
+
self.session.platform_json.foraging_id = stim_pkl.stem.split("_")[-1]
|
|
137
|
+
new_stem = f"{self.session.folder}.stim"
|
|
138
|
+
logger.debug(f"Renaming stim file copied to npexp: {stim_pkl} -> {new_stem}")
|
|
144
139
|
stim_pkl = stim_pkl.rename(stim_pkl.with_stem(new_stem))
|
|
145
|
-
|
|
140
|
+
|
|
146
141
|
# remove other stim pkl, which is nearly identical, if it was also copied
|
|
147
|
-
for pkl in self.session.npexp_path.glob(
|
|
142
|
+
for pkl in self.session.npexp_path.glob("*.pkl"):
|
|
148
143
|
if (
|
|
149
144
|
self.session.folder not in pkl.stem
|
|
150
|
-
and
|
|
151
|
-
abs(pkl.stat().st_size - stim_pkl.stat().st_size) < 1e6
|
|
145
|
+
and abs(pkl.stat().st_size - stim_pkl.stat().st_size) < 1e6
|
|
152
146
|
):
|
|
153
|
-
logger.debug(f
|
|
147
|
+
logger.debug(f"Deleting extra stim pkl copied to npexp: {pkl.stem}")
|
|
154
148
|
pkl.unlink()
|
|
155
|
-
|
|
156
|
-
|
|
149
|
+
|
|
150
|
+
|
|
157
151
|
def validate_selected_workflow(session: V2Session, mouse: np_session.Mouse) -> None:
|
|
158
|
-
for workflow in (
|
|
152
|
+
for workflow in ("hab", "ephys"):
|
|
159
153
|
if (
|
|
160
154
|
workflow in session.value.lower()
|
|
161
|
-
and workflow not in mouse.mtrain.stage[
|
|
155
|
+
and workflow not in mouse.mtrain.stage["name"].lower()
|
|
162
156
|
) or (
|
|
163
|
-
session.value.lower() ==
|
|
157
|
+
session.value.lower() == "ephys"
|
|
158
|
+
and "hab" in mouse.mtrain.stage["name"].lower()
|
|
164
159
|
):
|
|
165
|
-
raise ValueError(
|
|
160
|
+
raise ValueError(
|
|
161
|
+
f"Workflow selected ({session.value}) does not match MTrain stage ({mouse.mtrain.stage['name']}): please check cells above."
|
|
162
|
+
)
|
|
163
|
+
|
|
166
164
|
|
|
167
|
-
|
|
168
165
|
class Hab(V2Mixin, np_workflows.PipelineHab):
|
|
169
166
|
def __init__(self, *args, **kwargs):
|
|
170
167
|
self.services = (
|
|
@@ -209,9 +206,10 @@ def new_experiment(
|
|
|
209
206
|
case _:
|
|
210
207
|
raise ValueError(f"Invalid workflow type: {workflow}")
|
|
211
208
|
experiment.workflow = workflow
|
|
212
|
-
|
|
209
|
+
|
|
213
210
|
with contextlib.suppress(Exception):
|
|
214
|
-
np_logging.web(f
|
|
215
|
-
|
|
216
|
-
|
|
211
|
+
np_logging.web(f"V2_{experiment.workflow.name.lower()}").info(
|
|
212
|
+
f"{experiment} created"
|
|
213
|
+
)
|
|
217
214
|
|
|
215
|
+
return experiment
|
|
@@ -1,17 +1,6 @@
|
|
|
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
1
|
import IPython.display
|
|
9
2
|
import ipywidgets as ipw
|
|
10
|
-
import np_config
|
|
11
|
-
import np_logging
|
|
12
3
|
import np_session
|
|
13
|
-
import np_workflows
|
|
14
|
-
from pyparsing import Any
|
|
15
4
|
|
|
16
5
|
from np_workflows.experiments.openscope_V2.main_V2_pilot import V2Session
|
|
17
6
|
|
|
@@ -20,6 +9,7 @@ global_state = {}
|
|
|
20
9
|
|
|
21
10
|
# for widget, before creating a experiment --------------------------------------------- #
|
|
22
11
|
|
|
12
|
+
|
|
23
13
|
class SelectedSession:
|
|
24
14
|
def __init__(self, session: str | V2Session, mouse: str | int | np_session.Mouse):
|
|
25
15
|
if isinstance(session, str):
|
|
@@ -48,17 +38,19 @@ def V2_workflow_widget(
|
|
|
48
38
|
options=tuple(_.value for _ in V2Session),
|
|
49
39
|
description="Session",
|
|
50
40
|
)
|
|
51
|
-
|
|
41
|
+
|
|
52
42
|
def update_selection():
|
|
53
43
|
selection.__init__(str(session_dropdown.value), str(mouse))
|
|
54
|
-
|
|
55
|
-
if
|
|
44
|
+
|
|
45
|
+
if previously_selected_value := global_state.get("selected_session"):
|
|
56
46
|
session_dropdown.value = previously_selected_value
|
|
57
47
|
update_selection()
|
|
58
|
-
|
|
48
|
+
|
|
59
49
|
console = ipw.Output()
|
|
60
50
|
with console:
|
|
61
|
-
if last_session := np_session.Mouse(selection.mouse).state.get(
|
|
51
|
+
if last_session := np_session.Mouse(selection.mouse).state.get(
|
|
52
|
+
"last_V2_session"
|
|
53
|
+
):
|
|
62
54
|
print(f"{mouse} last session: {last_session}")
|
|
63
55
|
print(f"Selected: {selection.session}")
|
|
64
56
|
|
|
@@ -74,9 +66,9 @@ def V2_workflow_widget(
|
|
|
74
66
|
update_selection()
|
|
75
67
|
with console:
|
|
76
68
|
print(f"Selected: {selection.session}")
|
|
77
|
-
global_state[
|
|
78
|
-
|
|
79
|
-
session_dropdown.observe(update, names=
|
|
69
|
+
global_state["selected_session"] = selection.session.value
|
|
70
|
+
|
|
71
|
+
session_dropdown.observe(update, names="value")
|
|
80
72
|
|
|
81
73
|
IPython.display.display(ipw.VBox([session_dropdown, console]))
|
|
82
74
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
from .main_vippo_pilot import
|
|
1
|
+
from .main_vippo_pilot import Ephys, Hab, new_experiment, validate_selected_workflow
|
|
2
2
|
from .vippo_workflow_widget import vippo_workflow_widget
|