labdata 0.1.8__tar.gz → 0.1.9__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.8 → labdata-0.1.9}/PKG-INFO +1 -1
- {labdata-0.1.8 → labdata-0.1.9}/labdata/__init__.py +1 -1
- {labdata-0.1.8 → labdata-0.1.9}/labdata/cli.py +400 -24
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/caiman.py +34 -12
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/ec2.py +166 -18
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/ephys.py +178 -37
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/pose.py +55 -6
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/schedulers.py +151 -36
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/singularity.py +63 -10
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/suite2p.py +21 -4
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/utils.py +267 -40
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/admin.py +12 -0
- labdata-0.1.9/labdata/dashboard/ai_utils.py +326 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/compute.py +10 -0
- labdata-0.1.9/labdata/dashboard/console.py +356 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/decisiontask.py +76 -12
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/explorer/cellsegmentation.py +99 -13
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/explorer/spikesorting.py +90 -12
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/index.py +120 -3
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/notes.py +12 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/procedures.py +34 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/schema_graph.py +15 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/segmentation.py +24 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/sorting.py +12 -1
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/video.py +64 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/export.py +95 -4
- {labdata-0.1.8 → labdata-0.1.9}/labdata/rules/__init__.py +37 -10
- {labdata-0.1.8 → labdata-0.1.9}/labdata/rules/ephys.py +126 -23
- {labdata-0.1.8 → labdata-0.1.9}/labdata/rules/imaging.py +215 -40
- {labdata-0.1.8 → labdata-0.1.9}/labdata/rules/utils.py +66 -12
- {labdata-0.1.8 → labdata-0.1.9}/labdata/s3.py +19 -1
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/ephys.py +227 -76
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/general.py +227 -30
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/histology.py +14 -1
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/onephoton.py +28 -2
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/procedures.py +6 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/tasks.py +6 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/twophoton.py +136 -22
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/video.py +74 -13
- {labdata-0.1.8 → labdata-0.1.9}/labdata/stacks.py +44 -2
- {labdata-0.1.8 → labdata-0.1.9}/labdata/utils.py +81 -6
- {labdata-0.1.8 → labdata-0.1.9}/labdata/widgets.py +148 -30
- {labdata-0.1.8 → labdata-0.1.9}/labdata.egg-info/PKG-INFO +1 -1
- {labdata-0.1.8 → labdata-0.1.9}/labdata.egg-info/SOURCES.txt +1 -0
- labdata-0.1.9/labdata.egg-info/entry_points.txt +3 -0
- {labdata-0.1.8 → labdata-0.1.9}/pyproject.toml +2 -1
- labdata-0.1.8/labdata/dashboard/console.py +0 -166
- labdata-0.1.8/labdata.egg-info/entry_points.txt +0 -2
- {labdata-0.1.8 → labdata-0.1.9}/LICENSE +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/README.md +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/compute/__init__.py +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/copy.py +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/__init__.py +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/dashboard/explorer/__init__.py +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/__init__.py +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata/schema/utils.py +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata.egg-info/dependency_links.txt +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata.egg-info/requires.txt +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/labdata.egg-info/top_level.txt +0 -0
- {labdata-0.1.8 → labdata-0.1.9}/setup.cfg +0 -0
|
@@ -2,6 +2,40 @@ from .utils import *
|
|
|
2
2
|
import argparse
|
|
3
3
|
|
|
4
4
|
class CLI_parser():
|
|
5
|
+
'''Command-line interface dispatcher for labdata.
|
|
6
|
+
|
|
7
|
+
Each public method corresponds to a ``labdata <command>`` sub-command.
|
|
8
|
+
Instantiating the class parses ``sys.argv`` and calls the matching method.
|
|
9
|
+
|
|
10
|
+
Sub-commands
|
|
11
|
+
------------
|
|
12
|
+
dashboard
|
|
13
|
+
Launch the Streamlit dashboard or a Dash explorer.
|
|
14
|
+
subjects
|
|
15
|
+
List subjects, optionally filtered by user, name, or sex.
|
|
16
|
+
sessions
|
|
17
|
+
List sessions and datasets for one or more subjects.
|
|
18
|
+
get
|
|
19
|
+
Download dataset files from S3 to local storage.
|
|
20
|
+
put
|
|
21
|
+
Copy raw data files to the upload server (triggers an UploadJob).
|
|
22
|
+
clean
|
|
23
|
+
Delete local files that are confirmed uploaded (checksums verified).
|
|
24
|
+
run
|
|
25
|
+
Queue or launch an analysis compute job.
|
|
26
|
+
task
|
|
27
|
+
Execute a single ``ComputeTask`` by job ID (called by workers).
|
|
28
|
+
task_reset
|
|
29
|
+
Reset a failed/stuck compute task so it can be retried.
|
|
30
|
+
upload
|
|
31
|
+
Process pending ``UploadJob`` entries (server-side, admin use).
|
|
32
|
+
logpipe
|
|
33
|
+
Pipe stdout into a ``ComputeTask.task_log`` entry.
|
|
34
|
+
run_container
|
|
35
|
+
Launch a local Apptainer container interactively.
|
|
36
|
+
build_container
|
|
37
|
+
Build Apptainer ``.sif`` images from definition files.
|
|
38
|
+
'''
|
|
5
39
|
def __init__(self):
|
|
6
40
|
parser = argparse.ArgumentParser(
|
|
7
41
|
description = f'{tcolor["y"]("labdata")} - tools to manage data in an experimental neuroscience lab',
|
|
@@ -35,7 +69,7 @@ Server commands (don't run on experimental computers):
|
|
|
35
69
|
''')
|
|
36
70
|
|
|
37
71
|
parser.add_argument('command',
|
|
38
|
-
help= 'type:
|
|
72
|
+
help= 'type: labdata <command> -h for help')
|
|
39
73
|
|
|
40
74
|
args = parser.parse_args(sys.argv[1:2])
|
|
41
75
|
command = args.command.replace('-','_') # can use - in command
|
|
@@ -46,9 +80,45 @@ Server commands (don't run on experimental computers):
|
|
|
46
80
|
getattr(self,command)() # Runs the following parser
|
|
47
81
|
|
|
48
82
|
def dashboard(self):
|
|
83
|
+
'''Launch the labdata web dashboard or an interactive data explorer.
|
|
84
|
+
|
|
85
|
+
Without flags, opens the main Streamlit dashboard (metadata insertion,
|
|
86
|
+
session browser, admin panel). With ``--spike-sorting`` or
|
|
87
|
+
``--cell-segmentation`` opens a Dash-based interactive explorer instead.
|
|
88
|
+
|
|
89
|
+
Usage
|
|
90
|
+
-----
|
|
91
|
+
::
|
|
92
|
+
|
|
93
|
+
labdata dashboard
|
|
94
|
+
labdata dashboard -p <project>
|
|
95
|
+
labdata dashboard --spike-sorting -f <subject_filter>
|
|
96
|
+
labdata dashboard --cell-segmentation -f <subject_filter>
|
|
97
|
+
labdata dashboard --admin
|
|
98
|
+
labdata dashboard --console
|
|
99
|
+
|
|
100
|
+
Options
|
|
101
|
+
-------
|
|
102
|
+
-f, --filter-name
|
|
103
|
+
Filter subjects by name substring.
|
|
104
|
+
-u, --user
|
|
105
|
+
Filter by lab member username.
|
|
106
|
+
-p, --project
|
|
107
|
+
Project name (overrides preference file).
|
|
108
|
+
--spike-sorting
|
|
109
|
+
Open the Dash spike-sorting explorer instead of Streamlit.
|
|
110
|
+
--cell-segmentation
|
|
111
|
+
Open the Dash cell-segmentation explorer instead of Streamlit.
|
|
112
|
+
--admin
|
|
113
|
+
Include the admin tab (storage stats, project switcher).
|
|
114
|
+
--console
|
|
115
|
+
Include the IPython console tab.
|
|
116
|
+
--debug
|
|
117
|
+
Enable Dash debug mode for error tracebacks.
|
|
118
|
+
'''
|
|
49
119
|
parser = argparse.ArgumentParser(
|
|
50
|
-
description
|
|
51
|
-
usage
|
|
120
|
+
description='Open the interactive explorer or graphical interface.',
|
|
121
|
+
usage='labdata dashboard -f <FILTER>')
|
|
52
122
|
|
|
53
123
|
parser.add_argument('-f','--filter-name',
|
|
54
124
|
default = None,
|
|
@@ -105,9 +175,37 @@ Server commands (don't run on experimental computers):
|
|
|
105
175
|
sub.run(command,shell=True)
|
|
106
176
|
|
|
107
177
|
def subjects(self):
|
|
178
|
+
'''List subjects registered in the database.
|
|
179
|
+
|
|
180
|
+
Prints subjects grouped by responsible lab member, with optional
|
|
181
|
+
filters and an optional column showing total raw data size.
|
|
182
|
+
|
|
183
|
+
Usage
|
|
184
|
+
-----
|
|
185
|
+
::
|
|
186
|
+
|
|
187
|
+
labdata subjects
|
|
188
|
+
labdata subjects -u <user>
|
|
189
|
+
labdata subjects -f <name_substring>
|
|
190
|
+
labdata subjects -s M
|
|
191
|
+
labdata subjects --include-size
|
|
192
|
+
|
|
193
|
+
Options
|
|
194
|
+
-------
|
|
195
|
+
-u, --user
|
|
196
|
+
Filter by lab member username.
|
|
197
|
+
-f, --filter-name
|
|
198
|
+
Filter by subject name substring (SQL ``LIKE`` match).
|
|
199
|
+
-s, --filter-sex
|
|
200
|
+
Filter by sex (``M``, ``F``, or ``U``).
|
|
201
|
+
-p, --project
|
|
202
|
+
Project name.
|
|
203
|
+
--include-size
|
|
204
|
+
Show total raw data size (TB) and file count per subject.
|
|
205
|
+
'''
|
|
108
206
|
parser = argparse.ArgumentParser(
|
|
109
|
-
description
|
|
110
|
-
usage
|
|
207
|
+
description='List sessions and datatypes',
|
|
208
|
+
usage='labdata subject -u <USER> -f <FILTER>')
|
|
111
209
|
|
|
112
210
|
project = _get_project()
|
|
113
211
|
parser.add_argument('-p','--project',
|
|
@@ -181,9 +279,32 @@ Server commands (don't run on experimental computers):
|
|
|
181
279
|
print('')
|
|
182
280
|
|
|
183
281
|
def sessions(self):
|
|
282
|
+
'''List sessions and datasets for one or more subjects.
|
|
283
|
+
|
|
284
|
+
Prints sessions grouped by subject, showing dataset type, name, and
|
|
285
|
+
(optionally) size. For ephys datasets, also shows how many probes have
|
|
286
|
+
been sorted.
|
|
287
|
+
|
|
288
|
+
Usage
|
|
289
|
+
-----
|
|
290
|
+
::
|
|
291
|
+
|
|
292
|
+
labdata sessions -a <subject>
|
|
293
|
+
labdata sessions -a <subject1> <subject2>
|
|
294
|
+
labdata sessions -a <subject> --include-size
|
|
295
|
+
|
|
296
|
+
Options
|
|
297
|
+
-------
|
|
298
|
+
-a, --subject
|
|
299
|
+
One or more subject names.
|
|
300
|
+
-p, --project
|
|
301
|
+
Project name.
|
|
302
|
+
--include-size
|
|
303
|
+
Show total raw data size (TB/GB) and file count.
|
|
304
|
+
'''
|
|
184
305
|
parser = argparse.ArgumentParser(
|
|
185
|
-
description
|
|
186
|
-
usage
|
|
306
|
+
description='List sessions and datatypes',
|
|
307
|
+
usage='labdata sessions -a <SUBJECT>')
|
|
187
308
|
parser = self._add_default_arguments(parser,1, include_project = True)
|
|
188
309
|
parser.add_argument('--include-size', action = 'store_true',
|
|
189
310
|
default = False)
|
|
@@ -231,9 +352,35 @@ Server commands (don't run on experimental computers):
|
|
|
231
352
|
print(f'\t\t {pre} {t["dataset_type"]} - {t["dataset_name"]} {extra}')
|
|
232
353
|
|
|
233
354
|
def get(self):
|
|
355
|
+
'''Download dataset files from S3 to local storage.
|
|
356
|
+
|
|
357
|
+
Resolves the matching ``File`` entries via ``Dataset.DataFiles`` for
|
|
358
|
+
the requested subjects/sessions/datatypes and calls ``File.get()``
|
|
359
|
+
to download any files not already present locally.
|
|
360
|
+
|
|
361
|
+
Usage
|
|
362
|
+
-----
|
|
363
|
+
::
|
|
364
|
+
|
|
365
|
+
labdata get -a <subject>
|
|
366
|
+
labdata get -a <subject> -s <session>
|
|
367
|
+
labdata get -a <subject> -s <session> -d ephys
|
|
368
|
+
labdata get -a <subject1> <subject2> -s <session>
|
|
369
|
+
|
|
370
|
+
Options
|
|
371
|
+
-------
|
|
372
|
+
-a, --subject
|
|
373
|
+
One or more subject names.
|
|
374
|
+
-s, --session
|
|
375
|
+
One or more session names.
|
|
376
|
+
-d, --datatype
|
|
377
|
+
Filter by dataset type (e.g. ``ephys``, ``twophoton``).
|
|
378
|
+
-p, --project
|
|
379
|
+
Project name.
|
|
380
|
+
'''
|
|
234
381
|
parser = argparse.ArgumentParser(
|
|
235
|
-
description
|
|
236
|
-
usage
|
|
382
|
+
description='Download data from one or multiple sessions',
|
|
383
|
+
usage='labdata get -a <SUBJECTS> -s <SESSIONS>')
|
|
237
384
|
parser = self._add_default_arguments(parser,3, include_project = True)
|
|
238
385
|
|
|
239
386
|
# TODO: Add an argument to include files that match a pattern..
|
|
@@ -271,9 +418,45 @@ Server commands (don't run on experimental computers):
|
|
|
271
418
|
(schema.File() & (schema.Dataset.DataFiles() & keys).proj()).get()
|
|
272
419
|
|
|
273
420
|
def put(self):
|
|
421
|
+
'''Copy raw data files to the upload server and create an UploadJob.
|
|
422
|
+
|
|
423
|
+
Opens a Qt file-copy widget that checksums source files, copies them to
|
|
424
|
+
the designated upload server path, and registers an ``UploadJob`` in the
|
|
425
|
+
database for the requested upload rule. **This does not upload to the
|
|
426
|
+
cloud** — that happens when ``labdata upload`` is later run on the server.
|
|
427
|
+
|
|
428
|
+
Usage
|
|
429
|
+
-----
|
|
430
|
+
::
|
|
431
|
+
|
|
432
|
+
labdata put <path/to/data/folder>
|
|
433
|
+
labdata put <path/to/data/folder> -r ephys -a <subject> -s <session>
|
|
434
|
+
labdata put <file1> <file2> -r twophoton
|
|
435
|
+
|
|
436
|
+
Options
|
|
437
|
+
-------
|
|
438
|
+
filepaths
|
|
439
|
+
One or more local file paths or a single folder (all files inside
|
|
440
|
+
the folder are selected recursively).
|
|
441
|
+
-r, --rule
|
|
442
|
+
Upload rule to apply on the server (e.g. ``ephys``, ``two_photon``,
|
|
443
|
+
``miniscope``, ``fixed_brain``). Default is ``none`` (copy only).
|
|
444
|
+
-a, --subject
|
|
445
|
+
Subject name to associate with the upload job.
|
|
446
|
+
-s, --session
|
|
447
|
+
Session name to associate with the upload job.
|
|
448
|
+
-t, --datatype-name
|
|
449
|
+
Dataset type name.
|
|
450
|
+
-p, --project
|
|
451
|
+
Project name.
|
|
452
|
+
--overwrite
|
|
453
|
+
Overwrite files that already exist on the server.
|
|
454
|
+
--ask
|
|
455
|
+
Prompt for confirmation before copying each file.
|
|
456
|
+
'''
|
|
274
457
|
parser = argparse.ArgumentParser(
|
|
275
|
-
description
|
|
276
|
-
usage
|
|
458
|
+
description='Copies data to the server to be uploaded [THIS DOES NOT UPLOAD TO THE CLOUD]',
|
|
459
|
+
usage='labdata put -a <SUBJECT> -s <SESSION>')
|
|
277
460
|
from .rules import rulesmap
|
|
278
461
|
rules_help = ','.join([tcolor['g'](r) for r in rulesmap.keys()])
|
|
279
462
|
parser = self._add_default_arguments(parser,include_project = True)
|
|
@@ -325,9 +508,34 @@ Server commands (don't run on experimental computers):
|
|
|
325
508
|
app.exit()
|
|
326
509
|
#sys.exit(app.exec_())
|
|
327
510
|
def clean(self):
|
|
511
|
+
'''Delete local copies of files that are confirmed uploaded to S3.
|
|
512
|
+
|
|
513
|
+
Compares MD5 checksums of local files against the ``File`` table.
|
|
514
|
+
Files that match are deleted; files not yet confirmed in the database
|
|
515
|
+
are kept. Use ``--dry-run`` to preview what would be deleted.
|
|
516
|
+
|
|
517
|
+
Usage
|
|
518
|
+
-----
|
|
519
|
+
::
|
|
520
|
+
|
|
521
|
+
labdata clean
|
|
522
|
+
labdata clean --dry-run
|
|
523
|
+
labdata clean -f ephys
|
|
524
|
+
labdata clean --only-processed
|
|
525
|
+
|
|
526
|
+
Options
|
|
527
|
+
-------
|
|
528
|
+
-f, --filter
|
|
529
|
+
Only consider files whose path contains this string (repeatable).
|
|
530
|
+
--dry-run
|
|
531
|
+
Print what would be deleted without actually deleting.
|
|
532
|
+
--only-processed
|
|
533
|
+
Only delete files listed in ``ProcessedFile`` (original raw files
|
|
534
|
+
that were transformed by an upload rule).
|
|
535
|
+
'''
|
|
328
536
|
parser = argparse.ArgumentParser(
|
|
329
|
-
description
|
|
330
|
-
usage
|
|
537
|
+
description='Releases local storage space.',
|
|
538
|
+
usage='labdata clean -f "ephys"')
|
|
331
539
|
parser.add_argument('-f','--filter',action = 'store',default = [], type = str, nargs = '+')
|
|
332
540
|
parser.add_argument('--dry-run',action = 'store_true', default = False)
|
|
333
541
|
parser.add_argument('--only-processed',action = 'store_true', default = False)
|
|
@@ -338,9 +546,57 @@ Server commands (don't run on experimental computers):
|
|
|
338
546
|
print(deleted,kept)
|
|
339
547
|
|
|
340
548
|
def run(self):
|
|
549
|
+
'''Queue or launch an analysis compute job.
|
|
550
|
+
|
|
551
|
+
Creates ``ComputeTask`` entries for the matching datasets and submits
|
|
552
|
+
them to the selected execution target (local Apptainer, SLURM, or EC2).
|
|
553
|
+
If ``-j`` is given, re-submits an existing job without creating a new one.
|
|
554
|
+
|
|
555
|
+
Usage
|
|
556
|
+
-----
|
|
557
|
+
::
|
|
558
|
+
|
|
559
|
+
labdata run <analysis> -a <subject> -s <session>
|
|
560
|
+
labdata run <analysis> -a <subject> -t slurm
|
|
561
|
+
labdata run <analysis> -a <subject> -t slurm -- <analysis-specific-args>
|
|
562
|
+
labdata run <analysis> --queue
|
|
563
|
+
labdata run <analysis> -j <job_id>
|
|
564
|
+
|
|
565
|
+
Options
|
|
566
|
+
-------
|
|
567
|
+
analysis
|
|
568
|
+
Name of the registered analysis (e.g. ``spks``, ``suite2p``,
|
|
569
|
+
``caiman``, ``deeplabcut``, ``populate``).
|
|
570
|
+
-a, --subject
|
|
571
|
+
One or more subject names.
|
|
572
|
+
-s, --session
|
|
573
|
+
One or more session names.
|
|
574
|
+
-d, --datatype
|
|
575
|
+
Filter by dataset type.
|
|
576
|
+
-j, --job
|
|
577
|
+
Re-use an existing ``ComputeTask`` job ID instead of creating one.
|
|
578
|
+
-t, --target
|
|
579
|
+
Execution target: ``local``, ``slurm``, or a named remote from
|
|
580
|
+
the preference file. Defaults to ``prefs["compute"]["default_target"]``.
|
|
581
|
+
-p, --project
|
|
582
|
+
Project name.
|
|
583
|
+
--force-submit
|
|
584
|
+
Submit even if an identical task command already exists.
|
|
585
|
+
--multisession
|
|
586
|
+
Combine all matched datasets into a single multi-session job.
|
|
587
|
+
--keep-files
|
|
588
|
+
Preserve intermediate files after the job completes.
|
|
589
|
+
--queue
|
|
590
|
+
Print the current job queue for all targets and exit.
|
|
591
|
+
--delete-computes
|
|
592
|
+
Delete all ``ComputeTask`` entries for this analysis (careful).
|
|
593
|
+
--
|
|
594
|
+
Everything after ``--`` is forwarded as secondary arguments to the
|
|
595
|
+
analysis class (e.g. algorithm flags, parameter set selection).
|
|
596
|
+
'''
|
|
341
597
|
parser = argparse.ArgumentParser(
|
|
342
|
-
description
|
|
343
|
-
usage
|
|
598
|
+
description='Allocates or runs an analysis',
|
|
599
|
+
usage=f'''labdata run <ANALYSIS> -a <SUBJECT> -s <SESSION>
|
|
344
600
|
|
|
345
601
|
Available analysis are
|
|
346
602
|
{tcolor['g'](', '.join(prefs["compute"]["analysis"].keys()))}''')
|
|
@@ -417,9 +673,37 @@ Available analysis are
|
|
|
417
673
|
run_analysis(target,jobids, obj, project = args.project)
|
|
418
674
|
|
|
419
675
|
def task_reset(self):
|
|
676
|
+
'''Reset a failed or stuck compute task so it can be retried.
|
|
677
|
+
|
|
678
|
+
Sets ``task_waiting=1`` and ``task_status="WAITING"`` for the specified
|
|
679
|
+
job(s) so they will be picked up by the next ``labdata task`` call.
|
|
680
|
+
With ``--resubmit``, immediately re-launches the job on the same or a
|
|
681
|
+
specified target.
|
|
682
|
+
|
|
683
|
+
Usage
|
|
684
|
+
-----
|
|
685
|
+
::
|
|
686
|
+
|
|
687
|
+
labdata task_reset <job_id>
|
|
688
|
+
labdata task_reset <job_id1> <job_id2>
|
|
689
|
+
labdata task_reset <job_id> --resubmit
|
|
690
|
+
labdata task_reset <job_id> --resubmit -t slurm
|
|
691
|
+
labdata task_reset --clear-all
|
|
692
|
+
|
|
693
|
+
Options
|
|
694
|
+
-------
|
|
695
|
+
job_id
|
|
696
|
+
One or more ``ComputeTask.job_id`` values.
|
|
697
|
+
-t, --target
|
|
698
|
+
Execution target for ``--resubmit`` (default: from job record).
|
|
699
|
+
--resubmit
|
|
700
|
+
After resetting, immediately re-launch the job.
|
|
701
|
+
--clear-all
|
|
702
|
+
Delete **all** compute tasks (requires confirmation).
|
|
703
|
+
'''
|
|
420
704
|
parser = argparse.ArgumentParser(
|
|
421
|
-
description
|
|
422
|
-
usage
|
|
705
|
+
description='Reset a task in the ComputeTask so the job can be ran again',
|
|
706
|
+
usage='labdata task_reset <JOB_ID>')
|
|
423
707
|
parser.add_argument('job_id', action = 'store', type = int, nargs='+')
|
|
424
708
|
parser.add_argument('-t','--target', action = 'store', default = None, type = str)
|
|
425
709
|
parser.add_argument('--resubmit', action = 'store_true', default = False)
|
|
@@ -453,9 +737,32 @@ Available analysis are
|
|
|
453
737
|
run_analysis(target,[job_id],obj)
|
|
454
738
|
|
|
455
739
|
def task(self):
|
|
740
|
+
'''Execute a single ``ComputeTask`` by job ID.
|
|
741
|
+
|
|
742
|
+
This is the command run by worker nodes (inside an Apptainer container
|
|
743
|
+
or on a SLURM job) to actually perform the analysis. It instantiates
|
|
744
|
+
the correct ``BaseCompute`` subclass for the job and calls ``compute()``.
|
|
745
|
+
|
|
746
|
+
You normally do not call this directly — use ``labdata run`` to queue
|
|
747
|
+
and submit jobs instead.
|
|
748
|
+
|
|
749
|
+
Usage
|
|
750
|
+
-----
|
|
751
|
+
::
|
|
752
|
+
|
|
753
|
+
labdata task <job_id>
|
|
754
|
+
labdata task <job_id> -p <project>
|
|
755
|
+
|
|
756
|
+
Options
|
|
757
|
+
-------
|
|
758
|
+
job_id
|
|
759
|
+
``ComputeTask.job_id`` to execute.
|
|
760
|
+
-p, --project
|
|
761
|
+
Project name (used when the worker has no preference file project set).
|
|
762
|
+
'''
|
|
456
763
|
parser = argparse.ArgumentParser(
|
|
457
|
-
description
|
|
458
|
-
usage
|
|
764
|
+
description='Runs a ComputeTask',
|
|
765
|
+
usage='labdata task <JOB_ID>')
|
|
459
766
|
parser.add_argument('job_id',action = 'store',default = None,type = int)
|
|
460
767
|
project = _get_project()
|
|
461
768
|
parser.add_argument('-p','--project',
|
|
@@ -471,9 +778,42 @@ Available analysis are
|
|
|
471
778
|
task.compute()
|
|
472
779
|
|
|
473
780
|
def upload(self):
|
|
781
|
+
'''Process pending ``UploadJob`` entries (server-side / admin command).
|
|
782
|
+
|
|
783
|
+
Fetches waiting upload jobs assigned to this host, applies the
|
|
784
|
+
appropriate upload rule (compression + S3 upload), and updates the
|
|
785
|
+
database. Run this on the upload server, not on experimental computers.
|
|
786
|
+
|
|
787
|
+
Usage
|
|
788
|
+
-----
|
|
789
|
+
::
|
|
790
|
+
|
|
791
|
+
labdata upload
|
|
792
|
+
labdata upload <job_id>
|
|
793
|
+
labdata upload --queue
|
|
794
|
+
labdata upload --reset-failed
|
|
795
|
+
labdata upload --all-hosts -n 4
|
|
796
|
+
|
|
797
|
+
Options
|
|
798
|
+
-------
|
|
799
|
+
job_id
|
|
800
|
+
Specific ``UploadJob.job_id`` values to process (optional).
|
|
801
|
+
When omitted, all waiting jobs for this host are processed.
|
|
802
|
+
-p, --project
|
|
803
|
+
Project name to filter jobs.
|
|
804
|
+
-n, --n-jobs
|
|
805
|
+
Number of parallel upload workers. Default is ``DEFAULT_N_JOBS``.
|
|
806
|
+
--queue
|
|
807
|
+
Print the current upload queue (working, failed, waiting, recent
|
|
808
|
+
completed jobs) and exit without processing.
|
|
809
|
+
--all-hosts
|
|
810
|
+
Process jobs assigned to any host, not just this machine.
|
|
811
|
+
--reset-failed
|
|
812
|
+
Reset all ``FAILED`` jobs to ``WAITING`` before processing.
|
|
813
|
+
'''
|
|
474
814
|
parser = argparse.ArgumentParser(
|
|
475
|
-
description
|
|
476
|
-
usage
|
|
815
|
+
description='Runs an UploadTask',
|
|
816
|
+
usage='labdata upload <JOB_ID> (optional)')
|
|
477
817
|
parser.add_argument('job_id',action = 'store',default = [], type = int, nargs = '*')
|
|
478
818
|
parser.add_argument('--all-hosts',action = 'store_true',default = False)
|
|
479
819
|
parser.add_argument('--reset-failed',action = 'store_true',default = False)
|
|
@@ -549,9 +889,34 @@ Available analysis are
|
|
|
549
889
|
tasks = process_upload_jobs(key)
|
|
550
890
|
|
|
551
891
|
def logpipe(self):
|
|
892
|
+
'''Pipe stdin into a ``ComputeTask.task_log`` entry.
|
|
893
|
+
|
|
894
|
+
Reads lines from stdin (intended to be piped from an analysis process)
|
|
895
|
+
and periodically flushes them to the ``task_log`` field of the given
|
|
896
|
+
``ComputeTask``. This lets container jobs write their stdout to the
|
|
897
|
+
database for remote monitoring.
|
|
898
|
+
|
|
899
|
+
Typical usage (inside an sbatch script)::
|
|
900
|
+
|
|
901
|
+
labdata task <job_id> 2>&1 | labdata logpipe <job_id>
|
|
902
|
+
|
|
903
|
+
Usage
|
|
904
|
+
-----
|
|
905
|
+
::
|
|
906
|
+
|
|
907
|
+
labdata logpipe <job_id>
|
|
908
|
+
labdata logpipe <job_id> -i 10
|
|
909
|
+
|
|
910
|
+
Options
|
|
911
|
+
-------
|
|
912
|
+
job_id
|
|
913
|
+
``ComputeTask.job_id`` to write logs to.
|
|
914
|
+
-i, --refresh-period
|
|
915
|
+
How often (seconds) to flush the log to the database. Default 5.
|
|
916
|
+
'''
|
|
552
917
|
parser = argparse.ArgumentParser(
|
|
553
|
-
description
|
|
554
|
-
usage
|
|
918
|
+
description='Sends the stdout to a log',
|
|
919
|
+
usage='labdata logpipe <JOB_ID>')
|
|
555
920
|
parser.add_argument('job_id',action = 'store',default = None,type = int)
|
|
556
921
|
parser.add_argument('-i','--refresh-period',action = 'store',default = 5., type = float)
|
|
557
922
|
args = parser.parse_args(sys.argv[2:])
|
|
@@ -572,7 +937,8 @@ Available analysis are
|
|
|
572
937
|
return
|
|
573
938
|
else:
|
|
574
939
|
print(f'Appending stdout to ComputeTask [{job_id}] ')
|
|
575
|
-
def handle_line(line,log, tic):
|
|
940
|
+
def handle_line(line, log, tic):
|
|
941
|
+
'''Append a line to the running log buffer and periodically flush to the DB.'''
|
|
576
942
|
if not line is None:
|
|
577
943
|
if log is None:
|
|
578
944
|
log = line
|
|
@@ -742,5 +1108,15 @@ def _get_project():
|
|
|
742
1108
|
project = str(os.environ['LABDATA_DATABASE_PROJECT'])
|
|
743
1109
|
return project
|
|
744
1110
|
|
|
1111
|
+
def main_deprecated():
|
|
1112
|
+
import sys
|
|
1113
|
+
print("Warning: 'labdata2' is deprecated, use 'labdata' instead.", file=sys.stderr)
|
|
1114
|
+
main()
|
|
1115
|
+
|
|
745
1116
|
def main():
|
|
1117
|
+
'''Entry point for the ``labdata`` command-line tool.
|
|
1118
|
+
|
|
1119
|
+
Instantiates ``CLI_parser``, which reads ``sys.argv`` and dispatches to
|
|
1120
|
+
the appropriate sub-command method.
|
|
1121
|
+
'''
|
|
746
1122
|
CLI_parser()
|
|
@@ -2,21 +2,27 @@ from ..utils import *
|
|
|
2
2
|
from .utils import BaseCompute
|
|
3
3
|
from .suite2p import Suite2pCompute
|
|
4
4
|
class CaimanCompute(Suite2pCompute):
|
|
5
|
+
'''Cell segmentation compute class using CaImAn.
|
|
6
|
+
|
|
7
|
+
Works for both one-photon (miniscope) and two-photon datasets. Runs
|
|
8
|
+
inside the ``labdata-caiman`` Apptainer container (CPU-only).
|
|
9
|
+
|
|
10
|
+
The compute pipeline:
|
|
11
|
+
|
|
12
|
+
1. Identify files and determine dataset type (``Miniscope`` or ``TwoPhoton``).
|
|
13
|
+
2. Copy the ``zarr.zip`` stack to the scratch directory.
|
|
14
|
+
3. Run CaImAn CNMF/CNMF-E with the specified parameters.
|
|
15
|
+
4. Delete memory-mapped intermediate files.
|
|
16
|
+
5. Upload the HDF5 results file to S3 and insert into ``CellSegmentation``.
|
|
17
|
+
|
|
18
|
+
Parameters are stored in ``CellSegmentationParams`` (shared with
|
|
19
|
+
``Suite2pCompute`` using the same part tables).
|
|
20
|
+
'''
|
|
5
21
|
container = 'labdata-caiman'
|
|
6
22
|
cuda = False
|
|
7
23
|
name = 'caiman'
|
|
8
24
|
url = 'http://github.com/flatironinstitute/CaImAn'
|
|
9
|
-
def __init__(self,job_id, project
|
|
10
|
-
'''
|
|
11
|
-
This class runs Caiman on a dataset, which can be used for both 1p and 2p data. The ComputeTask will:
|
|
12
|
-
|
|
13
|
-
1. **File Identification and Dataset Type Check**: Identify the files and determine the type of dataset (Miniscope or TwoPhoton).
|
|
14
|
-
2. **File Copy to Scratch**: Copy only the necessary file(s) to a scratch folder for processing.
|
|
15
|
-
3. **Caiman Execution**: Execute Caiman on the copied file or folder. Using the parameters specified.
|
|
16
|
-
4. **Cleanup and Result Integration**: Delete the memory-mapped files generated during processing and integrate the results into the CellSegmentation table.
|
|
17
|
-
|
|
18
|
-
This class includes a handler for the CLI.
|
|
19
|
-
'''
|
|
25
|
+
def __init__(self, job_id, project=None, allow_s3=None, **kwargs):
|
|
20
26
|
super(Suite2pCompute,self).__init__(job_id, project = project, allow_s3 = allow_s3) # takes BaseCompute init
|
|
21
27
|
self.file_filters = ['.zarr.zip'] # this only runs on zarr.zip for the moment.
|
|
22
28
|
# default parameters
|
|
@@ -359,7 +365,23 @@ class CaimanCompute(Suite2pCompute):
|
|
|
359
365
|
print(f'[{self.name} job] Kept the temporary folder {temporary_folder}.')
|
|
360
366
|
self.schema.CellSegmentationMetrics().populate(dkey, display_progress = True)
|
|
361
367
|
|
|
362
|
-
def setup_cluster(n_cpus,cluster
|
|
368
|
+
def setup_cluster(n_cpus, cluster=None):
|
|
369
|
+
'''Start (or restart) a CaImAn multiprocessing cluster.
|
|
370
|
+
|
|
371
|
+
If ``cluster`` is not ``None``, the existing cluster is stopped first.
|
|
372
|
+
|
|
373
|
+
Parameters
|
|
374
|
+
----------
|
|
375
|
+
n_cpus : int
|
|
376
|
+
Number of parallel worker processes.
|
|
377
|
+
cluster : object or None, optional
|
|
378
|
+
Existing CaImAn cluster object to stop before starting a new one.
|
|
379
|
+
|
|
380
|
+
Returns
|
|
381
|
+
-------
|
|
382
|
+
object
|
|
383
|
+
The newly created CaImAn cluster (``dview``).
|
|
384
|
+
'''
|
|
363
385
|
from caiman import stop_server, cluster
|
|
364
386
|
#%% start a cluster for parallel processing (if a cluster already exists it will be closed and a new session will be opened)
|
|
365
387
|
if not cluster is None: # 'locals' contains list of current local variables
|