diff --git a/PUMI/engine.py b/PUMI/engine.py index ccb1604..7f38b28 100644 --- a/PUMI/engine.py +++ b/PUMI/engine.py @@ -14,6 +14,8 @@ import ast from PUMI import globals import subprocess +import json + def _parameterization_dir(param): """ @@ -685,15 +687,27 @@ def get_interface_version(interface): return tool_name, 'Unknown' -def save_software_versions(wf): +def create_dataset_description(wf, + pipeline_description_name, + dataset_description_name='Derivatives created by PUMI', + bids_version='1.9.0'): """ - Save PUMI version as well as software-version of FSL and other 'external' software that is used in a pipeline - in a textfile. + Create and save dataset description JSON for the derivatives created by PUMI that includes details about the + software versions used in the pipeline. + + Parameters: + wf (Workflow object): The workflow object. + pipeline_description_name (str): Name of the used pipeline (e.g., 'RCPL-Pipeline'). + dataset_description_name (str, optional): Name for the dataset. Default is 'Derivatives created by PUMI'. + bids_version (str, optional): The BIDS version used. Default is '1.9.0'. + + Returns: + None. A JSON file is created in the workflow's specified sink directory. """ - versions = {'PUMI': get_versions()['version']} + software_versions = {} for node_name in wf.list_node_names(): node = wf.get_node(node_name) @@ -703,13 +717,20 @@ def save_software_versions(wf): continue # We can skip the external-tool-independent nipype in-house interfaces else: interface_name, version = result - versions[interface_name] = version - - path = Path(wf.sink_dir) / "software_versions.txt" - path.parent.mkdir(parents=True, exist_ok=True) - with open(str(path), "w") as f_obj: - for key, value in versions.items(): - f_obj.write('%s: %s\n' % (key, value)) - f_obj.write('\n---------------------------------------------\n') - f_obj.write('| CAUTION: This list might not be complete! |\n') - f_obj.write('---------------------------------------------\n') + software_versions[interface_name] = version + + dataset_description_path = Path(wf.sink_dir) / 'dataset_description.json' + dataset_description_path.parent.mkdir(parents=True, exist_ok=True) + + dataset_description = { + 'Name': dataset_description_name, + 'BIDSVersion': bids_version, + 'PipelineDescription': { + 'Name': pipeline_description_name, + 'Version': get_versions()['version'], + 'Software': [{'Name': name, 'Version': version} for name, version in software_versions.items()] + } + } + + with open(dataset_description_path, 'w') as outfile: + json.dump(dataset_description, outfile, indent=4) diff --git a/pipelines/rcpl.py b/pipelines/rcpl.py index 7812a73..c96cfcb 100755 --- a/pipelines/rcpl.py +++ b/pipelines/rcpl.py @@ -2,7 +2,8 @@ from nipype.interfaces.fsl import Reorient2Std from nipype.interfaces import afni -from PUMI.engine import BidsPipeline, NestedNode as Node, FuncPipeline, GroupPipeline, BidsApp +from PUMI.engine import BidsPipeline, NestedNode as Node, FuncPipeline, GroupPipeline, BidsApp, \ + create_dataset_description from PUMI.pipelines.anat.anat_proc import anat_proc from PUMI.pipelines.func.compcor import anat_noise_roi, compcor from PUMI.pipelines.anat.func_to_anat import func2anat @@ -12,7 +13,6 @@ from PUMI.utils import mist_modules, mist_labels, get_reference from PUMI.pipelines.func.func2standard import func2standard from PUMI.pipelines.multimodal.image_manipulation import pick_volume -from PUMI.engine import save_software_versions import traits import os @@ -446,7 +446,7 @@ def rcpl(wf, bbr=True, **kwargs): wf.connect(predict_pain_sensitivity_rcpl_wf, 'out_file', collect_pain_predictions_wf, 'rcpl_out_file') wf.write_graph('RCPL-pipeline.png') - save_software_versions(wf) + create_dataset_description(wf, pipeline_description_name='RCPL-pipeline') rcpl_app = BidsApp( diff --git a/pipelines/rpn_signature.py b/pipelines/rpn_signature.py index 36c535c..4152965 100755 --- a/pipelines/rpn_signature.py +++ b/pipelines/rpn_signature.py @@ -2,7 +2,8 @@ from nipype.interfaces.fsl import Reorient2Std from nipype.interfaces import afni -from PUMI.engine import BidsPipeline, NestedNode as Node, FuncPipeline, GroupPipeline, BidsApp, save_software_versions +from PUMI.engine import BidsPipeline, NestedNode as Node, FuncPipeline, GroupPipeline, BidsApp +from PUMI.engine import create_dataset_description from PUMI.pipelines.anat.anat_proc import anat_proc from PUMI.pipelines.func.compcor import anat_noise_roi, compcor from PUMI.pipelines.anat.func_to_anat import func2anat @@ -319,7 +320,7 @@ def rpn(wf, bbr=True, **kwargs): wf.connect('inputspec', 'bold', predict_pain_sensitivity_wf, 'in_files') wf.write_graph('rpn-signature.png') - save_software_versions(wf) + create_dataset_description(wf, pipeline_description_name='RPN-pipeline') rpn_app = BidsApp(