junifer 0.0.3.dev186__py3-none-any.whl → 0.0.4__py3-none-any.whl
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.
- junifer/_version.py +14 -2
- junifer/api/cli.py +162 -17
- junifer/api/functions.py +87 -419
- junifer/api/parser.py +24 -0
- junifer/api/queue_context/__init__.py +8 -0
- junifer/api/queue_context/gnu_parallel_local_adapter.py +258 -0
- junifer/api/queue_context/htcondor_adapter.py +365 -0
- junifer/api/queue_context/queue_context_adapter.py +60 -0
- junifer/api/queue_context/tests/test_gnu_parallel_local_adapter.py +192 -0
- junifer/api/queue_context/tests/test_htcondor_adapter.py +257 -0
- junifer/api/res/afni/run_afni_docker.sh +6 -6
- junifer/api/res/ants/ResampleImage +3 -0
- junifer/api/res/ants/antsApplyTransforms +3 -0
- junifer/api/res/ants/antsApplyTransformsToPoints +3 -0
- junifer/api/res/ants/run_ants_docker.sh +39 -0
- junifer/api/res/fsl/applywarp +3 -0
- junifer/api/res/fsl/flirt +3 -0
- junifer/api/res/fsl/img2imgcoord +3 -0
- junifer/api/res/fsl/run_fsl_docker.sh +39 -0
- junifer/api/res/fsl/std2imgcoord +3 -0
- junifer/api/res/run_conda.sh +4 -4
- junifer/api/res/run_venv.sh +22 -0
- junifer/api/tests/data/partly_cloudy_agg_mean_tian.yml +16 -0
- junifer/api/tests/test_api_utils.py +21 -3
- junifer/api/tests/test_cli.py +232 -9
- junifer/api/tests/test_functions.py +211 -439
- junifer/api/tests/test_parser.py +1 -1
- junifer/configs/juseless/datagrabbers/aomic_id1000_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/camcan_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/ixi_vbm.py +6 -1
- junifer/configs/juseless/datagrabbers/tests/test_ucla.py +8 -8
- junifer/configs/juseless/datagrabbers/ucla.py +44 -26
- junifer/configs/juseless/datagrabbers/ukb_vbm.py +6 -1
- junifer/data/VOIs/meta/AutobiographicalMemory_VOIs.txt +23 -0
- junifer/data/VOIs/meta/Power2013_MNI_VOIs.tsv +264 -0
- junifer/data/__init__.py +4 -0
- junifer/data/coordinates.py +298 -31
- junifer/data/masks.py +360 -28
- junifer/data/parcellations.py +621 -188
- junifer/data/template_spaces.py +190 -0
- junifer/data/tests/test_coordinates.py +34 -3
- junifer/data/tests/test_data_utils.py +1 -0
- junifer/data/tests/test_masks.py +202 -86
- junifer/data/tests/test_parcellations.py +266 -55
- junifer/data/tests/test_template_spaces.py +104 -0
- junifer/data/utils.py +4 -2
- junifer/datagrabber/__init__.py +1 -0
- junifer/datagrabber/aomic/id1000.py +111 -70
- junifer/datagrabber/aomic/piop1.py +116 -53
- junifer/datagrabber/aomic/piop2.py +116 -53
- junifer/datagrabber/aomic/tests/test_id1000.py +27 -27
- junifer/datagrabber/aomic/tests/test_piop1.py +27 -27
- junifer/datagrabber/aomic/tests/test_piop2.py +27 -27
- junifer/datagrabber/base.py +62 -10
- junifer/datagrabber/datalad_base.py +0 -2
- junifer/datagrabber/dmcc13_benchmark.py +372 -0
- junifer/datagrabber/hcp1200/datalad_hcp1200.py +5 -0
- junifer/datagrabber/hcp1200/hcp1200.py +30 -13
- junifer/datagrabber/pattern.py +133 -27
- junifer/datagrabber/pattern_datalad.py +111 -13
- junifer/datagrabber/tests/test_base.py +57 -6
- junifer/datagrabber/tests/test_datagrabber_utils.py +204 -76
- junifer/datagrabber/tests/test_datalad_base.py +0 -6
- junifer/datagrabber/tests/test_dmcc13_benchmark.py +256 -0
- junifer/datagrabber/tests/test_multiple.py +43 -10
- junifer/datagrabber/tests/test_pattern.py +125 -178
- junifer/datagrabber/tests/test_pattern_datalad.py +44 -25
- junifer/datagrabber/utils.py +151 -16
- junifer/datareader/default.py +36 -10
- junifer/external/nilearn/junifer_nifti_spheres_masker.py +6 -0
- junifer/markers/base.py +25 -16
- junifer/markers/collection.py +35 -16
- junifer/markers/complexity/__init__.py +27 -0
- junifer/markers/complexity/complexity_base.py +149 -0
- junifer/markers/complexity/hurst_exponent.py +136 -0
- junifer/markers/complexity/multiscale_entropy_auc.py +140 -0
- junifer/markers/complexity/perm_entropy.py +132 -0
- junifer/markers/complexity/range_entropy.py +136 -0
- junifer/markers/complexity/range_entropy_auc.py +145 -0
- junifer/markers/complexity/sample_entropy.py +134 -0
- junifer/markers/complexity/tests/test_complexity_base.py +19 -0
- junifer/markers/complexity/tests/test_hurst_exponent.py +69 -0
- junifer/markers/complexity/tests/test_multiscale_entropy_auc.py +68 -0
- junifer/markers/complexity/tests/test_perm_entropy.py +68 -0
- junifer/markers/complexity/tests/test_range_entropy.py +69 -0
- junifer/markers/complexity/tests/test_range_entropy_auc.py +69 -0
- junifer/markers/complexity/tests/test_sample_entropy.py +68 -0
- junifer/markers/complexity/tests/test_weighted_perm_entropy.py +68 -0
- junifer/markers/complexity/weighted_perm_entropy.py +133 -0
- junifer/markers/falff/_afni_falff.py +153 -0
- junifer/markers/falff/_junifer_falff.py +142 -0
- junifer/markers/falff/falff_base.py +91 -84
- junifer/markers/falff/falff_parcels.py +61 -45
- junifer/markers/falff/falff_spheres.py +64 -48
- junifer/markers/falff/tests/test_falff_parcels.py +89 -121
- junifer/markers/falff/tests/test_falff_spheres.py +92 -127
- junifer/markers/functional_connectivity/crossparcellation_functional_connectivity.py +1 -0
- junifer/markers/functional_connectivity/edge_functional_connectivity_parcels.py +1 -0
- junifer/markers/functional_connectivity/functional_connectivity_base.py +1 -0
- junifer/markers/functional_connectivity/tests/test_crossparcellation_functional_connectivity.py +46 -44
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_parcels.py +34 -39
- junifer/markers/functional_connectivity/tests/test_edge_functional_connectivity_spheres.py +40 -52
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_parcels.py +62 -70
- junifer/markers/functional_connectivity/tests/test_functional_connectivity_spheres.py +99 -85
- junifer/markers/parcel_aggregation.py +60 -38
- junifer/markers/reho/_afni_reho.py +192 -0
- junifer/markers/reho/_junifer_reho.py +281 -0
- junifer/markers/reho/reho_base.py +69 -34
- junifer/markers/reho/reho_parcels.py +26 -16
- junifer/markers/reho/reho_spheres.py +23 -9
- junifer/markers/reho/tests/test_reho_parcels.py +93 -92
- junifer/markers/reho/tests/test_reho_spheres.py +88 -86
- junifer/markers/sphere_aggregation.py +54 -9
- junifer/markers/temporal_snr/temporal_snr_base.py +1 -0
- junifer/markers/temporal_snr/tests/test_temporal_snr_parcels.py +38 -37
- junifer/markers/temporal_snr/tests/test_temporal_snr_spheres.py +34 -38
- junifer/markers/tests/test_collection.py +43 -42
- junifer/markers/tests/test_ets_rss.py +29 -37
- junifer/markers/tests/test_parcel_aggregation.py +587 -468
- junifer/markers/tests/test_sphere_aggregation.py +209 -157
- junifer/markers/utils.py +2 -40
- junifer/onthefly/read_transform.py +13 -6
- junifer/pipeline/__init__.py +1 -0
- junifer/pipeline/pipeline_step_mixin.py +105 -41
- junifer/pipeline/registry.py +17 -0
- junifer/pipeline/singleton.py +45 -0
- junifer/pipeline/tests/test_pipeline_step_mixin.py +139 -51
- junifer/pipeline/tests/test_update_meta_mixin.py +1 -0
- junifer/pipeline/tests/test_workdir_manager.py +104 -0
- junifer/pipeline/update_meta_mixin.py +8 -2
- junifer/pipeline/utils.py +154 -15
- junifer/pipeline/workdir_manager.py +246 -0
- junifer/preprocess/__init__.py +3 -0
- junifer/preprocess/ants/__init__.py +4 -0
- junifer/preprocess/ants/ants_apply_transforms_warper.py +185 -0
- junifer/preprocess/ants/tests/test_ants_apply_transforms_warper.py +56 -0
- junifer/preprocess/base.py +96 -69
- junifer/preprocess/bold_warper.py +265 -0
- junifer/preprocess/confounds/fmriprep_confound_remover.py +91 -134
- junifer/preprocess/confounds/tests/test_fmriprep_confound_remover.py +106 -111
- junifer/preprocess/fsl/__init__.py +4 -0
- junifer/preprocess/fsl/apply_warper.py +179 -0
- junifer/preprocess/fsl/tests/test_apply_warper.py +45 -0
- junifer/preprocess/tests/test_bold_warper.py +159 -0
- junifer/preprocess/tests/test_preprocess_base.py +6 -6
- junifer/preprocess/warping/__init__.py +6 -0
- junifer/preprocess/warping/_ants_warper.py +167 -0
- junifer/preprocess/warping/_fsl_warper.py +109 -0
- junifer/preprocess/warping/space_warper.py +213 -0
- junifer/preprocess/warping/tests/test_space_warper.py +198 -0
- junifer/stats.py +18 -4
- junifer/storage/base.py +9 -1
- junifer/storage/hdf5.py +8 -3
- junifer/storage/pandas_base.py +2 -1
- junifer/storage/sqlite.py +1 -0
- junifer/storage/tests/test_hdf5.py +2 -1
- junifer/storage/tests/test_sqlite.py +8 -8
- junifer/storage/tests/test_utils.py +6 -6
- junifer/storage/utils.py +1 -0
- junifer/testing/datagrabbers.py +11 -7
- junifer/testing/utils.py +1 -0
- junifer/tests/test_stats.py +2 -0
- junifer/utils/__init__.py +1 -0
- junifer/utils/helpers.py +53 -0
- junifer/utils/logging.py +14 -3
- junifer/utils/tests/test_helpers.py +35 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/METADATA +59 -28
- junifer-0.0.4.dist-info/RECORD +257 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/WHEEL +1 -1
- junifer/markers/falff/falff_estimator.py +0 -334
- junifer/markers/falff/tests/test_falff_estimator.py +0 -238
- junifer/markers/reho/reho_estimator.py +0 -515
- junifer/markers/reho/tests/test_reho_estimator.py +0 -260
- junifer-0.0.3.dev186.dist-info/RECORD +0 -199
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/AUTHORS.rst +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/LICENSE.md +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/entry_points.txt +0 -0
- {junifer-0.0.3.dev186.dist-info → junifer-0.0.4.dist-info}/top_level.txt +0 -0
junifer/api/functions.py
CHANGED
@@ -6,8 +6,6 @@
|
|
6
6
|
# License: AGPL
|
7
7
|
|
8
8
|
import shutil
|
9
|
-
import subprocess
|
10
|
-
import textwrap
|
11
9
|
import typing
|
12
10
|
from pathlib import Path
|
13
11
|
from typing import Dict, List, Optional, Tuple, Union
|
@@ -15,11 +13,12 @@ from typing import Dict, List, Optional, Tuple, Union
|
|
15
13
|
from ..datagrabber.base import BaseDataGrabber
|
16
14
|
from ..markers.base import BaseMarker
|
17
15
|
from ..markers.collection import MarkerCollection
|
16
|
+
from ..pipeline import WorkDirManager
|
18
17
|
from ..pipeline.registry import build
|
19
18
|
from ..preprocess.base import BasePreprocessor
|
20
19
|
from ..storage.base import BaseFeatureStorage
|
21
20
|
from ..utils import logger, raise_error
|
22
|
-
from
|
21
|
+
from .queue_context import GnuParallelLocalAdapter, HTCondorAdapter
|
23
22
|
from .utils import yaml
|
24
23
|
|
25
24
|
|
@@ -80,7 +79,7 @@ def run(
|
|
80
79
|
datagrabber: Dict,
|
81
80
|
markers: List[Dict],
|
82
81
|
storage: Dict,
|
83
|
-
|
82
|
+
preprocessors: Optional[List[Dict]] = None,
|
84
83
|
elements: Union[str, List[Union[str, Tuple]], Tuple, None] = None,
|
85
84
|
) -> None:
|
86
85
|
"""Run the pipeline on the selected element.
|
@@ -103,10 +102,10 @@ def run(
|
|
103
102
|
Storage to use. Must have a key ``kind`` with the kind of
|
104
103
|
storage to use. All other keys are passed to the storage
|
105
104
|
init function.
|
106
|
-
|
107
|
-
|
108
|
-
preprocessor to use. All other keys
|
109
|
-
init function (default None).
|
105
|
+
preprocessors : list of dict, optional
|
106
|
+
List of preprocessors to use. Each preprocessor is a dict with at
|
107
|
+
least a key ``kind`` specifying the preprocessor to use. All other keys
|
108
|
+
are passed to the preprocessor init function (default None).
|
110
109
|
elements : str or tuple or list of str or tuple, optional
|
111
110
|
Element(s) to process. Will be used to index the DataGrabber
|
112
111
|
(default None).
|
@@ -115,6 +114,8 @@ def run(
|
|
115
114
|
# Convert str to Path
|
116
115
|
if isinstance(workdir, str):
|
117
116
|
workdir = Path(workdir)
|
117
|
+
# Initiate working directory manager
|
118
|
+
WorkDirManager(workdir)
|
118
119
|
|
119
120
|
if not isinstance(elements, list) and elements is not None:
|
120
121
|
elements = [elements]
|
@@ -149,21 +150,29 @@ def run(
|
|
149
150
|
storage_object = typing.cast(BaseFeatureStorage, storage_object)
|
150
151
|
|
151
152
|
# Get preprocessor to use (if provided)
|
152
|
-
if
|
153
|
-
|
153
|
+
if preprocessors is not None:
|
154
|
+
_preprocessors = [x.copy() for x in preprocessors]
|
155
|
+
built_preprocessors = []
|
156
|
+
for preprocessor in _preprocessors:
|
157
|
+
preprocessor_object = _get_preprocessor(preprocessor)
|
158
|
+
built_preprocessors.append(preprocessor_object)
|
154
159
|
else:
|
155
|
-
|
160
|
+
built_preprocessors = None
|
156
161
|
|
157
162
|
# Create new marker collection
|
158
163
|
mc = MarkerCollection(
|
159
164
|
markers=built_markers,
|
160
|
-
|
165
|
+
preprocessors=built_preprocessors,
|
161
166
|
storage=storage_object,
|
162
167
|
)
|
168
|
+
mc.validate(datagrabber_object)
|
169
|
+
|
163
170
|
# Fit elements
|
164
171
|
with datagrabber_object:
|
165
172
|
if elements is not None:
|
166
|
-
for t_element in
|
173
|
+
for t_element in datagrabber_object.filter(
|
174
|
+
elements # type: ignore
|
175
|
+
):
|
167
176
|
mc.fit(datagrabber_object[t_element])
|
168
177
|
else:
|
169
178
|
for t_element in datagrabber_object:
|
@@ -213,7 +222,7 @@ def queue(
|
|
213
222
|
----------
|
214
223
|
config : dict
|
215
224
|
The configuration to be used for queueing the job.
|
216
|
-
kind : {"HTCondor", "
|
225
|
+
kind : {"HTCondor", "GNUParallelLocal"}
|
217
226
|
The kind of job queue system to use.
|
218
227
|
jobname : str, optional
|
219
228
|
The name of the job (default "junifer_job").
|
@@ -228,13 +237,20 @@ def queue(
|
|
228
237
|
Raises
|
229
238
|
------
|
230
239
|
ValueError
|
231
|
-
If
|
240
|
+
If ``kind`` is invalid or
|
241
|
+
if the ``jobdir`` exists and ``overwrite = False``.
|
232
242
|
|
233
243
|
"""
|
244
|
+
valid_kind = ["HTCondor", "GNUParallelLocal"]
|
245
|
+
if kind not in valid_kind:
|
246
|
+
raise_error(
|
247
|
+
f"Invalid value for `kind`: {kind}, "
|
248
|
+
f"must be one of {valid_kind}"
|
249
|
+
)
|
250
|
+
|
234
251
|
# Create a folder within the CWD to store the job files / config
|
235
|
-
|
236
|
-
|
237
|
-
logger.info(f"Creating job in {jobdir.absolute()!s}")
|
252
|
+
jobdir = Path.cwd() / "junifer_jobs" / jobname
|
253
|
+
logger.info(f"Creating job directory at {jobdir.resolve()!s}")
|
238
254
|
if jobdir.exists():
|
239
255
|
if not overwrite:
|
240
256
|
raise_error(
|
@@ -242,15 +258,16 @@ def queue(
|
|
242
258
|
"This error is raised to prevent overwriting job files "
|
243
259
|
"that might be scheduled but not yet executed. "
|
244
260
|
f"Either delete the directory {jobdir.absolute()!s} "
|
245
|
-
"or set overwrite=True
|
261
|
+
"or set `overwrite=True.`"
|
246
262
|
)
|
247
263
|
else:
|
248
264
|
logger.info(
|
249
|
-
f"Deleting existing job directory at {jobdir.
|
265
|
+
f"Deleting existing job directory at {jobdir.resolve()!s}"
|
250
266
|
)
|
251
267
|
shutil.rmtree(jobdir)
|
252
268
|
jobdir.mkdir(exist_ok=True, parents=True)
|
253
269
|
|
270
|
+
# Load modules
|
254
271
|
if "with" in config:
|
255
272
|
to_load = config["with"]
|
256
273
|
# If there is a list of files to load, copy and remove the path
|
@@ -260,15 +277,16 @@ def queue(
|
|
260
277
|
to_load = [to_load]
|
261
278
|
for item in to_load:
|
262
279
|
if item.endswith(".py"):
|
263
|
-
logger.debug(f"Copying {item} to
|
264
|
-
shutil.copy(item, jobdir)
|
280
|
+
logger.debug(f"Copying {item} to ({jobdir.resolve()!s})")
|
281
|
+
shutil.copy(src=item, dst=jobdir)
|
265
282
|
fixed_load.append(Path(item).name)
|
266
283
|
else:
|
267
284
|
fixed_load.append(item)
|
268
285
|
config["with"] = fixed_load
|
269
286
|
|
287
|
+
# Save YAML
|
270
288
|
yaml_config = jobdir / "config.yaml"
|
271
|
-
logger.info(f"Writing YAML config to {yaml_config.
|
289
|
+
logger.info(f"Writing YAML config to {yaml_config.resolve()!s}")
|
272
290
|
yaml.dump(config, stream=yaml_config)
|
273
291
|
|
274
292
|
# Get list of elements
|
@@ -281,415 +299,65 @@ def queue(
|
|
281
299
|
datagrabber = _get_datagrabber(config["datagrabber"])
|
282
300
|
with datagrabber as dg:
|
283
301
|
elements = dg.get_elements()
|
284
|
-
|
285
|
-
# TODO: Fix typing of elements
|
302
|
+
# Listify elements
|
286
303
|
if not isinstance(elements, list):
|
287
|
-
elements = [elements]
|
288
|
-
|
289
|
-
typing.cast(List[Union[str, Tuple]], elements)
|
304
|
+
elements: List[Union[str, Tuple]] = [elements]
|
290
305
|
|
306
|
+
# Check job queueing system
|
307
|
+
adapter = None
|
291
308
|
if kind == "HTCondor":
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
elements=elements,
|
297
|
-
|
298
|
-
**kwargs,
|
309
|
+
adapter = HTCondorAdapter(
|
310
|
+
job_name=jobname,
|
311
|
+
job_dir=jobdir,
|
312
|
+
yaml_config_path=yaml_config,
|
313
|
+
elements=elements,
|
314
|
+
**kwargs, # type: ignore
|
299
315
|
)
|
300
|
-
elif kind == "
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
elements=elements,
|
306
|
-
|
307
|
-
**kwargs,
|
316
|
+
elif kind == "GNUParallelLocal":
|
317
|
+
adapter = GnuParallelLocalAdapter(
|
318
|
+
job_name=jobname,
|
319
|
+
job_dir=jobdir,
|
320
|
+
yaml_config_path=yaml_config,
|
321
|
+
elements=elements,
|
322
|
+
**kwargs, # type: ignore
|
308
323
|
)
|
309
|
-
else:
|
310
|
-
raise_error(f"Unknown queue kind: {kind}")
|
311
324
|
|
325
|
+
adapter.prepare() # type: ignore
|
312
326
|
logger.info("Queue done")
|
313
327
|
|
314
328
|
|
315
|
-
def
|
316
|
-
|
317
|
-
jobdir: Path,
|
318
|
-
yaml_config: Path,
|
319
|
-
elements: List[Union[str, Tuple]],
|
320
|
-
config: Dict,
|
321
|
-
env: Optional[Dict[str, str]] = None,
|
322
|
-
mem: str = "8G",
|
323
|
-
cpus: int = 1,
|
324
|
-
disk: str = "1G",
|
325
|
-
extra_preamble: str = "",
|
326
|
-
pre_run: Optional[str] = None,
|
327
|
-
verbose: str = "info",
|
328
|
-
collect: str = "yes",
|
329
|
-
submit: bool = False,
|
330
|
-
) -> None:
|
331
|
-
"""Submit job to HTCondor.
|
329
|
+
def reset(config: Dict) -> None:
|
330
|
+
"""Reset the storage and jobs directory.
|
332
331
|
|
333
332
|
Parameters
|
334
333
|
----------
|
335
|
-
jobname : str
|
336
|
-
The name of the job.
|
337
|
-
jobdir : pathlib.Path
|
338
|
-
The path to the job directory.
|
339
|
-
yaml_config : pathlib.Path
|
340
|
-
The path to the YAML config file.
|
341
|
-
elements : list of str or tuple
|
342
|
-
Element(s) to process. Will be used to index the DataGrabber.
|
343
334
|
config : dict
|
344
|
-
The configuration to be used for
|
345
|
-
env : dict, optional
|
346
|
-
The environment variables passed as dictionary (default None).
|
347
|
-
mem : str, optional
|
348
|
-
The size of memory (RAM) to use (default "8G").
|
349
|
-
cpus : int, optional
|
350
|
-
The number of CPU cores to use (default 1).
|
351
|
-
disk : str, optional
|
352
|
-
The size of disk (HDD or SSD) to use (default "1G").
|
353
|
-
extra_preamble : str, optional
|
354
|
-
Extra commands to pass to HTCondor (default "").
|
355
|
-
pre_run : str, optional
|
356
|
-
Extra bash commands to source before the job (default None).
|
357
|
-
verbose : str, optional
|
358
|
-
The level of verbosity (default "info").
|
359
|
-
collect : str, optional
|
360
|
-
Whether to submit "collect" task for junifer (default "yes").
|
361
|
-
Valid options are:
|
362
|
-
|
363
|
-
* "yes": Submit "collect" task and run even if some of the jobs
|
364
|
-
fail.
|
365
|
-
* "on_success_only": Submit "collect" task and run only if all jobs
|
366
|
-
succeed.
|
367
|
-
* "no": Do not submit "collect" task.
|
368
|
-
|
369
|
-
submit : bool, optional
|
370
|
-
Whether to submit the jobs. In any case, .dag files will be created
|
371
|
-
for submission (default False).
|
372
|
-
|
373
|
-
Raises
|
374
|
-
------
|
375
|
-
ValueError
|
376
|
-
If the value of `env` is invalid.
|
335
|
+
The configuration to be used for resetting.
|
377
336
|
|
378
337
|
"""
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
if
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
env = {"kind": "local"}
|
399
|
-
if env["kind"] == "conda":
|
400
|
-
env_name = env["name"]
|
401
|
-
executable = "run_conda.sh"
|
402
|
-
arguments = f"{env_name} junifer"
|
403
|
-
exec_path = jobdir / executable
|
404
|
-
logger.info(f"Copying {executable} to {exec_path.absolute()!s}")
|
405
|
-
shutil.copy(Path(__file__).parent / "res" / executable, exec_path)
|
406
|
-
make_executable(exec_path)
|
407
|
-
elif env["kind"] == "venv":
|
408
|
-
env_name = env["name"]
|
409
|
-
executable = "run_venv.sh"
|
410
|
-
arguments = f"{env_name} junifer"
|
411
|
-
# TODO: Copy run_venv.sh to jobdir
|
412
|
-
elif env["kind"] == "local":
|
413
|
-
executable = "junifer"
|
414
|
-
arguments = ""
|
415
|
-
else:
|
416
|
-
raise ValueError(f'Unknown env kind: {env["kind"]}')
|
417
|
-
|
418
|
-
logger.info("Writing pre_run.sh to jobdir")
|
419
|
-
pre_run_fname = jobdir / "pre_run.sh"
|
420
|
-
with open(pre_run_fname, "w") as f:
|
421
|
-
f.write("#!/bin/bash\n\n")
|
422
|
-
f.write("# Force datalad to run in non-interactive mode\n")
|
423
|
-
f.write("DATALAD_UI_INTERACTIVE=false\n\n")
|
424
|
-
if pre_run is not None:
|
425
|
-
f.write(pre_run)
|
426
|
-
make_executable(pre_run_fname)
|
427
|
-
|
428
|
-
# Create log directory
|
429
|
-
log_dir = jobdir / "logs"
|
430
|
-
log_dir.mkdir(exist_ok=True, parents=True)
|
431
|
-
|
432
|
-
# Add preamble data
|
433
|
-
run_preamble = f"""
|
434
|
-
# The environment
|
435
|
-
universe = vanilla
|
436
|
-
getenv = True
|
437
|
-
|
438
|
-
# Resources
|
439
|
-
request_cpus = {cpus}
|
440
|
-
request_memory = {mem}
|
441
|
-
request_disk = {disk}
|
442
|
-
|
443
|
-
# Executable
|
444
|
-
initial_dir = {jobdir.absolute()!s}
|
445
|
-
executable = $(initial_dir)/{executable}
|
446
|
-
transfer_executable = False
|
447
|
-
|
448
|
-
arguments = {arguments} {run_junifer_args}
|
449
|
-
|
450
|
-
{extra_preamble}
|
451
|
-
|
452
|
-
# Logs
|
453
|
-
log = {log_dir.absolute()!s}/junifer_run_$(log_element).log
|
454
|
-
output = {log_dir.absolute()!s}/junifer_run_$(log_element).out
|
455
|
-
error = {log_dir.absolute()!s}/junifer_run_$(log_element).err
|
456
|
-
"""
|
457
|
-
|
458
|
-
submit_run_fname = jobdir / f"run_{jobname}.submit"
|
459
|
-
submit_collect_fname = jobdir / f"collect_{jobname}.submit"
|
460
|
-
dag_fname = jobdir / f"{jobname}.dag"
|
461
|
-
|
462
|
-
# Write to run submit files
|
463
|
-
with open(submit_run_fname, "w") as submit_file:
|
464
|
-
submit_file.write(textwrap.dedent(run_preamble))
|
465
|
-
submit_file.write("queue\n")
|
466
|
-
|
467
|
-
collect_preamble = f"""
|
468
|
-
# The environment
|
469
|
-
universe = vanilla
|
470
|
-
getenv = True
|
471
|
-
|
472
|
-
# Resources
|
473
|
-
request_cpus = {cpus}
|
474
|
-
request_memory = {mem}
|
475
|
-
request_disk = {disk}
|
476
|
-
|
477
|
-
# Executable
|
478
|
-
initial_dir = {jobdir.absolute()!s}
|
479
|
-
executable = $(initial_dir)/{executable}
|
480
|
-
transfer_executable = False
|
481
|
-
|
482
|
-
arguments = {arguments} {collect_junifer_args}
|
483
|
-
|
484
|
-
{extra_preamble}
|
485
|
-
|
486
|
-
# Logs
|
487
|
-
log = {log_dir.absolute()!s}/junifer_collect.log
|
488
|
-
output = {log_dir.absolute()!s}/junifer_collect.out
|
489
|
-
error = {log_dir.absolute()!s}/junifer_collect.err
|
490
|
-
"""
|
491
|
-
|
492
|
-
# Now create the collect submit file
|
493
|
-
with open(submit_collect_fname, "w") as submit_file:
|
494
|
-
submit_file.write(textwrap.dedent(collect_preamble))
|
495
|
-
submit_file.write("queue\n")
|
496
|
-
|
497
|
-
with open(dag_fname, "w") as dag_file:
|
498
|
-
# Get all subject and session names from file list
|
499
|
-
for i_job, t_elem in enumerate(elements):
|
500
|
-
str_elem = (
|
501
|
-
",".join(t_elem) if isinstance(t_elem, tuple) else t_elem
|
502
|
-
)
|
503
|
-
log_elem = (
|
504
|
-
"_".join(t_elem) if isinstance(t_elem, tuple) else t_elem
|
505
|
-
)
|
506
|
-
dag_file.write(f"JOB run{i_job} {submit_run_fname}\n")
|
507
|
-
dag_file.write(
|
508
|
-
f'VARS run{i_job} element="{str_elem}" '
|
509
|
-
f'log_element="{log_elem}"\n\n'
|
510
|
-
)
|
511
|
-
if collect == "yes":
|
512
|
-
dag_file.write(f"FINAL collect {submit_collect_fname}\n")
|
513
|
-
collect_pre_fname = jobdir / "collect_pre.sh"
|
514
|
-
dag_file.write(
|
515
|
-
f"SCRIPT PRE collect {collect_pre_fname.as_posix()} "
|
516
|
-
"$DAG_STATUS\n"
|
517
|
-
)
|
518
|
-
with open(collect_pre_fname, "w") as pre_file:
|
519
|
-
pre_file.write("#!/bin/bash\n\n")
|
520
|
-
pre_file.write('if [ "${1}" == "4" ]; then\n')
|
521
|
-
pre_file.write(" exit 1\n")
|
522
|
-
pre_file.write("fi\n")
|
523
|
-
|
524
|
-
make_executable(collect_pre_fname)
|
525
|
-
elif collect == "on_success_only":
|
526
|
-
dag_file.write(f"JOB collect {submit_collect_fname}\n")
|
527
|
-
dag_file.write("PARENT ")
|
528
|
-
for i_job, _ in enumerate(elements):
|
529
|
-
dag_file.write(f"run{i_job} ")
|
530
|
-
dag_file.write("CHILD collect\n\n")
|
531
|
-
|
532
|
-
# Submit job(s)
|
533
|
-
if submit is True:
|
534
|
-
logger.info("Submitting HTCondor job")
|
535
|
-
subprocess.run(["condor_submit_dag", dag_fname])
|
536
|
-
logger.info("HTCondor job submitted")
|
537
|
-
else:
|
538
|
-
cmd = f"condor_submit_dag {dag_fname.absolute()!s}"
|
539
|
-
logger.info(
|
540
|
-
f"HTCondor job files created, to submit the job, run `{cmd}`"
|
338
|
+
# Fetch storage
|
339
|
+
storage = config["storage"]
|
340
|
+
storage_uri = Path(storage["uri"])
|
341
|
+
logger.info(f"Deleting {storage_uri.resolve()!s}")
|
342
|
+
# Delete storage; will be str
|
343
|
+
if storage_uri.exists():
|
344
|
+
# Delete files in the directory
|
345
|
+
for file in storage_uri.iterdir():
|
346
|
+
file.unlink(missing_ok=True)
|
347
|
+
# Remove directory
|
348
|
+
storage_uri.parent.rmdir()
|
349
|
+
|
350
|
+
# Fetch job name (if present)
|
351
|
+
if config.get("queue") is not None:
|
352
|
+
queue = config["queue"]
|
353
|
+
job_dir = (
|
354
|
+
Path.cwd()
|
355
|
+
/ "junifer_jobs"
|
356
|
+
/ (queue.get("jobname") or "junifer_job")
|
541
357
|
)
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
elements: List[Union[str, Tuple]],
|
549
|
-
config: Dict,
|
550
|
-
) -> None:
|
551
|
-
"""Submit job to SLURM.
|
552
|
-
|
553
|
-
Parameters
|
554
|
-
----------
|
555
|
-
jobname : str
|
556
|
-
The name of the job.
|
557
|
-
jobdir : pathlib.Path
|
558
|
-
The path to the job directory.
|
559
|
-
yaml_config : pathlib.Path
|
560
|
-
The path to the YAML config file.
|
561
|
-
elements : str or tuple or list[str or tuple], optional
|
562
|
-
Element(s) to process. Will be used to index the DataGrabber
|
563
|
-
(default None).
|
564
|
-
config : dict
|
565
|
-
The configuration to be used for queueing the job.
|
566
|
-
"""
|
567
|
-
pass
|
568
|
-
# logger.debug("Creating SLURM job")
|
569
|
-
# run_junifer_args = (
|
570
|
-
# f"run {str(yaml_config.absolute())} "
|
571
|
-
# f"--verbose {verbose} --element $(element)"
|
572
|
-
# )
|
573
|
-
# collect_junifer_args = \
|
574
|
-
# f"collect {str(yaml_config.absolute())} --verbose {verbose} "
|
575
|
-
|
576
|
-
# # Set up the env_name, executable and arguments according to the
|
577
|
-
# # environment type
|
578
|
-
# if env is None:
|
579
|
-
# env = {
|
580
|
-
# "kind": "local",
|
581
|
-
# }
|
582
|
-
# if env["kind"] == "conda":
|
583
|
-
# env_name = env["name"]
|
584
|
-
# executable = "run_conda.sh"
|
585
|
-
# arguments = f"{env_name} junifer"
|
586
|
-
# # TODO: Copy run_conda.sh to jobdir
|
587
|
-
# exec_path = jobdir / executable
|
588
|
-
# shutil.copy(Path(__file__).parent / "res" / executable, exec_path)
|
589
|
-
# make_executable(exec_path)
|
590
|
-
# elif env["kind"] == "venv":
|
591
|
-
# env_name = env["name"]
|
592
|
-
# executable = "run_venv.sh"
|
593
|
-
# arguments = f"{env_name} junifer"
|
594
|
-
# # TODO: Copy run_venv.sh to jobdir
|
595
|
-
# elif env["kind"] == "local":
|
596
|
-
# executable = "junifer"
|
597
|
-
# arguments = ""
|
598
|
-
# else:
|
599
|
-
# raise ValueError(f"Unknown env kind: {env['kind']}")
|
600
|
-
|
601
|
-
# # Create log directory
|
602
|
-
# log_dir = jobdir / 'logs'
|
603
|
-
# log_dir.mkdir(exist_ok=True, parents=True)
|
604
|
-
|
605
|
-
# # Add preamble data
|
606
|
-
# run_preamble = f"""
|
607
|
-
# #!/bin/bash
|
608
|
-
|
609
|
-
# #SBATCH --job-name={}
|
610
|
-
# #SBATCH --account={}
|
611
|
-
# #SBATCH --partition={}
|
612
|
-
# #SBATCH --time={}
|
613
|
-
# #SBATCH --ntasks={}
|
614
|
-
# #SBATCH --cpus-per-task={cpus}
|
615
|
-
# #SBATCH --mem-per-cpu={mem}
|
616
|
-
# #SBATCH --mail-type={}
|
617
|
-
# #SBATCH --mail-user={}
|
618
|
-
# #SBATCH --output={}
|
619
|
-
# #SBATCH --error={}
|
620
|
-
|
621
|
-
# # Executable
|
622
|
-
# initial_dir = {str(jobdir.absolute())}
|
623
|
-
# executable = $(initial_dir)/{executable}
|
624
|
-
# transfer_executable = False
|
625
|
-
|
626
|
-
# arguments = {arguments} {run_junifer_args}
|
627
|
-
|
628
|
-
# {extra_preamble}
|
629
|
-
|
630
|
-
# # Logs
|
631
|
-
# log = {str(log_dir.absolute())}/junifer_run_$(element).log
|
632
|
-
# output = {str(log_dir.absolute())}/junifer_run_$(element).out
|
633
|
-
# error = {str(log_dir.absolute())}/junifer_run_$(element).err
|
634
|
-
# """
|
635
|
-
|
636
|
-
# submit_run_fname = jobdir / f'run_{jobname}.sh'
|
637
|
-
# submit_collect_fname = jobdir / f'collect_{jobname}.sh'
|
638
|
-
|
639
|
-
# # Write to run submit files
|
640
|
-
# with open(submit_run_fname, 'w') as submit_file:
|
641
|
-
# submit_file.write(run_preamble)
|
642
|
-
# submit_file.write('queue\n')
|
643
|
-
|
644
|
-
# collect_preamble = f"""
|
645
|
-
# # The environment
|
646
|
-
# universe = vanilla
|
647
|
-
# getenv = True
|
648
|
-
|
649
|
-
# # Resources
|
650
|
-
# request_cpus = {cpus}
|
651
|
-
# request_memory = {mem}
|
652
|
-
# request_disk = {disk}
|
653
|
-
|
654
|
-
# # Executable
|
655
|
-
# initial_dir = {str(jobdir.absolute())}
|
656
|
-
# executable = $(initial_dir)/{executable}
|
657
|
-
# transfer_executable = False
|
658
|
-
|
659
|
-
# arguments = {arguments} {collect_junifer_args}
|
660
|
-
|
661
|
-
# {extra_preamble}
|
662
|
-
|
663
|
-
# # Logs
|
664
|
-
# log = {str(log_dir.absolute())}/junifer_collect.log
|
665
|
-
# output = {str(log_dir.absolute())}/junifer_collect.out
|
666
|
-
# error = {str(log_dir.absolute())}/junifer_collect.err
|
667
|
-
# """
|
668
|
-
|
669
|
-
# # Now create the collect submit file
|
670
|
-
# with open(submit_collect_fname, 'w') as submit_file:
|
671
|
-
# submit_file.write(collect_preamble) # Eval preamble here
|
672
|
-
# submit_file.write('queue\n')
|
673
|
-
|
674
|
-
# with open(dag_fname, 'w') as dag_file:
|
675
|
-
# # Get all subject and session names from file list
|
676
|
-
# for i_job, t_elem in enumerate(elements):
|
677
|
-
# dag_file.write(f'JOB run{i_job} {submit_run_fname}\n')
|
678
|
-
# dag_file.write(f'VARS run{i_job} element="{t_elem}"\n\n')
|
679
|
-
# if collect is True:
|
680
|
-
# dag_file.write(f'JOB collect {submit_collect_fname}\n')
|
681
|
-
# dag_file.write('PARENT ')
|
682
|
-
# for i_job, _t_elem in enumerate(elements):
|
683
|
-
# dag_file.write(f'run{i_job} ')
|
684
|
-
# dag_file.write('CHILD collect\n\n')
|
685
|
-
|
686
|
-
# # Submit job(s)
|
687
|
-
# if submit is True:
|
688
|
-
# logger.info('Submitting SLURM job')
|
689
|
-
# subprocess.run(['condor_submit_dag', dag_fname])
|
690
|
-
# logger.info('HTCondor SLURM submitted')
|
691
|
-
# else:
|
692
|
-
# cmd = f"condor_submit_dag {str(dag_fname.absolute())}"
|
693
|
-
# logger.info(
|
694
|
-
# f"SLURM job files created, to submit the job, run `{cmd}`"
|
695
|
-
# )
|
358
|
+
logger.info(f"Deleting job directory at {job_dir.resolve()!s}")
|
359
|
+
if job_dir.exists():
|
360
|
+
# Remove files and directories
|
361
|
+
shutil.rmtree(job_dir)
|
362
|
+
# Remove directory
|
363
|
+
job_dir.parent.rmdir()
|
junifer/api/parser.py
CHANGED
@@ -50,6 +50,8 @@ def parse_yaml(filepath: Union[str, Path]) -> Dict:
|
|
50
50
|
# Convert load modules to list
|
51
51
|
if not isinstance(to_load, list):
|
52
52
|
to_load = [to_load]
|
53
|
+
# Initialize list to have absolute paths for custom modules
|
54
|
+
final_to_load = []
|
53
55
|
for t_module in to_load:
|
54
56
|
if t_module.endswith(".py"):
|
55
57
|
logger.debug(f"Importing file: {t_module}")
|
@@ -65,9 +67,18 @@ def parse_yaml(filepath: Union[str, Path]) -> Dict:
|
|
65
67
|
module = importlib.util.module_from_spec(spec) # type: ignore
|
66
68
|
sys.modules[t_module] = module
|
67
69
|
spec.loader.exec_module(module) # type: ignore
|
70
|
+
# Add absolute path to final list
|
71
|
+
final_to_load.append(str(file_path.resolve()))
|
68
72
|
else:
|
69
73
|
logger.info(f"Importing module: {t_module}")
|
70
74
|
importlib.import_module(t_module)
|
75
|
+
# Add module to final list
|
76
|
+
final_to_load.append(t_module)
|
77
|
+
|
78
|
+
# Replace modules to be loaded so that custom modules will take the
|
79
|
+
# absolute path. This was not the case as found in #224. Similar thing
|
80
|
+
# is done with the storage URI below.
|
81
|
+
contents["with"] = final_to_load
|
71
82
|
|
72
83
|
# Compute path for the URI parameter in storage files that are relative
|
73
84
|
# This is a tricky thing that appeared in #127. The problem is that
|
@@ -88,4 +99,17 @@ def parse_yaml(filepath: Union[str, Path]) -> Dict:
|
|
88
99
|
contents["storage"]["uri"] = str(
|
89
100
|
(filepath.parent / uri_path).resolve()
|
90
101
|
)
|
102
|
+
|
103
|
+
# Allow relative path if queue env kind is venv; same motivation as above
|
104
|
+
if "queue" in contents:
|
105
|
+
if "env" in contents["queue"]:
|
106
|
+
if "venv" == contents["queue"]["env"]["kind"]:
|
107
|
+
# Check if the env name is relative
|
108
|
+
venv_path = Path(contents["queue"]["env"]["name"])
|
109
|
+
if not venv_path.is_absolute():
|
110
|
+
# Compute the absolute path
|
111
|
+
contents["queue"]["env"]["name"] = str(
|
112
|
+
(filepath.parent / venv_path).resolve()
|
113
|
+
)
|
114
|
+
|
91
115
|
return contents
|
@@ -0,0 +1,8 @@
|
|
1
|
+
"""Provide imports for queue context sub-package."""
|
2
|
+
|
3
|
+
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
|
4
|
+
# License: AGPL
|
5
|
+
|
6
|
+
from .queue_context_adapter import QueueContextAdapter
|
7
|
+
from .htcondor_adapter import HTCondorAdapter
|
8
|
+
from .gnu_parallel_local_adapter import GnuParallelLocalAdapter
|