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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brainforge-cli
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Command-line interface for Brainforge
5
5
  Author: Daniel Trinh
6
6
  Requires-Python: >=3.10
@@ -2,4 +2,4 @@
2
2
  Brainforge CLI Package
3
3
  """
4
4
 
5
- __version__ = "1.0.0"
5
+ __version__ = "1.0.1"
@@ -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.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.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(job_type, name):
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
- either a `single` job or an array job over `multiple` participants.
153
+ an array job over one or more `participants`.
163
154
  """
164
- generate_yaml_template(job_type, name)
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 not None and participant is not None:
103
+ if participants is None:
105
104
  click.secho(
106
- "Error: Cannot specify both 'participant' and 'participants'. Choose one.",
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 is None and participant is None:
110
+ if not isinstance(participants, list) or len(participants) == 0:
112
111
  click.secho(
113
- "Error: You must specify either 'participant' (for single jobs) or 'participants' (for array jobs).",
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 or single participant mapping
145
+ # 3. Handle array item mapping
163
146
  if participants:
164
- lines.append('BFG_PARTICIPANTS=(')
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('BFG_PARTICIPANT=${BFG_PARTICIPANTS[$SLURM_ARRAY_TASK_ID]}')
170
- lines.append('echo "BrainForge Task running for participant: $BFG_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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brainforge-cli
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Command-line interface for Brainforge
5
5
  Author: Daniel Trinh
6
6
  Requires-Python: >=3.10
@@ -3,7 +3,7 @@ from setuptools import setup
3
3
 
4
4
  setup(
5
5
  name="brainforge-cli",
6
- version="1.0.0",
6
+ version="1.0.1",
7
7
  description="Command-line interface for Brainforge",
8
8
  author="Daniel Trinh",
9
9
  packages=find_packages(),
@@ -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