labdata 0.1.22__tar.gz → 0.1.23__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.1.22 → labdata-0.1.23}/PKG-INFO +1 -1
- {labdata-0.1.22 → labdata-0.1.23}/labdata/__init__.py +1 -1
- {labdata-0.1.22 → labdata-0.1.23}/labdata/cli.py +61 -3
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/ephys.py +48 -10
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/utils.py +15 -10
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/compute.py +1 -1
- {labdata-0.1.22 → labdata-0.1.23}/labdata/rules/ephys.py +18 -6
- {labdata-0.1.22 → labdata-0.1.23}/labdata/rules/utils.py +18 -12
- {labdata-0.1.22 → labdata-0.1.23}/labdata/s3.py +211 -128
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/ephys.py +102 -31
- {labdata-0.1.22 → labdata-0.1.23}/labdata.egg-info/PKG-INFO +1 -1
- {labdata-0.1.22 → labdata-0.1.23}/LICENSE +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/README.md +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/accounts.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/__init__.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/caiman.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/ec2.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/pcluster.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/pose.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/schedulers.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/singularity.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/submission.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/compute/suite2p.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/copy.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/__init__.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/activity.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/admin.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/ai_utils.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/console.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/decisiontask.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/docstrings.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/drift.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/ephys.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/events.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/imaging.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/index.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/launch.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/local_files.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/notes.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/procedures.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/raster.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/reads.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/schema_graph.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/segmentation.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/sorting.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/sync.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/upload.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/upload_worker.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/video.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/dashboard/writes.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/ephys_io.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/ephys_signal.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/export.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/rules/__init__.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/rules/imaging.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/__init__.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/general.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/histology.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/onephoton.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/procedures.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/tasks.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/twophoton.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/utils.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/schema/video.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/stacks.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/utils.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata/widgets.py +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata.egg-info/SOURCES.txt +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata.egg-info/dependency_links.txt +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata.egg-info/entry_points.txt +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata.egg-info/requires.txt +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/labdata.egg-info/top_level.txt +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/pyproject.toml +0 -0
- {labdata-0.1.22 → labdata-0.1.23}/setup.cfg +0 -0
|
@@ -615,7 +615,9 @@ Administration commands (don't run on experimental computers):
|
|
|
615
615
|
whose output goes to ``task_log`` and ``~/.labdata/logs``,
|
|
616
616
|
instead of in the terminal.
|
|
617
617
|
--queue
|
|
618
|
-
Print the
|
|
618
|
+
Print the scheduler queue of each target, then the ``ComputeTask``
|
|
619
|
+
table by status with the last line of each task log, and exit.
|
|
620
|
+
With an analysis name, only the tasks of that analysis.
|
|
619
621
|
--delete-computes
|
|
620
622
|
Delete all ``ComputeTask`` entries for this analysis (careful).
|
|
621
623
|
--
|
|
@@ -666,6 +668,8 @@ Available analysis are
|
|
|
666
668
|
targets = np.unique([prefs['compute']['default_target']]+
|
|
667
669
|
list(prefs['compute']['remotes'].keys()))
|
|
668
670
|
for q in targets:
|
|
671
|
+
if q == 'local': # no scheduler queue, the tasks are in the table below
|
|
672
|
+
continue
|
|
669
673
|
queue = check_queue(target = q)
|
|
670
674
|
if queue is None:
|
|
671
675
|
continue
|
|
@@ -678,7 +682,14 @@ Available analysis are
|
|
|
678
682
|
cc = tcolor['g']
|
|
679
683
|
else:
|
|
680
684
|
cc = tcolor['r']
|
|
681
|
-
print(f"\t {j['jobid']} {j['name']} {cc('
|
|
685
|
+
print(f"\t {j['jobid']} {j['name']} {cc(j['state'].lower())} time: {tcolor['y'](j['time'])}\n\t\tpartition: {j['partition']} node: {j['nodelist(reason)']} ")
|
|
686
|
+
# the ComputeTask table, optionally for one analysis: labdata run <analysis> --queue
|
|
687
|
+
qparser = argparse.ArgumentParser(add_help = False)
|
|
688
|
+
qparser.add_argument('analysis', nargs = '?', default = None)
|
|
689
|
+
qparser.add_argument('-p','--project', default = _get_project())
|
|
690
|
+
qargs, _ = qparser.parse_known_args(argum)
|
|
691
|
+
from .utils import load_project_schema
|
|
692
|
+
self._print_compute_tasks(load_project_schema(qargs.project), analysis = qargs.analysis)
|
|
682
693
|
return
|
|
683
694
|
args = parser.parse_args(argum)
|
|
684
695
|
if '--delete-computes' in argum:
|
|
@@ -2051,7 +2062,54 @@ examples:
|
|
|
2051
2062
|
type = str,
|
|
2052
2063
|
help= f'Select project: {project}')
|
|
2053
2064
|
return parser
|
|
2054
|
-
|
|
2065
|
+
|
|
2066
|
+
def _print_compute_tasks(self, schema, analysis = None, limit = 10):
|
|
2067
|
+
'''Print the ComputeTask table by status, with the last line of each task log.
|
|
2068
|
+
|
|
2069
|
+
Shows the most recent ``limit`` tasks of each status and the count of the rest.
|
|
2070
|
+
'''
|
|
2071
|
+
tasks = schema.ComputeTask()
|
|
2072
|
+
if not analysis is None:
|
|
2073
|
+
tasks = tasks & dict(task_name = analysis)
|
|
2074
|
+
title = 'ComputeTask table' + (f' [{analysis}]' if not analysis is None else '')
|
|
2075
|
+
print(tcolor['m'](f"++++++++++++++++++++ {title} ++++++++++++++++++++"))
|
|
2076
|
+
if not len(tasks):
|
|
2077
|
+
print('\t no compute tasks.')
|
|
2078
|
+
return
|
|
2079
|
+
statuses = [('WORKING', tcolor['c']), ('WAITING', tcolor['y']), ('ON SERVER', tcolor['b']),
|
|
2080
|
+
('FAILED', tcolor['r']), ('CANCELED', tcolor['o']), ('COMPLETED', tcolor['g'])]
|
|
2081
|
+
def _brief(log, width = 100):
|
|
2082
|
+
if log is None:
|
|
2083
|
+
return ''
|
|
2084
|
+
if isinstance(log, bytes):
|
|
2085
|
+
log = log.decode('utf-8', errors = 'replace')
|
|
2086
|
+
lines = [l.strip() for l in str(log).splitlines() if len(l.strip())]
|
|
2087
|
+
line = ' '.join(lines[-1].split()) if len(lines) else ''
|
|
2088
|
+
return line if len(line) <= width else line[:width-3] + '...'
|
|
2089
|
+
def _time(t):
|
|
2090
|
+
return t.strftime('%Y-%m-%d %H:%M') if not t is None else ''
|
|
2091
|
+
for status, cc in statuses:
|
|
2092
|
+
sel = tasks & f'task_status LIKE "%{status}%"'
|
|
2093
|
+
n = len(sel)
|
|
2094
|
+
if not n:
|
|
2095
|
+
continue
|
|
2096
|
+
print(cc(f"{status} ({n})"))
|
|
2097
|
+
rows = sel.fetch('job_id', 'task_name', 'task_target', 'task_host', 'subject_name',
|
|
2098
|
+
'session_name', 'task_starttime', 'task_endtime', 'task_log',
|
|
2099
|
+
as_dict = True, order_by = 'job_id DESC', limit = limit)
|
|
2100
|
+
for r in rows:
|
|
2101
|
+
where = '/'.join([str(r[k]) for k in ('subject_name', 'session_name') if not r[k] is None])
|
|
2102
|
+
when = _time(r['task_endtime'] if status in ('COMPLETED', 'FAILED', 'CANCELED') and
|
|
2103
|
+
not r['task_endtime'] is None else r['task_starttime'])
|
|
2104
|
+
print(f"\t {r['job_id']} {r['task_name']} {where} "
|
|
2105
|
+
f"target: {r['task_target']} host: {r['task_host']}" +
|
|
2106
|
+
(f" {tcolor['y'](when)}" if len(when) else ''))
|
|
2107
|
+
log = _brief(r['task_log'])
|
|
2108
|
+
if len(log):
|
|
2109
|
+
print(f"\t\t{log}")
|
|
2110
|
+
if n > limit:
|
|
2111
|
+
print(f"\t ... and {n - limit} more")
|
|
2112
|
+
|
|
2055
2113
|
def _get_default_arg(self,argument,cli_arg = 'submit', default = None):
|
|
2056
2114
|
# checks if there is a default in the options
|
|
2057
2115
|
if not f'{cli_arg}_defaults' in labdata_preferences.keys():
|
|
@@ -35,7 +35,7 @@ class SpksCompute(BaseCompute):
|
|
|
35
35
|
low_pass = 300.,
|
|
36
36
|
high_pass = 13000.)
|
|
37
37
|
self.parameter_keys = ['motion_correction','low_pass','high_pass','thresholds','remove_cross_duplicates',
|
|
38
|
-
'waveforms_from_input','dredge']
|
|
38
|
+
'waveforms_from_input','dredge','remove_channels_without_signal']
|
|
39
39
|
# the parameters that go on the SpikeSortingParams
|
|
40
40
|
self.use_hdf5 = True # flag to use h5py or zarr format for the waveforms.
|
|
41
41
|
self.parameter_set_num = None # identifier in SpikeSortingParams
|
|
@@ -131,9 +131,15 @@ class SpksCompute(BaseCompute):
|
|
|
131
131
|
action='store', default = None, nargs=2, type = float,
|
|
132
132
|
help = "Interval to sort in seconds: start end")
|
|
133
133
|
parser.add_argument('--dredge',
|
|
134
|
-
action='store_true', default = False,
|
|
134
|
+
action='store_true', default = False,
|
|
135
135
|
help = "Motion correction using dredge")
|
|
136
|
-
|
|
136
|
+
parser.add_argument('--remove-channels-without-signal',
|
|
137
|
+
action='store', default = None, nargs = '*', type = str,
|
|
138
|
+
choices = ['amplitude','correlation','coherence'],
|
|
139
|
+
help = "Leave out the channels without signal before sorting and the common average reference. "
|
|
140
|
+
"Methods: amplitude (low MAD in the sorting band), correlation and coherence (low correlation or phase coherence "
|
|
141
|
+
"with the channels within 300 um on the same shank, LFP band). Without methods: amplitude coherence.")
|
|
142
|
+
|
|
137
143
|
args = parser.parse_args(arguments[1:])
|
|
138
144
|
if 'ks2.5' in args.method: # defaults for ks2.5
|
|
139
145
|
self.parameters = dict(algorithm_name = 'spks_kilosort2.5',
|
|
@@ -163,7 +169,12 @@ class SpksCompute(BaseCompute):
|
|
|
163
169
|
self.parameters['waveforms_from_input'] = False
|
|
164
170
|
if args.dredge:
|
|
165
171
|
# default is to extract from the original input file
|
|
166
|
-
self.parameters['dredge'] = True
|
|
172
|
+
self.parameters['dredge'] = True
|
|
173
|
+
if not args.remove_channels_without_signal is None:
|
|
174
|
+
methods = args.remove_channels_without_signal
|
|
175
|
+
if not len(methods):
|
|
176
|
+
methods = ['amplitude','coherence']
|
|
177
|
+
self.parameters['remove_channels_without_signal'] = sorted(set(methods))
|
|
167
178
|
print(args)
|
|
168
179
|
if not args.thresholds is None:
|
|
169
180
|
self.parameters['thresholds'] = args.thresholds
|
|
@@ -294,6 +305,13 @@ class SpksCompute(BaseCompute):
|
|
|
294
305
|
dredge_motion_correction = False
|
|
295
306
|
if 'dredge' in self.parameters.keys():
|
|
296
307
|
dredge_motion_correction = self.parameters['dredge']
|
|
308
|
+
preprocessing = dict()
|
|
309
|
+
if self.parameters.get('remove_channels_without_signal', None):
|
|
310
|
+
# channels without signal are left out before the filters and the common average reference
|
|
311
|
+
from spks.raw import default_filter_pipeline_par
|
|
312
|
+
preprocessing['filter_pipeline_par'] = [dict(function = 'remove_channels_without_signal',
|
|
313
|
+
methods = self.parameters['remove_channels_without_signal'])] + [
|
|
314
|
+
dict(d) for d in default_filter_pipeline_par]
|
|
297
315
|
# run kilosort using spks for preprocessing.
|
|
298
316
|
if self.parameters['algorithm_name'] == 'spks_kilosort2.5':
|
|
299
317
|
results_folder = run_kilosort(version = '2.5',sessionfiles = probepath,
|
|
@@ -303,7 +321,8 @@ class SpksCompute(BaseCompute):
|
|
|
303
321
|
thresholds = self.parameters['thresholds'],
|
|
304
322
|
lowpass = self.parameters['low_pass'],
|
|
305
323
|
highpass = self.parameters['high_pass'],
|
|
306
|
-
dredge_motion_correction = dredge_motion_correction
|
|
324
|
+
dredge_motion_correction = dredge_motion_correction,
|
|
325
|
+
**preprocessing)
|
|
307
326
|
elif self.parameters['algorithm_name'] == 'spks_kilosort3.0':
|
|
308
327
|
results_folder = run_kilosort(version = '3.0',
|
|
309
328
|
sessionfiles = probepath,
|
|
@@ -313,7 +332,8 @@ class SpksCompute(BaseCompute):
|
|
|
313
332
|
thresholds = self.parameters['thresholds'],
|
|
314
333
|
lowpass = self.parameters['low_pass'],
|
|
315
334
|
highpass = self.parameters['high_pass'],
|
|
316
|
-
dredge_motion_correction = dredge_motion_correction
|
|
335
|
+
dredge_motion_correction = dredge_motion_correction,
|
|
336
|
+
**preprocessing)
|
|
317
337
|
elif self.parameters['algorithm_name'] == 'spks_kilosort4.0':
|
|
318
338
|
results_folder = run_kilosort(version = '4.0',
|
|
319
339
|
sessionfiles = probepath,
|
|
@@ -323,7 +343,8 @@ class SpksCompute(BaseCompute):
|
|
|
323
343
|
thresholds = self.parameters['thresholds'],
|
|
324
344
|
lowpass = self.parameters['low_pass'],
|
|
325
345
|
highpass = self.parameters['high_pass'],
|
|
326
|
-
dredge_motion_correction = dredge_motion_correction
|
|
346
|
+
dredge_motion_correction = dredge_motion_correction,
|
|
347
|
+
**preprocessing)
|
|
327
348
|
elif self.parameters['algorithm_name'] == 'spks_mountainsort5':
|
|
328
349
|
raise(NotImplemented(f"[{self.name} job] - Algorithm {self.parameters['algorithm_name']} not implemented."))
|
|
329
350
|
else:
|
|
@@ -416,6 +437,10 @@ class SpksCompute(BaseCompute):
|
|
|
416
437
|
del clu
|
|
417
438
|
clu = Clusters(results_folder, get_waveforms = False, get_metrics = False)
|
|
418
439
|
clu.compute_template_amplitudes_and_depths()
|
|
440
|
+
# channels of the sorting in the numbering of the recorded file (channels can be left out before sorting)
|
|
441
|
+
sorting_channel_indices = clu.channel_map.flatten()
|
|
442
|
+
if not clu.metadata is None and 'original_channels' in clu.metadata.keys():
|
|
443
|
+
sorting_channel_indices = np.asarray(clu.metadata['original_channels']).flatten()[sorting_channel_indices]
|
|
419
444
|
base_keys = []
|
|
420
445
|
ssdict = [] # sortings
|
|
421
446
|
udict = [] # unit
|
|
@@ -439,7 +464,7 @@ class SpksCompute(BaseCompute):
|
|
|
439
464
|
n_detected_spikes = len(spikes),
|
|
440
465
|
sorting_datetime = datetime.fromtimestamp(
|
|
441
466
|
Path(results_folder).stat().st_ctime),
|
|
442
|
-
sorting_channel_indices =
|
|
467
|
+
sorting_channel_indices = sorting_channel_indices,
|
|
443
468
|
sorting_channel_coords = clu.channel_positions))
|
|
444
469
|
udict.append([]) # list of lists
|
|
445
470
|
for iclu in np.unique(clusters):
|
|
@@ -569,10 +594,14 @@ class SpksCompute(BaseCompute):
|
|
|
569
594
|
dat = map_binary(binaryfile, nchannels = nchannels)
|
|
570
595
|
nsamples = int(clu.sampling_rate*2)
|
|
571
596
|
offset_samples = int(np.random.uniform(offsets[0]+nsamples, offsets[1]-nsamples-1))
|
|
597
|
+
segment = np.array(dat[offset_samples : offset_samples + nsamples])
|
|
598
|
+
# statistics in microvolt with the gains of the waveforms, UnitMetrics reads the noise from here
|
|
599
|
+
from ..rules.ephys import channel_statistics
|
|
572
600
|
self.schema.SpikeSorting.Segment.insert1(dict(base_key,
|
|
573
601
|
segment_num = 1,
|
|
574
602
|
offset_samples = offset_samples - offsets[0],
|
|
575
|
-
segment =
|
|
603
|
+
segment = segment,
|
|
604
|
+
**channel_statistics(segment.astype(np.float32)*clu.channel_gains)))
|
|
576
605
|
del dat
|
|
577
606
|
self.set_job_status(job_log = f'Completed {base_key}')
|
|
578
607
|
from labdata.schema import UnitMetrics
|
|
@@ -632,7 +661,9 @@ def select_random_waveforms(unit_dict, wpre=45, wpost=45, duration=None, nmax_wa
|
|
|
632
661
|
|
|
633
662
|
Adds a ``waveform_indices`` array to each unit dict, drawing up to
|
|
634
663
|
``nmax_waves`` spikes from the first quarter and last quarter of the
|
|
635
|
-
recording.
|
|
664
|
+
recording. A unit with no spikes in either quarter gets up to
|
|
665
|
+
``2*nmax_waves`` spikes from the whole recording. Spikes too close to
|
|
666
|
+
the file boundaries (within ``wpre`` or
|
|
636
667
|
``wpost`` samples) are excluded.
|
|
637
668
|
|
|
638
669
|
Parameters
|
|
@@ -668,6 +699,13 @@ def select_random_waveforms(unit_dict, wpre=45, wpost=45, duration=None, nmax_wa
|
|
|
668
699
|
sel += [t for t in np.random.choice(s_end, nmax_waves, replace=True)]
|
|
669
700
|
else:
|
|
670
701
|
sel += [t for t in s_end]
|
|
702
|
+
if not len(sel):
|
|
703
|
+
# the unit fires only in the middle of the recording, take the spikes from anywhere
|
|
704
|
+
s_all = s[(s>(wpre+2)) & (s<(duration-2*wpost))]
|
|
705
|
+
if len(s_all)>2*nmax_waves:
|
|
706
|
+
sel = [t for t in np.random.choice(s_all, 2*nmax_waves, replace=True)]
|
|
707
|
+
else:
|
|
708
|
+
sel = [t for t in s_all]
|
|
671
709
|
u['waveform_indices'] = np.sort(np.array(sel).flatten()) # add this to the
|
|
672
710
|
return unit_dict
|
|
673
711
|
|
|
@@ -598,16 +598,21 @@ class BaseCompute():
|
|
|
598
598
|
job_waiting = 0)
|
|
599
599
|
|
|
600
600
|
def cleanup_function(job_id=self.job_id):
|
|
601
|
-
'''Put the task back in the queue (CANCELLED, waiting) if the process stops while it works.
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
601
|
+
'''Put the task back in the queue (CANCELLED, waiting) if the process stops while it works.
|
|
602
|
+
|
|
603
|
+
Runs from a signal handler, maybe while the task waits on a query: it writes on a
|
|
604
|
+
connection of its own, and to the output directly (labdata logpipe keeps the line).'''
|
|
605
|
+
from ..rules.utils import _new_connection
|
|
606
|
+
try:
|
|
607
|
+
conn = _new_connection()
|
|
608
|
+
conn.query(f'''UPDATE {self.schema.ComputeTask.full_table_name}
|
|
609
|
+
SET task_status = "CANCELLED", task_waiting = 1, task_endtime = NOW()
|
|
610
|
+
WHERE job_id = %s AND task_status = "WORKING"''', args = (job_id,))
|
|
611
|
+
conn.close()
|
|
612
|
+
os.write(1, (f'[task {job_id}] INTERRUPTED on {prefs["hostname"]} (the process was stopped); '
|
|
613
|
+
'the task is back in the queue.\n').encode())
|
|
614
|
+
except Exception as err:
|
|
615
|
+
os.write(2, f'[task {job_id}] Could not put the task back in the queue: {err}\n'.encode())
|
|
611
616
|
|
|
612
617
|
self.cleanup_function = cleanup_function
|
|
613
618
|
self.register_safe_exit()
|
|
@@ -382,7 +382,7 @@ def _queue_view(schema):
|
|
|
382
382
|
status_field='task_status',
|
|
383
383
|
target_field='task_target',
|
|
384
384
|
cols=['job_id', 'task_name', 'task_status', 'task_host', 'task_target',
|
|
385
|
-
'task_waiting', 'task_log', 'subject_name', 'session_name',
|
|
385
|
+
'task_waiting', 'task_cmd', 'task_log', 'subject_name', 'session_name',
|
|
386
386
|
'task_starttime', 'task_endtime'],
|
|
387
387
|
delete_key='compute',
|
|
388
388
|
resubmit_fn=lambda job_ids, target_override=None: _resubmit_jobs(schema, job_ids, project, target_override=target_override),
|
|
@@ -278,13 +278,25 @@ def ephys_noise_statistics_from_file(filepath, channel_indices, gain,
|
|
|
278
278
|
channel_mad = np.zeros((len(channel_indices),len(dd))),
|
|
279
279
|
channel_max = np.zeros((len(channel_indices),len(dd))),
|
|
280
280
|
channel_min = np.zeros((len(channel_indices),len(dd))))
|
|
281
|
-
from scipy.stats import median_abs_deviation
|
|
282
281
|
for i,d in enumerate(dd):
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
282
|
+
for k,v in channel_statistics(d[:,channel_indices]).items():
|
|
283
|
+
res[k][:,i] = v
|
|
284
|
+
return res
|
|
285
|
+
|
|
286
|
+
def channel_statistics(data):
|
|
287
|
+
'''Return the peak-to-peak, median, MAD, max and min of each channel.
|
|
288
|
+
|
|
289
|
+
``data`` is samples by channels, usually in microvolt. The MAD is the
|
|
290
|
+
unscaled median absolute deviation; divide it by 0.6745 for the standard
|
|
291
|
+
deviation of Gaussian noise. Each value of the returned dict has one entry
|
|
292
|
+
per channel.
|
|
293
|
+
'''
|
|
294
|
+
from scipy.stats import median_abs_deviation
|
|
295
|
+
res = dict(channel_mad = median_abs_deviation(data,axis = 0),
|
|
296
|
+
channel_max = np.max(data,axis = 0),
|
|
297
|
+
channel_min = np.min(data,axis = 0),
|
|
298
|
+
channel_median = np.median(data,axis = 0))
|
|
299
|
+
res['channel_peak_to_peak'] = res['channel_max']-res['channel_min']
|
|
288
300
|
return res
|
|
289
301
|
|
|
290
302
|
def extract_events_from_nidq(paths):
|
|
@@ -116,21 +116,27 @@ class UploadRule():
|
|
|
116
116
|
self._release_interrupt_handler()
|
|
117
117
|
|
|
118
118
|
def _interrupted(self):
|
|
119
|
-
'''Mark the job FAILED when the process stops while it is WORKING.
|
|
119
|
+
'''Mark the job FAILED when the process stops while it is WORKING.
|
|
120
|
+
|
|
121
|
+
This runs from a signal handler, maybe while the job waits on a query:
|
|
122
|
+
it writes on a connection of its own and to the output file directly,
|
|
123
|
+
since the job's connection and ``print`` may be in use.
|
|
124
|
+
'''
|
|
120
125
|
from ..schema import UploadJob
|
|
126
|
+
if self._stage_stop is not None:
|
|
127
|
+
self._stage_stop.set() # no more progress lines
|
|
128
|
+
host = self.prefs['hostname']
|
|
121
129
|
try:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
f'(the process was stopped) during: {last}')
|
|
131
|
-
print(f'Job {self.job_id} was interrupted; marked FAILED.', flush = True)
|
|
130
|
+
conn = _new_connection()
|
|
131
|
+
conn.query(f'''UPDATE {UploadJob.full_table_name}
|
|
132
|
+
SET job_status = "FAILED", job_waiting = 0, job_endtime = NOW(), job_host = %s,
|
|
133
|
+
job_log = LEFT(CONCAT("INTERRUPTED on ", %s, " (the process was stopped) during: ",
|
|
134
|
+
COALESCE(job_log, "")), 500)
|
|
135
|
+
WHERE job_id = %s AND job_status = "WORKING"''', args = (host, host, self.job_id))
|
|
136
|
+
conn.close()
|
|
137
|
+
os.write(1, f'Job {self.job_id}: interrupted; marked FAILED if it was working.\n'.encode())
|
|
132
138
|
except Exception as err:
|
|
133
|
-
|
|
139
|
+
os.write(2, f'Could not mark job {self.job_id} as interrupted: {err}\n'.encode())
|
|
134
140
|
|
|
135
141
|
def _hold_interrupt_handler(self):
|
|
136
142
|
import safe_exit
|