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,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
"""
|
|
3
2
|
optotagging.py
|
|
4
3
|
|
|
@@ -9,37 +8,30 @@ by joshs@alleninstitute.org
|
|
|
9
8
|
(c) 2018 Allen Institute for Brain Science
|
|
10
9
|
|
|
11
10
|
"""
|
|
12
|
-
import camstim # ensures "magic" gets setup properly by importing first
|
|
13
|
-
import logging # must occur after camstim import for "magic"
|
|
14
|
-
from camstim.zro import agent
|
|
15
|
-
|
|
16
|
-
import numpy as np
|
|
17
|
-
from toolbox.IO.nidaq import AnalogOutput
|
|
18
|
-
from toolbox.IO.nidaq import DigitalOutput
|
|
19
11
|
|
|
20
12
|
import datetime
|
|
21
|
-
import
|
|
22
|
-
import time
|
|
13
|
+
import logging # must occur after camstim import for "magic"
|
|
23
14
|
import pickle as pkl
|
|
15
|
+
import time
|
|
24
16
|
|
|
17
|
+
import numpy as np
|
|
18
|
+
from camstim.zro import agent
|
|
19
|
+
from toolbox.IO.nidaq import AnalogOutput, DigitalOutput
|
|
25
20
|
|
|
26
21
|
# %%
|
|
27
22
|
|
|
28
23
|
|
|
29
|
-
def run_optotagging(levels, conditions, waveforms, isis, sampleRate=10000.):
|
|
30
|
-
|
|
31
|
-
from toolbox.IO.nidaq import AnalogOutput
|
|
32
|
-
from toolbox.IO.nidaq import DigitalOutput
|
|
24
|
+
def run_optotagging(levels, conditions, waveforms, isis, sampleRate=10000.0):
|
|
33
25
|
|
|
34
26
|
sweep_on = np.array([0, 0, 1, 0, 0, 0, 0, 0], dtype=np.uint8)
|
|
35
27
|
stim_on = np.array([0, 0, 1, 1, 0, 0, 0, 0], dtype=np.uint8)
|
|
36
28
|
stim_off = np.array([0, 0, 1, 0, 0, 0, 0, 0], dtype=np.uint8)
|
|
37
29
|
sweep_off = np.array([0, 0, 0, 0, 0, 0, 0, 0], dtype=np.uint8)
|
|
38
30
|
|
|
39
|
-
ao = AnalogOutput(
|
|
31
|
+
ao = AnalogOutput("Dev1", channels=[1])
|
|
40
32
|
ao.cfg_sample_clock(sampleRate)
|
|
41
33
|
|
|
42
|
-
do = DigitalOutput(
|
|
34
|
+
do = DigitalOutput("Dev1", 2)
|
|
43
35
|
|
|
44
36
|
do.start()
|
|
45
37
|
ao.start()
|
|
@@ -62,71 +54,123 @@ def run_optotagging(levels, conditions, waveforms, isis, sampleRate=10000.):
|
|
|
62
54
|
do.clear()
|
|
63
55
|
ao.clear()
|
|
64
56
|
|
|
57
|
+
|
|
65
58
|
# %%
|
|
66
59
|
|
|
67
60
|
|
|
68
|
-
def generatePulseTrain(
|
|
61
|
+
def generatePulseTrain(
|
|
62
|
+
pulseWidth, pulseInterval, numRepeats, riseTime, sampleRate=10000.0
|
|
63
|
+
):
|
|
69
64
|
|
|
70
65
|
data = np.zeros((int(sampleRate),), dtype=np.float64)
|
|
71
|
-
|
|
66
|
+
# rise_samples =
|
|
72
67
|
|
|
73
68
|
rise_and_fall = (
|
|
74
|
-
(
|
|
69
|
+
(
|
|
70
|
+
(
|
|
71
|
+
1
|
|
72
|
+
- np.cos(
|
|
73
|
+
np.arange(sampleRate * riseTime / 1000.0, dtype=np.float64)
|
|
74
|
+
* 2
|
|
75
|
+
* np.pi
|
|
76
|
+
/ 10
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
+ 1
|
|
80
|
+
)
|
|
81
|
+
- 1
|
|
82
|
+
) / 2
|
|
75
83
|
half_length = rise_and_fall.size / 2
|
|
76
84
|
rise = rise_and_fall[:half_length]
|
|
77
85
|
fall = rise_and_fall[half_length:]
|
|
78
86
|
|
|
79
|
-
peak_samples = int(sampleRate*(pulseWidth-riseTime*2)/1000)
|
|
87
|
+
peak_samples = int(sampleRate * (pulseWidth - riseTime * 2) / 1000)
|
|
80
88
|
peak = np.ones((peak_samples,))
|
|
81
89
|
|
|
82
|
-
pulse = np.concatenate((rise,
|
|
83
|
-
peak,
|
|
84
|
-
fall))
|
|
90
|
+
pulse = np.concatenate((rise, peak, fall))
|
|
85
91
|
|
|
86
|
-
interval = int(pulseInterval*sampleRate/1000.)
|
|
92
|
+
interval = int(pulseInterval * sampleRate / 1000.0)
|
|
87
93
|
|
|
88
94
|
for i in range(0, numRepeats):
|
|
89
|
-
data[i*interval:i*interval+pulse.size] = pulse
|
|
95
|
+
data[i * interval : i * interval + pulse.size] = pulse
|
|
90
96
|
|
|
91
97
|
return data
|
|
92
98
|
|
|
93
99
|
|
|
94
100
|
# %% create waveforms
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
|
|
103
|
+
def optotagging(
|
|
104
|
+
mouseID, operation_mode="experiment", level_list=[1.15, 1.28, 1.345], genotype=None
|
|
105
|
+
):
|
|
97
106
|
|
|
98
107
|
sampleRate = 10000
|
|
99
108
|
|
|
100
109
|
# 1 s cosine ramp:
|
|
101
|
-
data_cosine = (
|
|
102
|
-
|
|
110
|
+
data_cosine = (
|
|
111
|
+
(
|
|
112
|
+
(
|
|
113
|
+
1
|
|
114
|
+
- np.cos(
|
|
115
|
+
np.arange(sampleRate, dtype=np.float64) * 2 * np.pi / sampleRate
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
+ 1
|
|
119
|
+
)
|
|
120
|
+
- 1
|
|
121
|
+
) / 2 # create raised cosine waveform
|
|
103
122
|
|
|
104
123
|
# 1 ms cosine ramp:
|
|
105
124
|
rise_and_fall = (
|
|
106
|
-
(
|
|
125
|
+
(
|
|
126
|
+
(
|
|
127
|
+
1
|
|
128
|
+
- np.cos(
|
|
129
|
+
np.arange(sampleRate * 0.001, dtype=np.float64) * 2 * np.pi / 10
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
+ 1
|
|
133
|
+
)
|
|
134
|
+
- 1
|
|
135
|
+
) / 2
|
|
107
136
|
half_length = rise_and_fall.size / 2
|
|
108
137
|
|
|
109
138
|
# pulses with cosine ramp:
|
|
110
|
-
pulse_2ms = np.concatenate(
|
|
111
|
-
(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
139
|
+
pulse_2ms = np.concatenate(
|
|
140
|
+
(
|
|
141
|
+
rise_and_fall[:half_length],
|
|
142
|
+
np.ones((int(sampleRate * 0.001),)),
|
|
143
|
+
rise_and_fall[half_length:],
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
pulse_5ms = np.concatenate(
|
|
147
|
+
(
|
|
148
|
+
rise_and_fall[:half_length],
|
|
149
|
+
np.ones((int(sampleRate * 0.004),)),
|
|
150
|
+
rise_and_fall[half_length:],
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
pulse_10ms = np.concatenate(
|
|
154
|
+
(
|
|
155
|
+
rise_and_fall[:half_length],
|
|
156
|
+
np.ones((int(sampleRate * 0.009),)),
|
|
157
|
+
rise_and_fall[half_length:],
|
|
158
|
+
)
|
|
159
|
+
)
|
|
116
160
|
|
|
117
161
|
data_2ms_10Hz = np.zeros((sampleRate,), dtype=np.float64)
|
|
118
162
|
|
|
119
163
|
for i in range(0, 10):
|
|
120
164
|
interval = sampleRate / 10
|
|
121
|
-
data_2ms_10Hz[i*interval:i*interval+pulse_2ms.size] = pulse_2ms
|
|
165
|
+
data_2ms_10Hz[i * interval : i * interval + pulse_2ms.size] = pulse_2ms
|
|
122
166
|
|
|
123
167
|
data_5ms = np.zeros((sampleRate,), dtype=np.float64)
|
|
124
|
-
data_5ms[:pulse_5ms.size] = pulse_5ms
|
|
168
|
+
data_5ms[: pulse_5ms.size] = pulse_5ms
|
|
125
169
|
|
|
126
170
|
data_10ms = np.zeros((sampleRate,), dtype=np.float64)
|
|
127
|
-
data_10ms[:pulse_10ms.size] = pulse_10ms
|
|
171
|
+
data_10ms[: pulse_10ms.size] = pulse_10ms
|
|
128
172
|
|
|
129
|
-
data_10s = np.zeros((sampleRate*10,), dtype=np.float64)
|
|
173
|
+
data_10s = np.zeros((sampleRate * 10,), dtype=np.float64)
|
|
130
174
|
data_10s[:-2] = 1
|
|
131
175
|
|
|
132
176
|
# %% for experiment
|
|
@@ -138,8 +182,8 @@ def optotagging(mouseID, operation_mode='experiment', level_list=[1.15, 1.28, 1.
|
|
|
138
182
|
condition_list = [2, 3]
|
|
139
183
|
waveforms = [data_2ms_10Hz, data_5ms, data_10ms, data_cosine]
|
|
140
184
|
|
|
141
|
-
opto_levels = np.array(level_list*numRepeats*len(condition_list)) # BLUE
|
|
142
|
-
opto_conditions = condition_list*numRepeats*len(level_list)
|
|
185
|
+
opto_levels = np.array(level_list * numRepeats * len(condition_list)) # BLUE
|
|
186
|
+
opto_conditions = condition_list * numRepeats * len(level_list)
|
|
143
187
|
opto_conditions = np.sort(opto_conditions)
|
|
144
188
|
opto_isis = np.random.random(opto_levels.shape) * isi_rand + isi
|
|
145
189
|
|
|
@@ -151,7 +195,7 @@ def optotagging(mouseID, operation_mode='experiment', level_list=[1.15, 1.28, 1.
|
|
|
151
195
|
|
|
152
196
|
# %% for testing
|
|
153
197
|
|
|
154
|
-
if operation_mode ==
|
|
198
|
+
if operation_mode == "test_levels":
|
|
155
199
|
isi = 2.0
|
|
156
200
|
isi_rand = 0.0
|
|
157
201
|
|
|
@@ -160,60 +204,69 @@ def optotagging(mouseID, operation_mode='experiment', level_list=[1.15, 1.28, 1.
|
|
|
160
204
|
condition_list = [0]
|
|
161
205
|
waveforms = [data_10s, data_10s]
|
|
162
206
|
|
|
163
|
-
opto_levels = np.array(level_list*numRepeats *
|
|
164
|
-
|
|
165
|
-
opto_conditions = condition_list*numRepeats*len(level_list)
|
|
207
|
+
opto_levels = np.array(level_list * numRepeats * len(condition_list)) # BLUE
|
|
208
|
+
opto_conditions = condition_list * numRepeats * len(level_list)
|
|
166
209
|
opto_conditions = np.sort(opto_conditions)
|
|
167
210
|
opto_isis = np.random.random(opto_levels.shape) * isi_rand + isi
|
|
168
211
|
|
|
169
|
-
elif operation_mode ==
|
|
212
|
+
elif operation_mode == "pretest":
|
|
170
213
|
numRepeats = 1
|
|
171
214
|
|
|
172
215
|
condition_list = [0]
|
|
173
|
-
data_2s = data_10s[-sampleRate*2:]
|
|
216
|
+
data_2s = data_10s[-sampleRate * 2 :]
|
|
174
217
|
waveforms = [data_2s]
|
|
175
218
|
|
|
176
|
-
opto_levels = np.array(level_list*numRepeats *
|
|
177
|
-
|
|
178
|
-
opto_conditions = condition_list*numRepeats*len(level_list)
|
|
219
|
+
opto_levels = np.array(level_list * numRepeats * len(condition_list)) # BLUE
|
|
220
|
+
opto_conditions = condition_list * numRepeats * len(level_list)
|
|
179
221
|
opto_conditions = np.sort(opto_conditions)
|
|
180
|
-
opto_isis = [1]*len(opto_conditions)
|
|
222
|
+
opto_isis = [1] * len(opto_conditions)
|
|
181
223
|
# %%
|
|
182
224
|
|
|
183
225
|
outputDirectory = agent.OUTPUT_DIR
|
|
184
|
-
fileDate =
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
226
|
+
fileDate = (
|
|
227
|
+
str(datetime.datetime.now())
|
|
228
|
+
.replace(":", "")
|
|
229
|
+
.replace(".", "")
|
|
230
|
+
.replace("-", "")
|
|
231
|
+
.replace(" ", "")[2:14]
|
|
232
|
+
)
|
|
233
|
+
fileName = outputDirectory + "/" + fileDate + "_" + mouseID + ".opto.pkl"
|
|
234
|
+
|
|
235
|
+
print("saving info to: " + fileName)
|
|
236
|
+
fl = open(fileName, "wb")
|
|
190
237
|
output = {}
|
|
191
238
|
|
|
192
|
-
output[
|
|
193
|
-
output[
|
|
194
|
-
output[
|
|
195
|
-
output[
|
|
239
|
+
output["opto_levels"] = opto_levels
|
|
240
|
+
output["opto_conditions"] = opto_conditions
|
|
241
|
+
output["opto_ISIs"] = opto_isis
|
|
242
|
+
output["opto_waveforms"] = waveforms
|
|
196
243
|
|
|
197
244
|
pkl.dump(output, fl)
|
|
198
245
|
fl.close()
|
|
199
|
-
print(
|
|
246
|
+
print("saved.")
|
|
200
247
|
|
|
201
248
|
# %%
|
|
202
|
-
run_optotagging(
|
|
203
|
-
|
|
249
|
+
run_optotagging(
|
|
250
|
+
opto_levels, opto_conditions, waveforms, opto_isis, float(sampleRate)
|
|
251
|
+
)
|
|
204
252
|
|
|
205
253
|
|
|
206
254
|
# %%
|
|
207
255
|
if __name__ == "__main__":
|
|
208
|
-
import json
|
|
209
256
|
import argparse
|
|
257
|
+
import json
|
|
210
258
|
|
|
211
259
|
parser = argparse.ArgumentParser()
|
|
212
|
-
parser.add_argument(
|
|
260
|
+
parser.add_argument(
|
|
261
|
+
"json_params",
|
|
262
|
+
type=str,
|
|
263
|
+
)
|
|
213
264
|
args, _ = parser.parse_known_args()
|
|
214
265
|
|
|
215
|
-
with open(
|
|
266
|
+
with open(
|
|
267
|
+
args.json_params,
|
|
268
|
+
) as f:
|
|
216
269
|
json_params = json.load(f)
|
|
217
270
|
|
|
218
|
-
logging.info(
|
|
271
|
+
logging.info("Optotagging with params: %s" % json_params)
|
|
219
272
|
optotagging(**json_params)
|
|
@@ -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_loop.main_loop_pilot import LoopSession
|
|
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 | LoopSession, mouse: str | int | np_session.Mouse):
|
|
25
15
|
if isinstance(session, str):
|
|
@@ -48,17 +38,19 @@ def loop_workflow_widget(
|
|
|
48
38
|
options=tuple(_.value for _ in LoopSession),
|
|
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_loop_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 loop_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,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 LoopSession(enum.Enum):
|
|
|
49
30
|
|
|
50
31
|
class LoopMixin:
|
|
51
32
|
"""Provides project-specific methods and attributes, mainly related to camstim scripts."""
|
|
52
|
-
|
|
33
|
+
|
|
53
34
|
workflow: LoopSession
|
|
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 LoopMixin:
|
|
|
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 LoopMixin:
|
|
|
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_Loop_session"] = str(self.workflow)
|
|
97
78
|
if self.mouse == 366122:
|
|
98
79
|
return
|
|
99
80
|
match self.workflow:
|
|
100
81
|
case LoopSession.PRETEST:
|
|
101
82
|
return
|
|
102
83
|
case LoopSession.HAB:
|
|
103
|
-
self.session.project.state[
|
|
84
|
+
self.session.project.state["latest_hab"] = self.session.id
|
|
104
85
|
case LoopSession.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"Loop_{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"Loop_{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: LoopSession, 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(LoopMixin, 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"Loop_{experiment.workflow.name.lower()}").info(
|
|
212
|
+
f"{experiment} created"
|
|
213
|
+
)
|
|
217
214
|
|
|
215
|
+
return experiment
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
from .main_psycode_pilot import
|
|
1
|
+
from .main_psycode_pilot import Ephys, Hab, new_experiment, validate_selected_workflow
|
|
2
2
|
from .psycode_workflow_widget import PsyCode_workflow_widget
|