From 0b111ad5c59e964c657da47211f84f59a9bf76f7 Mon Sep 17 00:00:00 2001 From: Pierre Nedelec <65797646+pierre-nedelec@users.noreply.github.com> Date: Wed, 26 Oct 2022 17:02:20 -0700 Subject: [PATCH 1/3] set up file logger with timestamp --- AFQ/api/bundle_dict.py | 2 +- AFQ/api/participant.py | 2 +- AFQ/data/fetch.py | 12 ++++---- AFQ/definitions/mapping.py | 2 +- AFQ/segmentation.py | 2 +- AFQ/tasks/decorators.py | 3 +- AFQ/tasks/mapping.py | 2 +- AFQ/tasks/segmentation.py | 2 +- AFQ/tasks/tractography.py | 2 +- AFQ/tasks/viz.py | 2 +- AFQ/tractography.py | 2 +- AFQ/utils/bin.py | 3 ++ AFQ/utils/volume.py | 2 +- AFQ/viz/fury_backend.py | 2 +- AFQ/viz/plot.py | 2 +- AFQ/viz/plotly_backend.py | 2 +- AFQ/viz/utils.py | 2 +- bin/pyAFQ | 41 ++++++++++++++++--------- examples/plot_callosal_tract_profile.py | 2 +- 19 files changed, 52 insertions(+), 37 deletions(-) diff --git a/AFQ/api/bundle_dict.py b/AFQ/api/bundle_dict.py index 6fa72d404..bcc9a9788 100644 --- a/AFQ/api/bundle_dict.py +++ b/AFQ/api/bundle_dict.py @@ -168,7 +168,7 @@ def __init__(self, for bundle_name in bundle_info: self.bundle_names.append(bundle_name) - self.logger = logging.getLogger('AFQ') + self.logger = logging.getLogger(__name__) if self.seg_algo == "afq": if "FP" in self.bundle_names\ diff --git a/AFQ/api/participant.py b/AFQ/api/participant.py index 2ac788343..8ff885366 100644 --- a/AFQ/api/participant.py +++ b/AFQ/api/participant.py @@ -88,7 +88,7 @@ def __init__(self, "unrecognized parameter tractography_params, " "did you mean tracking_params ?")) - self.logger = logging.getLogger('AFQ') + self.logger = logging.getLogger(__name__) self.output_dir = output_dir self.kwargs = dict( diff --git a/AFQ/data/fetch.py b/AFQ/data/fetch.py index d9fef8047..567897b72 100644 --- a/AFQ/data/fetch.py +++ b/AFQ/data/fetch.py @@ -163,7 +163,7 @@ def read_callosum_templates(as_img=True, resample_to=False): dict with: keys: names of template ROIs and values: nibabel Nifti1Image objects from each of the ROI nifti files. """ - logger = logging.getLogger('AFQ') + logger = logging.getLogger(__name__) logger.debug('loading callosum templates') tic = time.perf_counter() @@ -555,7 +555,7 @@ def read_templates(as_img=True, resample_to=False): dict with: keys: names of template ROIs and values: nibabel Nifti1Image objects from each of the ROI nifti files. """ - logger = logging.getLogger('AFQ') + logger = logging.getLogger(__name__) logger.debug('loading AFQ templates') tic = time.perf_counter() @@ -649,7 +649,7 @@ def read_or_templates(as_img=True, resample_to=False): dict with: keys: names of template ROIs and values: nibabel Nifti1Image objects from each of the ROI nifti files. """ - logger = logging.getLogger('AFQ') + logger = logging.getLogger(__name__) logger.debug('loading or templates') tic = time.perf_counter() @@ -795,7 +795,7 @@ def organize_stanford_data(path=None, clear_previous_afq=False): If clear_previous_afq is True and there is an afq folder in derivatives, it will be removed. """ - logger = logging.getLogger('AFQ') + logger = logging.getLogger(__name__) # fetches data for first subject and session logger.info('fetching Stanford HARDI data') @@ -1196,7 +1196,7 @@ def bundles_to_aal(bundles, atlas=None): targets[bundle + "_" + region_name] = nib.Nifti1Image( aal_roi, atlas.affine) else: - logger = logging.getLogger('AFQ') + logger = logging.getLogger(__name__) logger.warning(f"Segmentation end points undefined for {bundle}," + " continuing without end points") targets[bundle + "_start"] = None @@ -1317,7 +1317,7 @@ def read_ukbb_fa_template(mask=True): ) if not op.exists(fa_path): - logger = logging.getLogger('AFQ') + logger = logging.getLogger(__name__) logger.warning( "Downloading brain MRI group mean statistics from UK Biobank. " + "This download is approximately 1.1 GB. " diff --git a/AFQ/definitions/mapping.py b/AFQ/definitions/mapping.py index 2d2a15a1c..8d5623801 100644 --- a/AFQ/definitions/mapping.py +++ b/AFQ/definitions/mapping.py @@ -29,7 +29,7 @@ __all__ = ["FnirtMap", "SynMap", "SlrMap", "AffMap", "ItkMap"] -logger = logging.getLogger('AFQ') +logger = logging.getLogger(__name__) # For map defintions, get_for_subses should return only the mapping diff --git a/AFQ/segmentation.py b/AFQ/segmentation.py index 7f852ebb2..5d945bbb5 100644 --- a/AFQ/segmentation.py +++ b/AFQ/segmentation.py @@ -28,7 +28,7 @@ __all__ = ["Segmentation", "clean_bundle", "clean_by_endpoints"] -logger = logging.getLogger('AFQ') +logger = logging.getLogger(__name__) def _resample_tg(tg, n_points): diff --git a/AFQ/tasks/decorators.py b/AFQ/tasks/decorators.py index 40a395af2..8eedcc322 100644 --- a/AFQ/tasks/decorators.py +++ b/AFQ/tasks/decorators.py @@ -19,8 +19,7 @@ __all__ = ["as_file", "as_fit_deriv", "as_img"] -logger = logging.getLogger('AFQ') -logger.setLevel(logging.INFO) +logger = logging.getLogger(__name__) # get args and kwargs from function diff --git a/AFQ/tasks/mapping.py b/AFQ/tasks/mapping.py index 95b75a64f..ff0133ff3 100644 --- a/AFQ/tasks/mapping.py +++ b/AFQ/tasks/mapping.py @@ -19,7 +19,7 @@ from dipy.io.stateful_tractogram import Space -logger = logging.getLogger('AFQ') +logger = logging.getLogger(__name__) @pimms.calc("b0_warped") diff --git a/AFQ/tasks/segmentation.py b/AFQ/tasks/segmentation.py index e5b48ecad..8e14347e3 100644 --- a/AFQ/tasks/segmentation.py +++ b/AFQ/tasks/segmentation.py @@ -24,7 +24,7 @@ from dipy.tracking.streamline import set_number_of_points, values_from_volume -logger = logging.getLogger('AFQ') +logger = logging.getLogger(__name__) @pimms.calc("bundles") diff --git a/AFQ/tasks/tractography.py b/AFQ/tasks/tractography.py index 25ddd9478..af8dffff3 100644 --- a/AFQ/tasks/tractography.py +++ b/AFQ/tasks/tractography.py @@ -11,7 +11,7 @@ from AFQ.tasks.utils import get_default_args from AFQ.definitions.image import ScalarImage -logger = logging.getLogger('AFQ') +logger = logging.getLogger(__name__) @pimms.calc("seed") diff --git a/AFQ/tasks/viz.py b/AFQ/tasks/viz.py index 5a21d6469..cf71b77d7 100644 --- a/AFQ/tasks/viz.py +++ b/AFQ/tasks/viz.py @@ -17,7 +17,7 @@ from plotly.subplots import make_subplots -logger = logging.getLogger('AFQ') +logger = logging.getLogger(__name__) def _viz_prepare_vol(vol, xform, mapping, scalar_dict): diff --git a/AFQ/tractography.py b/AFQ/tractography.py index 9cc2fca2a..edf30af56 100644 --- a/AFQ/tractography.py +++ b/AFQ/tractography.py @@ -99,7 +99,7 @@ def track(params_file, directions="prob", max_angle=30., sphere=None, Descoteaux, M. Towards quantitative connectivity analysis: reducing tractography biases. NeuroImage, 98, 266-278, 2014. """ - logger = logging.getLogger('AFQ') + logger = logging.getLogger(__name__) logger.info("Loading Image...") if isinstance(params_file, str): diff --git a/AFQ/utils/bin.py b/AFQ/utils/bin.py index 33273c5c5..c9b63e0b2 100644 --- a/AFQ/utils/bin.py +++ b/AFQ/utils/bin.py @@ -3,6 +3,8 @@ import platform import os.path as op import os +import logging +logger = logging.getLogger(__name__) from argparse import ArgumentParser from funcargparse import FuncArgParser @@ -298,6 +300,7 @@ def parse_config_run_afq(toml_file, default_arg_dict, to_call="export_all", with open(afq_metadata_file, 'w') as ff: ff.write(dict_to_toml(default_arg_dict)) + logger.info("Starting to create the GroupAFQ object") myafq = GroupAFQ(bids_path, **kwargs) # call user specified function: diff --git a/AFQ/utils/volume.py b/AFQ/utils/volume.py index 95e770da2..5d29ee1d3 100644 --- a/AFQ/utils/volume.py +++ b/AFQ/utils/volume.py @@ -11,7 +11,7 @@ from dipy.tracking.streamline import select_random_set_of_streamlines import dipy.tracking.utils as dtu -logger = logging.getLogger('AFQ') +logger = logging.getLogger(__name__) def transform_inverse_roi(roi, mapping, bundle_name="ROI"): diff --git a/AFQ/viz/fury_backend.py b/AFQ/viz/fury_backend.py index d2e61e6d7..5fbe9b1ef 100644 --- a/AFQ/viz/fury_backend.py +++ b/AFQ/viz/fury_backend.py @@ -15,7 +15,7 @@ except (ImportError, ModuleNotFoundError): raise ImportError(vut.viz_import_msg_error("fury")) -viz_logger = logging.getLogger("AFQ") +viz_logger = logging.getLogger(__name__) def _inline_interact(scene, inline, interact): diff --git a/AFQ/viz/plot.py b/AFQ/viz/plot.py index cb1fbdabe..0c8447abc 100644 --- a/AFQ/viz/plot.py +++ b/AFQ/viz/plot.py @@ -334,7 +334,7 @@ def __init__(self, out_folder, csv_fnames, names, is_special="", Can be 'ICC1, 'ICC2', 'ICC3', 'ICC1k', 'ICC2k', 'ICC3k'. Default: "ICC2" """ - self.logger = logging.getLogger('AFQ') + self.logger = logging.getLogger(__name__) self.ICC_func = ICC_func if "k" in self.ICC_func: self.ICC_func_name = f"ICC({self.ICC_func[3]},k)" diff --git a/AFQ/viz/plotly_backend.py b/AFQ/viz/plotly_backend.py index 84ac63fad..2d0f90da1 100644 --- a/AFQ/viz/plotly_backend.py +++ b/AFQ/viz/plotly_backend.py @@ -21,7 +21,7 @@ scope = pio.kaleido.scope -viz_logger = logging.getLogger("AFQ") +viz_logger = logging.getLogger(__name__) def _inline_interact(figure, show, show_inline): diff --git a/AFQ/viz/utils.py b/AFQ/viz/utils.py index 0907a5c41..139ead293 100644 --- a/AFQ/viz/utils.py +++ b/AFQ/viz/utils.py @@ -18,7 +18,7 @@ __all__ = ["Viz"] -viz_logger = logging.getLogger("AFQ") +viz_logger = logging.getLogger(__name__) tableau_20 = [ (0.12156862745098039, 0.4666666666666667, 0.7058823529411765), (0.6823529411764706, 0.7803921568627451, 0.9098039215686274), diff --git a/bin/pyAFQ b/bin/pyAFQ index b2bf70960..97788e199 100644 --- a/bin/pyAFQ +++ b/bin/pyAFQ @@ -1,21 +1,8 @@ #!/usr/bin/env python - import warnings import os.path as op from argparse import ArgumentParser - - -with warnings.catch_warnings(): - warnings.simplefilter("ignore") - - print("Loading AFQ libraries...") - - import AFQ - import AFQ.utils.bin as afb - - import logging - logger = logging.getLogger('AFQ') - logger.setLevel(level=logging.INFO) +import logging usage = \ """pyAFQ /path/to/afq_config.toml @@ -97,6 +84,10 @@ def parse_cli(arg_dict): cli_parser.add_argument( '-t', '--notrack', action="store_true", default=False, help="Disable the use of pyAFQ being recorded by Google Analytics. ") + + cli_parser.add_argument( + '-l', '--log', default=None, + help="Path to log file to save the outputs of pyAFQ.") opts = cli_parser.parse_args() @@ -109,12 +100,34 @@ def parse_cli(arg_dict): "flag when using the pyAFQ CLI") import popylar popylar.track_event(AFQ._ga_id, "pyAFQ_cli", "CLI called") + + if opts.log is not None: + # Create handler + f_handler = logging.FileHandler(opts.log) + f_handler.setLevel(logging.INFO) + # Create formatter and add it to handler + f_format = logging.Formatter( + '%(asctime)s - %(name)s:%(levelname)s:%(message)s') + f_handler.setFormatter(f_format) + # Add handler to the logger + logger.addHandler(f_handler) + print(f'Logging to file: {opts.log}') + logger.info("Starting pyAFQ") return opts.config, opts.generate_toml, opts.overwrite,\ opts.verbose, opts.dry_run, opts.to_call, opts.generate_json if __name__ == '__main__': + logger = logging.getLogger() + logger.setLevel(level=logging.INFO) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + print("Loading AFQ libraries...") + # import AFQ + import AFQ.utils.bin as afb + arg_dict = afb.func_dict_to_arg_dict(logger=logger) config_file, generate_only, overwrite, verbose,\ dry_run, to_call, generate_json =\ diff --git a/examples/plot_callosal_tract_profile.py b/examples/plot_callosal_tract_profile.py index 3acc634f3..4fa3fbeb3 100644 --- a/examples/plot_callosal_tract_profile.py +++ b/examples/plot_callosal_tract_profile.py @@ -50,7 +50,7 @@ # Ensure segmentation logging information is included in this example's output root = logging.getLogger() root.setLevel(logging.ERROR) -logging.getLogger('AFQ').setLevel(logging.INFO) +logging.getLogger(__name__).setLevel(logging.INFO) handler = logging.StreamHandler(sys.stdout) handler.setLevel(logging.INFO) root.addHandler(handler) From 551b12579651a9dfea5fd6cae3d1331410cac205 Mon Sep 17 00:00:00 2001 From: Pierre Nedelec <65797646+pierre-nedelec@users.noreply.github.com> Date: Tue, 10 Jan 2023 22:19:47 -0800 Subject: [PATCH 2/3] custom output directory (instead of default "afq") --- AFQ/utils/bin.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/AFQ/utils/bin.py b/AFQ/utils/bin.py index c9b63e0b2..532bb880d 100644 --- a/AFQ/utils/bin.py +++ b/AFQ/utils/bin.py @@ -295,8 +295,12 @@ def parse_config_run_afq(toml_file, default_arg_dict, to_call="export_all", afq_path = op.join(bids_path, 'derivatives', 'afq') os.makedirs(afq_path, exist_ok=True) - - afq_metadata_file = op.join(afq_path, 'afq_metadata.toml') + if kwargs['output_dir'] != '': + output_dir = kwargs['output_dir'] + os.makedirs(output_dir, exist_ok=True) + else: + output_dir = afq_path + afq_metadata_file = op.join(output_dir, 'afq_metadata.toml') with open(afq_metadata_file, 'w') as ff: ff.write(dict_to_toml(default_arg_dict)) From 482c9f090b29ba425d782a1826e778f557d398b2 Mon Sep 17 00:00:00 2001 From: Pierre Nedelec <65797646+pierre-nedelec@users.noreply.github.com> Date: Tue, 10 Jan 2023 22:37:48 -0800 Subject: [PATCH 3/3] logging setup from GroupAFQ --- AFQ/api/group.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AFQ/api/group.py b/AFQ/api/group.py index 6ba04a303..b4850d0f1 100644 --- a/AFQ/api/group.py +++ b/AFQ/api/group.py @@ -42,9 +42,7 @@ __all__ = ["GroupAFQ"] - -logger = logging.getLogger('AFQ') -logger.setLevel(logging.INFO) +logger = logging.getLogger(__name__) # get rid of unnecessary columns in df @@ -65,6 +63,7 @@ def __init__(self, output_dir=None, parallel_params={"engine": "serial"}, bids_layout_kwargs={}, + log_level=logging.INFO, **kwargs): ''' Initialize a GroupAFQ object from a BIDS dataset. @@ -117,6 +116,7 @@ def __init__(self, my_path, reg_template_spec="mni_t2", reg_subject_spec="b0") ''' + logger.setLevel(log_level) if not isinstance(bids_path, str): raise TypeError("bids_path must be a string") if not op.exists(bids_path):