brainforge-cli 1.0.0__tar.gz → 1.0.1__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.
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/PKG-INFO +1 -1
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge/__init__.py +1 -1
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge/cli.py +5 -14
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge/run.py +8 -31
- brainforge_cli-1.0.1/brainforge/template.py +44 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge_cli.egg-info/PKG-INFO +1 -1
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/setup.py +1 -1
- brainforge_cli-1.0.0/brainforge/template.py +0 -72
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/README.md +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge/auth.py +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge/job_logs.py +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge/job_status.py +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge_cli.egg-info/SOURCES.txt +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge_cli.egg-info/dependency_links.txt +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge_cli.egg-info/entry_points.txt +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge_cli.egg-info/requires.txt +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/brainforge_cli.egg-info/top_level.txt +0 -0
- {brainforge_cli-1.0.0 → brainforge_cli-1.0.1}/setup.cfg +0 -0
|
@@ -12,7 +12,7 @@ from brainforge.template import generate_yaml_template
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@click.group()
|
|
15
|
-
@click.version_option(version="1.0.
|
|
15
|
+
@click.version_option(version="1.0.1")
|
|
16
16
|
def main():
|
|
17
17
|
"""
|
|
18
18
|
BrainForge CLI: The premier interface for executing neuroimaging analyses.
|
|
@@ -26,7 +26,7 @@ def main():
|
|
|
26
26
|
@main.command()
|
|
27
27
|
def info():
|
|
28
28
|
"""Display information about Brainforge CLI and its current version."""
|
|
29
|
-
click.echo("Welcome to the Brainforge CLI v1.0.
|
|
29
|
+
click.echo("Welcome to the Brainforge CLI v1.0.1")
|
|
30
30
|
click.echo("Run 'brainforge --help' to see all available commands.")
|
|
31
31
|
|
|
32
32
|
|
|
@@ -137,15 +137,6 @@ def logs(job_id, log_type, follow):
|
|
|
137
137
|
|
|
138
138
|
|
|
139
139
|
@job.command(name="generate")
|
|
140
|
-
@click.option(
|
|
141
|
-
'--type',
|
|
142
|
-
'-t',
|
|
143
|
-
'job_type', # map back to variable name
|
|
144
|
-
prompt='Select job template type',
|
|
145
|
-
type=click.Choice(['single', 'multiple']),
|
|
146
|
-
default='single',
|
|
147
|
-
help="Type of job template (single or multiple).",
|
|
148
|
-
)
|
|
149
140
|
@click.option(
|
|
150
141
|
'--name',
|
|
151
142
|
'-n',
|
|
@@ -153,15 +144,15 @@ def logs(job_id, log_type, follow):
|
|
|
153
144
|
default='template.yaml',
|
|
154
145
|
help="Output filename for the generated template.",
|
|
155
146
|
)
|
|
156
|
-
def generate_cmd(
|
|
147
|
+
def generate_cmd(name):
|
|
157
148
|
"""
|
|
158
149
|
Generate a working YAML template for a BrainForge SLURM job.
|
|
159
150
|
|
|
160
151
|
Creates a boilerplate .yaml file pre-filled with all required
|
|
161
152
|
variables (`SINGULARITY_IMAGE`, `HOST_INPUT_DIR`, etc.) to run
|
|
162
|
-
|
|
153
|
+
an array job over one or more `participants`.
|
|
163
154
|
"""
|
|
164
|
-
generate_yaml_template(
|
|
155
|
+
generate_yaml_template(name)
|
|
165
156
|
|
|
166
157
|
|
|
167
158
|
if __name__ == "__main__":
|
|
@@ -99,38 +99,21 @@ def validate_spec(spec):
|
|
|
99
99
|
sys.exit(1)
|
|
100
100
|
|
|
101
101
|
participants = execution.get('participants')
|
|
102
|
-
participant = execution.get('participant')
|
|
103
102
|
|
|
104
|
-
if participants is
|
|
103
|
+
if participants is None:
|
|
105
104
|
click.secho(
|
|
106
|
-
"Error:
|
|
105
|
+
"Error: You must specify 'participants' as a list of identifiers.",
|
|
107
106
|
fg="red",
|
|
108
107
|
)
|
|
109
108
|
sys.exit(1)
|
|
110
109
|
|
|
111
|
-
if participants
|
|
110
|
+
if not isinstance(participants, list) or len(participants) == 0:
|
|
112
111
|
click.secho(
|
|
113
|
-
"Error:
|
|
112
|
+
"Error: 'participants' must be a non-empty list of subject identifiers.",
|
|
114
113
|
fg="red",
|
|
115
114
|
)
|
|
116
115
|
sys.exit(1)
|
|
117
116
|
|
|
118
|
-
if participants is not None:
|
|
119
|
-
if not isinstance(participants, list) or len(participants) == 0:
|
|
120
|
-
click.secho(
|
|
121
|
-
"Error: 'participants' must be a non-empty list of subject identifiers.",
|
|
122
|
-
fg="red",
|
|
123
|
-
)
|
|
124
|
-
sys.exit(1)
|
|
125
|
-
|
|
126
|
-
if participant is not None:
|
|
127
|
-
# Checking if it's truthy handles empty strings intuitively (e.g. "")
|
|
128
|
-
if not str(participant).strip():
|
|
129
|
-
click.secho(
|
|
130
|
-
"Error: 'participant' must be a valid, non-empty identifier.", fg="red"
|
|
131
|
-
)
|
|
132
|
-
sys.exit(1)
|
|
133
|
-
|
|
134
117
|
|
|
135
118
|
def generate_sbatch_script(spec):
|
|
136
119
|
lines = ["#!/bin/bash"]
|
|
@@ -159,21 +142,15 @@ def generate_sbatch_script(spec):
|
|
|
159
142
|
lines.append("# --- Auto-generated by BrainForge CLI ---")
|
|
160
143
|
lines.append("")
|
|
161
144
|
|
|
162
|
-
# 3. Handle array item mapping
|
|
145
|
+
# 3. Handle array item mapping
|
|
163
146
|
if participants:
|
|
164
|
-
lines.append('
|
|
147
|
+
lines.append('PARTICIPANTS=(')
|
|
165
148
|
for item in participants:
|
|
166
149
|
# Safely quote items
|
|
167
150
|
lines.append(f' "{item}"')
|
|
168
151
|
lines.append(')')
|
|
169
|
-
lines.append('
|
|
170
|
-
lines.append('echo "BrainForge Task running for participant: $
|
|
171
|
-
lines.append("")
|
|
172
|
-
elif 'participant' in execution:
|
|
173
|
-
# Singular participant definition
|
|
174
|
-
val = execution['participant']
|
|
175
|
-
lines.append(f'BFG_PARTICIPANT="{val}"')
|
|
176
|
-
lines.append('echo "BrainForge Task running for participant: $BFG_PARTICIPANT"')
|
|
152
|
+
lines.append('PARTICIPANT=${PARTICIPANTS[$SLURM_ARRAY_TASK_ID]}')
|
|
153
|
+
lines.append('echo "BrainForge Task running for participant: $PARTICIPANT"')
|
|
177
154
|
lines.append("")
|
|
178
155
|
|
|
179
156
|
# 4. Modules loading
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
JOB_TEMPLATE = """# Job Template for BrainForge CLI
|
|
6
|
+
slurm:
|
|
7
|
+
job-name: "name"
|
|
8
|
+
partition: "partition"
|
|
9
|
+
account: "account"
|
|
10
|
+
nodes: 1
|
|
11
|
+
ntasks: 1
|
|
12
|
+
mem: "8G"
|
|
13
|
+
cpus-per-task: 4
|
|
14
|
+
output: "logs/job_%A_%a.out"
|
|
15
|
+
error: "logs/job_%A_%a.err"
|
|
16
|
+
|
|
17
|
+
execution:
|
|
18
|
+
modules:
|
|
19
|
+
- singularity
|
|
20
|
+
env:
|
|
21
|
+
SINGULARITY_IMAGE: "/path/to/container.sif"
|
|
22
|
+
HOST_INPUT_DIR: "/path/to/input"
|
|
23
|
+
HOST_OUTPUT_DIR: "/path/to/output"
|
|
24
|
+
participants:
|
|
25
|
+
- "sub-01"
|
|
26
|
+
- "sub-02"
|
|
27
|
+
- "sub-03"
|
|
28
|
+
command: "singularity run --bind $HOST_INPUT_DIR:/input --bind $HOST_OUTPUT_DIR:/output $SINGULARITY_IMAGE --participant $PARTICIPANT"
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def generate_yaml_template(output_name):
|
|
33
|
+
# Ensure we don't accidentally overwrite without warning, though
|
|
34
|
+
# for simplicity we'll just open and write.
|
|
35
|
+
# If file exists, click.confirm could be used, but let's keep it simple first.
|
|
36
|
+
if os.path.exists(output_name):
|
|
37
|
+
click.confirm(f"File '{output_name}' already exists. Overwrite?", abort=True)
|
|
38
|
+
|
|
39
|
+
with open(output_name, 'w') as f:
|
|
40
|
+
f.write(JOB_TEMPLATE)
|
|
41
|
+
|
|
42
|
+
click.secho(
|
|
43
|
+
f"Successfully generated template: {output_name}", fg="green"
|
|
44
|
+
)
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
import click
|
|
4
|
-
|
|
5
|
-
SINGLE_JOB_TEMPLATE = """# Single Job Template for BrainForge CLI
|
|
6
|
-
slurm:
|
|
7
|
-
job-name: "name"
|
|
8
|
-
partition: "partition"
|
|
9
|
-
account: "account"
|
|
10
|
-
nodes: 1
|
|
11
|
-
ntasks: 1
|
|
12
|
-
mem: "8G"
|
|
13
|
-
cpus-per-task: 4
|
|
14
|
-
output: "logs/job_%j.out"
|
|
15
|
-
error: "logs/job_%j.err"
|
|
16
|
-
|
|
17
|
-
execution:
|
|
18
|
-
modules:
|
|
19
|
-
- singularity
|
|
20
|
-
env:
|
|
21
|
-
SINGULARITY_IMAGE: "/path/to/container.sif"
|
|
22
|
-
HOST_INPUT_DIR: "/path/to/input"
|
|
23
|
-
HOST_OUTPUT_DIR: "/path/to/output"
|
|
24
|
-
participant: "sub-01"
|
|
25
|
-
command: "echo 'Running Single Job for item $BFG_PARTICIPANT'"
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
MULTIPLE_JOB_TEMPLATE = """# Multiple Job Template for BrainForge CLI
|
|
29
|
-
slurm:
|
|
30
|
-
job-name: "name"
|
|
31
|
-
partition: "partition"
|
|
32
|
-
account: "account"
|
|
33
|
-
nodes: 1
|
|
34
|
-
ntasks: 1
|
|
35
|
-
mem: "8G"
|
|
36
|
-
cpus-per-task: 4
|
|
37
|
-
output: "logs/job_%A_%a.out"
|
|
38
|
-
error: "logs/job_%A_%a.err"
|
|
39
|
-
|
|
40
|
-
execution:
|
|
41
|
-
modules:
|
|
42
|
-
- singularity
|
|
43
|
-
env:
|
|
44
|
-
SINGULARITY_IMAGE: "/path/to/container.sif"
|
|
45
|
-
HOST_INPUT_DIR: "/path/to/input"
|
|
46
|
-
HOST_OUTPUT_DIR: "/path/to/output"
|
|
47
|
-
participants:
|
|
48
|
-
- "sub-01"
|
|
49
|
-
- "sub-02"
|
|
50
|
-
- "sub-03"
|
|
51
|
-
command: "echo 'Running Multiple Job for item $BFG_PARTICIPANT'"
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def generate_yaml_template(job_type, output_name):
|
|
56
|
-
if job_type == 'multiple':
|
|
57
|
-
content = MULTIPLE_JOB_TEMPLATE
|
|
58
|
-
else:
|
|
59
|
-
content = SINGLE_JOB_TEMPLATE
|
|
60
|
-
|
|
61
|
-
# Ensure we don't accidentally overwrite without warning, though
|
|
62
|
-
# for simplicity we'll just open and write.
|
|
63
|
-
# If file exists, click.confirm could be used, but let's keep it simple first.
|
|
64
|
-
if os.path.exists(output_name):
|
|
65
|
-
click.confirm(f"File '{output_name}' already exists. Overwrite?", abort=True)
|
|
66
|
-
|
|
67
|
-
with open(output_name, 'w') as f:
|
|
68
|
-
f.write(content)
|
|
69
|
-
|
|
70
|
-
click.secho(
|
|
71
|
-
f"Successfully generated {job_type} template: {output_name}", fg="green"
|
|
72
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|