labdata 0.0.23__tar.gz → 0.0.25__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {labdata-0.0.23 → labdata-0.0.25}/PKG-INFO +2 -2
- {labdata-0.0.23 → labdata-0.0.25}/labdata/__init__.py +1 -1
- {labdata-0.0.23 → labdata-0.0.25}/labdata/cli.py +1 -1
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/caiman.py +8 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/ephys.py +15 -11
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/suite2p.py +81 -33
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/utils.py +2 -2
- labdata-0.0.25/labdata/dashboard/compute.py +112 -0
- labdata-0.0.25/labdata/dashboard/decisiontask.py +331 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/dashboard/explorer/spikesorting.py +47 -13
- labdata-0.0.25/labdata/dashboard/index.py +225 -0
- labdata-0.0.25/labdata/dashboard/notes.py +184 -0
- labdata-0.0.25/labdata/dashboard/procedures.py +284 -0
- labdata-0.0.25/labdata/dashboard/segmentation.py +414 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/dashboard/sorting.py +1 -1
- {labdata-0.0.23 → labdata-0.0.25}/labdata/dashboard/video.py +48 -12
- {labdata-0.0.23 → labdata-0.0.25}/labdata/rules/ephys.py +7 -1
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/ephys.py +9 -5
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/general.py +6 -5
- {labdata-0.0.23 → labdata-0.0.25}/labdata/stacks.py +18 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata.egg-info/PKG-INFO +2 -2
- {labdata-0.0.23 → labdata-0.0.25}/labdata.egg-info/SOURCES.txt +2 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata.egg-info/requires.txt +1 -1
- {labdata-0.0.23 → labdata-0.0.25}/pyproject.toml +1 -1
- labdata-0.0.23/labdata/dashboard/compute.py +0 -54
- labdata-0.0.23/labdata/dashboard/decisiontask.py +0 -144
- labdata-0.0.23/labdata/dashboard/index.py +0 -253
- labdata-0.0.23/labdata/dashboard/segmentation.py +0 -49
- {labdata-0.0.23 → labdata-0.0.25}/LICENSE +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/README.md +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/__init__.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/ec2.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/pose.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/schedulers.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/compute/singularity.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/copy.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/dashboard/__init__.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/dashboard/explorer/__init__.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/dashboard/explorer/cellsegmentation.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/export.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/rules/__init__.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/rules/imaging.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/rules/utils.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/s3.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/__init__.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/histology.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/onephoton.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/procedures.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/tasks.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/twophoton.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/utils.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/schema/video.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/utils.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata/widgets.py +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata.egg-info/dependency_links.txt +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata.egg-info/entry_points.txt +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/labdata.egg-info/top_level.txt +0 -0
- {labdata-0.0.23 → labdata-0.0.25}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: labdata
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.25
|
|
4
4
|
Summary: Package to manage data in experimental neuroscience labs
|
|
5
5
|
Author-email: Joao Couto <jpcouto@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/jcouto/labdata
|
|
@@ -23,7 +23,7 @@ Requires-Dist: dash; extra == "dashboard"
|
|
|
23
23
|
Requires-Dist: dash_bootstrap_components; extra == "dashboard"
|
|
24
24
|
Requires-Dist: dash-extensions; extra == "dashboard"
|
|
25
25
|
Requires-Dist: plotly; extra == "dashboard"
|
|
26
|
-
Requires-Dist: streamlit>1.
|
|
26
|
+
Requires-Dist: streamlit>1.54; extra == "dashboard"
|
|
27
27
|
Requires-Dist: streamlit-bokeh; extra == "dashboard"
|
|
28
28
|
Requires-Dist: scipy; extra == "dashboard"
|
|
29
29
|
Requires-Dist: scikit-image; extra == "dashboard"
|
|
@@ -163,6 +163,8 @@ class CaimanCompute(Suite2pCompute):
|
|
|
163
163
|
else:
|
|
164
164
|
frame_rate = (self.schema.Miniscope() & self.dataset_key).fetch1('frame_rate')
|
|
165
165
|
is_two_photon = False
|
|
166
|
+
if len((self.schema.File & dset).check_if_files_local()[0]) == 0:
|
|
167
|
+
self.files_existed = False
|
|
166
168
|
dat = dset.open()
|
|
167
169
|
parameters = (self.schema.CellSegmentationParams & f'parameter_set_num = {self.parameter_set_num}').fetch1()
|
|
168
170
|
|
|
@@ -271,6 +273,9 @@ class CaimanCompute(Suite2pCompute):
|
|
|
271
273
|
selectiondict = []
|
|
272
274
|
for icell,roi in enumerate(roi_masks):
|
|
273
275
|
roi_pixels, roi_pixels_values = get_roi_pixels(roi)
|
|
276
|
+
if len(roi_pixels)>5000:
|
|
277
|
+
print(f"[CaimanCompute] {icell} had an roi with {len(roi_pixels)} pixels. Skipping.")
|
|
278
|
+
continue
|
|
274
279
|
roidict.append(dict(planekey,
|
|
275
280
|
roi_num = icell,
|
|
276
281
|
roi_pixels = roi_pixels,
|
|
@@ -320,6 +325,9 @@ class CaimanCompute(Suite2pCompute):
|
|
|
320
325
|
print(f'[{self.name} job] Removing the temporary folder.')
|
|
321
326
|
import shutil
|
|
322
327
|
shutil.rmtree(temporary_folder)
|
|
328
|
+
if not self.files_existed:
|
|
329
|
+
for f in (self.schema.File & dset).get():
|
|
330
|
+
os.unlink(f)
|
|
323
331
|
else:
|
|
324
332
|
print(f'[{self.name} job] Kept the temporary folder {temporary_folder}.')
|
|
325
333
|
|
|
@@ -21,7 +21,7 @@ class SpksCompute(BaseCompute):
|
|
|
21
21
|
self.file_filters = ['.ap.']
|
|
22
22
|
# default parameters
|
|
23
23
|
self.parameters = dict(algorithm_name = 'spks_kilosort4.0',
|
|
24
|
-
motion_correction =
|
|
24
|
+
motion_correction = 1,
|
|
25
25
|
low_pass = 300.,
|
|
26
26
|
high_pass = 13000.)
|
|
27
27
|
self.parameter_keys = ['motion_correction','low_pass','high_pass','thresholds','remove_cross_duplicates',
|
|
@@ -151,7 +151,7 @@ class SpksCompute(BaseCompute):
|
|
|
151
151
|
if not args.thresholds is None:
|
|
152
152
|
self.parameters['thresholds'] = args.thresholds
|
|
153
153
|
if self.parameters['motion_correction'] < 2:
|
|
154
|
-
self.parameters['motion_correction'] =
|
|
154
|
+
self.parameters['motion_correction'] = self.parameters['motion_correction']
|
|
155
155
|
self.probe = args.probe
|
|
156
156
|
if not self.probe is None:
|
|
157
157
|
self.parameters['probe'] = int(self.probe) # submit a single probe
|
|
@@ -188,8 +188,8 @@ class SpksCompute(BaseCompute):
|
|
|
188
188
|
datasets += (self.schema.EphysRecording()& k).proj('subject_name','session_name','dataset_name').fetch(as_dict = True)
|
|
189
189
|
|
|
190
190
|
if not parameter_set_num is None:
|
|
191
|
-
datasets = ((self.schema.EphysRecording() & datasets) -
|
|
192
|
-
(self.schema.SpikeSorting() & f'parameter_set_num = {parameter_set_num}')).proj(
|
|
191
|
+
datasets = (self.schema.EphysRecording & ((self.schema.EphysRecording.ProbeSetting() & datasets) -
|
|
192
|
+
(self.schema.SpikeSorting() & f'parameter_set_num = {parameter_set_num}'))).proj(
|
|
193
193
|
'subject_name',
|
|
194
194
|
'session_name',
|
|
195
195
|
'dataset_name').fetch(as_dict = True)
|
|
@@ -314,7 +314,9 @@ class SpksCompute(BaseCompute):
|
|
|
314
314
|
# send the files to AWS # this will skip the raw data binary files
|
|
315
315
|
dataset = dict(**self.dataset_key[0])
|
|
316
316
|
dataset['dataset_name'] = f'spike_sorting/imec{probe_num}/{self.parameter_set_num}/{self.parameters["algorithm_name"]}'
|
|
317
|
-
src = [a for a in Path(results_folder).rglob('*') if not str(a).endswith('.bin')
|
|
317
|
+
src = [a for a in Path(results_folder).rglob('*') if not (str(a).endswith('.bin') or
|
|
318
|
+
str(a).endswith('.dat') or
|
|
319
|
+
str(a).endswith('.hdf5'))] # get the files minus the .bin/.dat
|
|
318
320
|
new_src = [Path(prefs['local_paths'][0])/a for a in self.schema.AnalysisFile().generate_filepaths(src,dataset)]
|
|
319
321
|
from tqdm.auto import tqdm
|
|
320
322
|
import shutil
|
|
@@ -445,7 +447,7 @@ class SpksCompute(BaseCompute):
|
|
|
445
447
|
shuffle = True)
|
|
446
448
|
from tqdm.auto import tqdm
|
|
447
449
|
# print(f'Collecting waveforms and saving for dataset:{idset}.')
|
|
448
|
-
# save these to zarr to be compressed faster
|
|
450
|
+
# save these to zarr to be compressed faster?
|
|
449
451
|
if self.use_hdf5: # zarr not implemented yet.
|
|
450
452
|
import h5py as h5
|
|
451
453
|
with h5.File(Path(results_folder)/f'waveforms{idset}.hdf5','w') as wavefile:
|
|
@@ -475,6 +477,7 @@ class SpksCompute(BaseCompute):
|
|
|
475
477
|
# insert the syncs
|
|
476
478
|
if len(event):
|
|
477
479
|
# Add stream
|
|
480
|
+
print(f'Inserting {len(event)} events.')
|
|
478
481
|
self.schema.DatasetEvents.insert1(dict(dataset_key,
|
|
479
482
|
stream_name = stream_name),
|
|
480
483
|
skip_duplicates = True,
|
|
@@ -504,10 +507,10 @@ class SpksCompute(BaseCompute):
|
|
|
504
507
|
offset_samples = offset_samples - offsets[0],
|
|
505
508
|
segment = np.array(dat[offset_samples : offset_samples + nsamples])))
|
|
506
509
|
del dat
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
510
|
+
self.set_job_status(job_log = f'Completed {base_key}')
|
|
511
|
+
from labdata.schema import UnitMetrics
|
|
512
|
+
# limit number of jobs because of memory constraints
|
|
513
|
+
self.schema.UnitMetrics.populate(base_key, display_progress = True, processes = int(max(1,np.ceil(n_jobs/2))))
|
|
511
514
|
|
|
512
515
|
def extract_waveforms(self,udict, nchannels, results_folder, n_pre_samples,n_jobs, offsets):
|
|
513
516
|
# extract the waveforms
|
|
@@ -575,7 +578,8 @@ def get_waveforms_from_binary(binary_file,
|
|
|
575
578
|
n_jobs = 8):
|
|
576
579
|
from tqdm.auto import tqdm
|
|
577
580
|
from spks.io import map_binary
|
|
578
|
-
|
|
581
|
+
# get the spikes for kilosort from the temp_wh.dat file if available.
|
|
582
|
+
dat = map_binary(binary_file, nchannels = binary_file_nchannels)
|
|
579
583
|
# return as generator to avoid having to use huge amounts of memory.
|
|
580
584
|
res = Parallel(backend='loky',n_jobs=n_jobs,return_as = 'generator')(delayed(get_spike_waveforms)(
|
|
581
585
|
dat,
|
|
@@ -3,7 +3,7 @@ from .utils import BaseCompute
|
|
|
3
3
|
|
|
4
4
|
class Suite2pCompute(BaseCompute):
|
|
5
5
|
container = 'labdata-suite2p'
|
|
6
|
-
cuda =
|
|
6
|
+
cuda = True
|
|
7
7
|
name = 'suite2p'
|
|
8
8
|
url = 'http://github.com/mouseland/suite2p'
|
|
9
9
|
def __init__(self,job_id, project = None, allow_s3 = None, **kwargs):
|
|
@@ -163,8 +163,9 @@ class Suite2pCompute(BaseCompute):
|
|
|
163
163
|
return datasets
|
|
164
164
|
|
|
165
165
|
def _compute(self):
|
|
166
|
-
|
|
167
|
-
set_num_threads
|
|
166
|
+
# control the number of threads used by suite2p this colides with FISSA, for now don't limit.
|
|
167
|
+
#from numba import set_num_threads
|
|
168
|
+
#set_num_threads(DEFAULT_N_JOBS)
|
|
168
169
|
from threadpoolctl import threadpool_limits
|
|
169
170
|
import suite2p
|
|
170
171
|
version = f'suite2p{suite2p.version}'
|
|
@@ -173,8 +174,11 @@ class Suite2pCompute(BaseCompute):
|
|
|
173
174
|
version+= f';fissa{fissa.__version__}'
|
|
174
175
|
|
|
175
176
|
seskeys = (self.schema.TwoPhoton() & self.dataset_key).proj().fetch(as_dict = True)
|
|
177
|
+
self.files_existed = True
|
|
178
|
+
if len((self.schema.File & (self.schema.TwoPhoton & seskeys)).check_if_files_local()[0]) == 0:
|
|
179
|
+
self.files_existed = False
|
|
176
180
|
if len(seskeys):
|
|
177
|
-
|
|
181
|
+
rec_filesorig = (self.schema.File & (self.schema.TwoPhoton & seskeys)).get()
|
|
178
182
|
ses_par = (self.schema.TwoPhoton() & seskeys).fetch(as_dict = True)
|
|
179
183
|
else:
|
|
180
184
|
raise(NotImplemented("Only two-photon segmentation is implemented using this compute."))
|
|
@@ -188,20 +192,20 @@ class Suite2pCompute(BaseCompute):
|
|
|
188
192
|
savedir = Path(prefs['scratch_path'])/f'suite2p_temporary_{rand}'
|
|
189
193
|
savedir.mkdir(exist_ok=True,parents=True)
|
|
190
194
|
|
|
191
|
-
ops = suite2p.
|
|
195
|
+
ops = suite2p.default_settings()
|
|
192
196
|
for p in self.parameters.keys():
|
|
193
197
|
if p in ops.keys():
|
|
194
198
|
ops[p] = self.parameters[p]
|
|
195
199
|
|
|
196
200
|
# handle different file types
|
|
197
|
-
sbx_files = [f for f in
|
|
198
|
-
zarr_files = [f for f in
|
|
201
|
+
sbx_files = [f for f in rec_filesorig if str(f).endswith('.sbx')]
|
|
202
|
+
zarr_files = [f for f in rec_filesorig if str(f).endswith('.zarr.zip')]
|
|
199
203
|
if len(zarr_files):
|
|
200
204
|
print('Converting zarr file to tiff.')
|
|
201
205
|
rec_files = [savedir/'rawdata.tif']
|
|
202
206
|
from tifffile import TiffWriter
|
|
203
207
|
with TiffWriter(str(rec_files[0]), bigtiff=True, append=True) as tif:
|
|
204
|
-
from tqdm import tqdm
|
|
208
|
+
from tqdm.auto import tqdm
|
|
205
209
|
for ifile,zarrfile in enumerate(zarr_files):
|
|
206
210
|
fd = open_zarr(zarrfile)
|
|
207
211
|
for o,f in tqdm(chunk_indices(len(fd),512),
|
|
@@ -213,7 +217,7 @@ class Suite2pCompute(BaseCompute):
|
|
|
213
217
|
ops['input_format'] = 'sbx'
|
|
214
218
|
rec_files = sbx_files
|
|
215
219
|
else:
|
|
216
|
-
raise(ValueError(f'No valid file format found in {
|
|
220
|
+
raise(ValueError(f'No valid file format found in {rec_filesorig}.'))
|
|
217
221
|
|
|
218
222
|
ops['fs'] = fs
|
|
219
223
|
ops['nplanes'] = nplanes
|
|
@@ -221,15 +225,19 @@ class Suite2pCompute(BaseCompute):
|
|
|
221
225
|
ops['fast_disk'] = str(savedir)
|
|
222
226
|
ops['save_folder'] = str(savedir/'suite2p')
|
|
223
227
|
ops['combined'] = False # do not combine planes
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
228
|
+
ops['save_path0'] = str(savedir/'suite2p')
|
|
229
|
+
ops['data_path'] = [s.parent for s in rec_files]
|
|
230
|
+
ops['file_list'] = rec_files
|
|
231
|
+
ops['reg_tif'] = True
|
|
227
232
|
for o in ops.keys():
|
|
228
|
-
print(f' {o} : {ops[o]}')
|
|
229
|
-
|
|
233
|
+
print(f'[suite2p] {o} : {ops[o]}', flush=True)
|
|
234
|
+
|
|
230
235
|
with threadpool_limits(limits=DEFAULT_N_JOBS, user_api='blas'):
|
|
231
|
-
suite2p.run_s2p(
|
|
232
|
-
|
|
236
|
+
suite2p.run_s2p(settings=dict(fs = ops['fs'],
|
|
237
|
+
tau = ops['tau'],
|
|
238
|
+
diameter = ops['diameter'],
|
|
239
|
+
torch_device = ops['torch_device']),
|
|
240
|
+
db = ops)
|
|
233
241
|
from labdata.stacks import compute_projections
|
|
234
242
|
planefolders = natsorted([a.parent for a in savedir.rglob('*F.npy')
|
|
235
243
|
if not 'combined' in str(a)])
|
|
@@ -268,6 +276,22 @@ class Suite2pCompute(BaseCompute):
|
|
|
268
276
|
roi_pixels_values = s['lam']))
|
|
269
277
|
# extract and denoise with FISSA!
|
|
270
278
|
if self.parameters['use_fissa']:
|
|
279
|
+
# this is a work around. Save the binary to a tiff file and extract with low-memory mode
|
|
280
|
+
if str(rec_files[0]).endswith('rawdata.tif'):
|
|
281
|
+
if rec_files[0].exists():
|
|
282
|
+
os.unlink(rec_files[0])
|
|
283
|
+
print('Deleting the previously generated tiff file.')
|
|
284
|
+
#correctedfname = savedir/'motion_corrected'
|
|
285
|
+
#correctedfname.mkdir(exist_ok=True)
|
|
286
|
+
#from tifffile import imwrite
|
|
287
|
+
#from tqdm.auto import tqdm
|
|
288
|
+
#chunks = chunk_indices(len(binary),1056)
|
|
289
|
+
#chunkfiles = []
|
|
290
|
+
#for ichunk,(o,f) in tqdm(enumerate(chunks), total = len(chunks),
|
|
291
|
+
# desc = f"Concatenating motion corrected data:"):
|
|
292
|
+
# chunkfiles.append(correctedfname/'chunk{ichunk}.tif')
|
|
293
|
+
# imwrite(chunkfiles[-1],np.array(binary[o:f]).reshape((-1,*binary.shape[-2:])))
|
|
294
|
+
#with threadpool_limits(limits=DEFAULT_N_JOBS, user_api='blas'):
|
|
271
295
|
dff,spks = extract_dff_fissa(stats,binary,dims,
|
|
272
296
|
batch_size=ops['batch_size'],
|
|
273
297
|
tau=ops['tau'],
|
|
@@ -286,8 +310,14 @@ class Suite2pCompute(BaseCompute):
|
|
|
286
310
|
dims = [a for a in dims]))
|
|
287
311
|
cellcount[isession] += len(rois) # count cells in all planes
|
|
288
312
|
projdicts.extend([dict(planekey, proj_name = n,
|
|
289
|
-
proj_im = pi) for n,pi in zip(['mean',
|
|
290
|
-
|
|
313
|
+
proj_im = pi) for n,pi in zip(['mean',
|
|
314
|
+
'std',
|
|
315
|
+
'max',
|
|
316
|
+
'lcorr'],
|
|
317
|
+
[mean_proj,
|
|
318
|
+
std_proj,
|
|
319
|
+
max_proj,
|
|
320
|
+
corr_proj])])
|
|
291
321
|
for icell,roi in enumerate(rois):
|
|
292
322
|
roidicts.append(dict(planekey,**roi))
|
|
293
323
|
if not dff is None:
|
|
@@ -320,23 +350,40 @@ class Suite2pCompute(BaseCompute):
|
|
|
320
350
|
# CellSegmentation.MotionCorrection.insert1(dict(planekey,
|
|
321
351
|
# motion_block_size = 0,
|
|
322
352
|
# displacement = motion))
|
|
323
|
-
self.schema.CellSegmentation.Projection.insert(projdicts,
|
|
324
|
-
|
|
353
|
+
self.schema.CellSegmentation.Projection.insert(projdicts,
|
|
354
|
+
allow_direct_insert = True)
|
|
355
|
+
self.schema.CellSegmentation.ROI.insert(roidicts,
|
|
356
|
+
allow_direct_insert = True)
|
|
325
357
|
# Insert traces in parallel to prevent timeout errors from mysql
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
skip_duplicates = True,
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
358
|
+
parallel_insert(self.schema.schema_project,
|
|
359
|
+
'CellSegmentation.Traces',
|
|
360
|
+
tracesdicts,
|
|
361
|
+
n_jobs = DEFAULT_N_JOBS,
|
|
362
|
+
skip_duplicates = True,
|
|
363
|
+
ignore_extra_fields = True)
|
|
364
|
+
parallel_insert(self.schema.schema_project,
|
|
365
|
+
'CellSegmentation.RawTraces',
|
|
366
|
+
rawtracesdicts,
|
|
367
|
+
n_jobs = DEFAULT_N_JOBS,
|
|
368
|
+
skip_duplicates = True,
|
|
369
|
+
ignore_extra_fields = True)
|
|
370
|
+
parallel_insert(self.schema.schema_project,
|
|
371
|
+
'CellSegmentation.Deconvolved',
|
|
372
|
+
deconvdicts,
|
|
373
|
+
n_jobs = DEFAULT_N_JOBS,
|
|
374
|
+
skip_duplicates = True,
|
|
375
|
+
ignore_extra_fields = True)
|
|
376
|
+
self.schema.CellSegmentation.Selection.insert(selectiondicts,
|
|
377
|
+
allow_direct_insert = True)
|
|
334
378
|
if not self.keep_intermediate:
|
|
335
|
-
print(f'[{self.name} job] Removing the temporary folder.')
|
|
379
|
+
print(f'[{self.name} job] Removing the temporary folder {savedir}.')
|
|
336
380
|
import shutil
|
|
337
381
|
shutil.rmtree(savedir)
|
|
382
|
+
if not self.files_existed:
|
|
383
|
+
for f in rec_filesorig:
|
|
384
|
+
os.unlink(f)
|
|
338
385
|
else:
|
|
339
|
-
print(f'[{self.name} job] Kept the temporary folder {
|
|
386
|
+
print(f'[{self.name} job] Kept the temporary folder {savedir}.')
|
|
340
387
|
|
|
341
388
|
|
|
342
389
|
def extract_dff_fissa(stats,binary,dims,fs, batch_size,tau):
|
|
@@ -361,13 +408,14 @@ def extract_dff_fissa(stats,binary,dims,fs, batch_size,tau):
|
|
|
361
408
|
import fissa
|
|
362
409
|
from tqdm.auto import tqdm
|
|
363
410
|
roiidx = np.where([r.sum()>4 for r in tqdm(rois,desc = 'Using only cells with more than 4 pixels') ])[0]
|
|
364
|
-
fissa_res = fissa.Experiment(images=[binary],
|
|
411
|
+
fissa_res = fissa.Experiment(images = [binary],
|
|
412
|
+
rois = [[rois[i] for i in roiidx]],
|
|
365
413
|
ncores_separation = DEFAULT_N_JOBS,
|
|
366
|
-
ncores_preparation = DEFAULT_N_JOBS
|
|
414
|
+
ncores_preparation = DEFAULT_N_JOBS,
|
|
415
|
+
verbosity = 2)
|
|
367
416
|
|
|
368
417
|
res = fissa_res.separate()
|
|
369
418
|
fissa_res.calc_deltaf(freq=fs)
|
|
370
|
-
|
|
371
419
|
dff = np.zeros((num_rois,
|
|
372
420
|
len(fissa_res.deltaf_result[0][0][0])),dtype = 'float32')
|
|
373
421
|
for iroi,r in zip(roiidx,fissa_res.deltaf_result):
|
|
@@ -146,7 +146,7 @@ def run_analysis(target, jobids, compute_obj, project = None):
|
|
|
146
146
|
|
|
147
147
|
if container_file.exists():
|
|
148
148
|
|
|
149
|
-
cmd += '
|
|
149
|
+
cmd += ' | ' + run_on_apptainer(container_file,
|
|
150
150
|
command = f'labdata2 logpipe {jid}',
|
|
151
151
|
dry_run = True)
|
|
152
152
|
else:
|
|
@@ -223,7 +223,7 @@ def run_analysis(target, jobids, compute_obj, project = None):
|
|
|
223
223
|
cuda = compute_obj.cuda,
|
|
224
224
|
dry_run = True)
|
|
225
225
|
# generate slurm cmd and launch
|
|
226
|
-
cmd += '
|
|
226
|
+
cmd += ' | ' + run_on_apptainer(container_file,
|
|
227
227
|
command = f'labdata2 logpipe {j}',
|
|
228
228
|
dry_run = True)
|
|
229
229
|
opts = dict()
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
import streamlit as st
|
|
4
|
+
|
|
5
|
+
STATES = ['COMPLETED', 'WORKING', 'WAITING', 'FAILED', 'CANCELED','ON SERVER']
|
|
6
|
+
STATE_COLORS = ['#01D186', '#48CAE4', '#F3A21C', '#FF5733', '#F3A21C','#F3A21C']
|
|
7
|
+
|
|
8
|
+
def _jobs_panel(table, status_field, target_field, cols, delete_key):
|
|
9
|
+
|
|
10
|
+
import datajoint as dj
|
|
11
|
+
import altair as alt
|
|
12
|
+
|
|
13
|
+
rows = table.fetch(as_dict=True)
|
|
14
|
+
if not rows:
|
|
15
|
+
st.write('### There are no jobs in the table')
|
|
16
|
+
return
|
|
17
|
+
|
|
18
|
+
df = pd.DataFrame(rows)[[c for c in cols if c in pd.DataFrame(rows).columns]]
|
|
19
|
+
|
|
20
|
+
# counts via aggr
|
|
21
|
+
counts = {r[status_field]: r['n'] for r in
|
|
22
|
+
dj.U(status_field).aggr(table, n='count(*)').fetch(as_dict=True)}
|
|
23
|
+
metric_cols = st.columns(len(STATES))
|
|
24
|
+
for col, t in zip(metric_cols, STATES):
|
|
25
|
+
col.metric(t, counts.get(t, 0))
|
|
26
|
+
|
|
27
|
+
# bar chart per target queue
|
|
28
|
+
df = df.set_index('job_id')
|
|
29
|
+
if target_field in df.columns:
|
|
30
|
+
targets = [d if d is not None else 'unknown' for d in df[target_field].values]
|
|
31
|
+
df[target_field] = targets
|
|
32
|
+
qcounts = []
|
|
33
|
+
for t in np.unique(targets):
|
|
34
|
+
status_vals = df[status_field].values
|
|
35
|
+
target_vals = df[target_field].values
|
|
36
|
+
for tt, c in zip(STATES, STATE_COLORS):
|
|
37
|
+
qcounts.append({'Task status': f'{tt}|{t}',
|
|
38
|
+
'Jobs': int(sum(1 for a, tv in zip(status_vals, target_vals)
|
|
39
|
+
if tt in a and tv == t)),
|
|
40
|
+
'color': c})
|
|
41
|
+
qcounts = [q for q in qcounts if q['Jobs'] > 0]
|
|
42
|
+
ch = (alt.Chart(pd.DataFrame(qcounts)).mark_bar().encode(
|
|
43
|
+
y='Task status:N',
|
|
44
|
+
x='Jobs:Q',
|
|
45
|
+
color=alt.Color('color:N').scale(None)
|
|
46
|
+
).configure_axis(labelLimit=3000))
|
|
47
|
+
st.altair_chart(ch, width="stretch")
|
|
48
|
+
|
|
49
|
+
# filtered table
|
|
50
|
+
selected_states = st.multiselect('Select status:', STATES, default=STATES[1:], key=f'{delete_key}_filter')
|
|
51
|
+
idx = np.zeros(len(df), dtype=bool)
|
|
52
|
+
for s in selected_states:
|
|
53
|
+
idx = idx | np.array([s in a for a in df[status_field].values])
|
|
54
|
+
|
|
55
|
+
display_df = df.iloc[idx].copy()
|
|
56
|
+
display_df.insert(0, 'Select', False)
|
|
57
|
+
edited = st.data_editor(
|
|
58
|
+
display_df,
|
|
59
|
+
column_config={'Select': st.column_config.CheckboxColumn(required=True)},
|
|
60
|
+
disabled=[c for c in display_df.columns if c != 'Select'],
|
|
61
|
+
width="stretch",
|
|
62
|
+
key=f'{delete_key}_editor',
|
|
63
|
+
)
|
|
64
|
+
selected_jobs = edited[edited['Select'] == True].index.tolist()
|
|
65
|
+
if selected_jobs:
|
|
66
|
+
st.caption(f'{len(selected_jobs)} job(s) selected')
|
|
67
|
+
if st.button('Delete selected', type='primary', key=f'{delete_key}_btn'):
|
|
68
|
+
st.session_state[f'{delete_key}_pending'] = selected_jobs
|
|
69
|
+
if st.session_state.get(f'{delete_key}_pending'):
|
|
70
|
+
jobs_to_delete = st.session_state[f'{delete_key}_pending']
|
|
71
|
+
st.warning(f'Are you sure you want to delete {len(jobs_to_delete)} job(s)? This cannot be undone and scheduled jobs might fail.')
|
|
72
|
+
col_yes, col_no, _ = st.columns([1, 1, 4])
|
|
73
|
+
if col_yes.button('Yes, delete', type='primary', key=f'{delete_key}_yes'):
|
|
74
|
+
(table & [dict(job_id=j) for j in jobs_to_delete]).delete(safemode=False)
|
|
75
|
+
st.session_state.pop(f'{delete_key}_pending', None)
|
|
76
|
+
st.success(f'Deleted {len(jobs_to_delete)} job(s).')
|
|
77
|
+
st.rerun()
|
|
78
|
+
if col_no.button('Cancel', key=f'{delete_key}_no'):
|
|
79
|
+
st.session_state.pop(f'{delete_key}_pending', None)
|
|
80
|
+
st.rerun()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def compute_tab(schema=None):
|
|
84
|
+
if schema is None:
|
|
85
|
+
from labdata import load_project_schema
|
|
86
|
+
schema = load_project_schema()
|
|
87
|
+
|
|
88
|
+
tab_compute, tab_upload = st.tabs(['Compute tasks', 'Upload jobs'])
|
|
89
|
+
|
|
90
|
+
with tab_compute:
|
|
91
|
+
_jobs_panel(
|
|
92
|
+
table=schema.ComputeTask,
|
|
93
|
+
status_field='task_status',
|
|
94
|
+
target_field='task_target',
|
|
95
|
+
cols=['job_id', 'task_name', 'task_status', 'task_host', 'task_target',
|
|
96
|
+
'task_waiting', 'task_log', 'subject_name', 'session_name',
|
|
97
|
+
'task_starttime', 'task_endtime'],
|
|
98
|
+
delete_key='compute',
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
with tab_upload:
|
|
102
|
+
all_projects = st.toggle('Show all projects', value=False, key='upload_all_projects')
|
|
103
|
+
upload_table = schema.UploadJob if all_projects else schema.UploadJob & dict(project_name=schema.schema_project)
|
|
104
|
+
_jobs_panel(
|
|
105
|
+
table=upload_table,
|
|
106
|
+
status_field='job_status',
|
|
107
|
+
target_field='job_host',
|
|
108
|
+
cols=['job_id', 'job_status', 'job_host', 'job_rule', 'job_waiting',
|
|
109
|
+
'job_log', 'subject_name', 'session_name', 'dataset_name',
|
|
110
|
+
'upload_storage', 'job_starttime', 'job_endtime'],
|
|
111
|
+
delete_key='upload',
|
|
112
|
+
)
|