From 5f7dc24f815a0c841b5317e07dc483c0d3616546 Mon Sep 17 00:00:00 2001 From: Marcel Zwiers Date: Tue, 17 Sep 2024 15:20:11 +0200 Subject: [PATCH] Refactoring + deepcopy bugfixes --- bidscoin/bcoin.py | 13 +- bidscoin/bids.py | 345 +++++++----- bidscoin/bidsapps/deface.py | 4 +- bidscoin/bidsapps/echocombine.py | 4 +- bidscoin/bidsapps/fixmeta.py | 10 +- bidscoin/bidsapps/medeface.py | 4 +- bidscoin/bidsapps/skullstrip.py | 4 +- bidscoin/bidsapps/slicereport.py | 14 +- bidscoin/bidscoiner.py | 21 +- bidscoin/bidseditor.py | 370 ++++++------- bidscoin/bidsmapper.py | 20 +- bidscoin/plugins/dcm2niix2bids.py | 18 +- bidscoin/plugins/nibabel2bids.py | 40 +- bidscoin/plugins/spec2nii2bids.py | 42 +- bidscoin/utilities/bidsparticipants.py | 20 +- bidscoin/utilities/dicomsort.py | 4 +- bidscoin/utilities/rawmapper.py | 9 +- tests/test_bids.py | 740 +++++++++++++------------ tests/test_bidsmapper.py | 11 +- tests/test_plugins.py | 4 +- 20 files changed, 888 insertions(+), 809 deletions(-) diff --git a/bidscoin/bcoin.py b/bidscoin/bcoin.py index 34614e27..a6b746eb 100755 --- a/bidscoin/bcoin.py +++ b/bidscoin/bcoin.py @@ -498,11 +498,11 @@ def test_bidsmap(bidsmapfile: str): return bidsmap.validate(1) -def test_bidscoin(bidsmapfile: Path, options: dict=None, testplugins: bool=True, testgui: bool=True, testtemplate: bool=True) -> int: +def test_bidscoin(bidsmapfile, options: dict=None, testplugins: bool=True, testgui: bool=True, testtemplate: bool=True) -> int: """ Performs a bidscoin installation test - :param bidsmapfile: The bidsmap or the full pathname/basename of the bidsmap yaml-file + :param bidsmapfile: The full pathname/basename of the bidsmap yaml-file or the bidsmap object itself :param options: The bidscoin options. If empty, the default options are used :return: 0 if the test was successful, otherwise 1 """ @@ -568,8 +568,7 @@ def test_bidscoin(bidsmapfile: Path, options: dict=None, testplugins: bool=True, list_executables(True) # Test the plugins - options = bidsmap['Options'] if not options and bidsmap else {} - if not options.get('plugins'): + if not bidsmap.plugins: LOGGER.warning('No plugins found in the bidsmap (BIDScoin will likely not do anything)') if testplugins: @@ -577,7 +576,7 @@ def test_bidscoin(bidsmapfile: Path, options: dict=None, testplugins: bool=True, list_plugins(True) for plugin in pluginfolder.glob('*.py'): if plugin.stem != '__init__': - errorcode = test_plugin(plugin.stem, options['plugins'].get(plugin.stem,{}) if options else {}) + errorcode = test_plugin(plugin.stem, bidsmap.plugins.get(plugin.stem, {})) success = not errorcode and success if errorcode: LOGGER.warning(f"Failed test: {plugin.stem}") @@ -705,9 +704,9 @@ def main(): settracking(value=args.tracking) reportcredits(args=args.credits) - except Exception: + except Exception as error: trackusage('bidscoin_exception') - raise + raise error if __name__ == "__main__": diff --git a/bidscoin/bids.py b/bidscoin/bids.py index 44e2dca9..f0f4a023 100644 --- a/bidscoin/bids.py +++ b/bidscoin/bids.py @@ -74,7 +74,7 @@ class DataSource: def __init__(self, sourcefile: Union[str, Path]='', plugins: Plugins=None, dataformat: str='', options: Options=None): """ - Reads the properties and attributes from a source data file + Reads (cached) properties and attributes from a source data file :param sourcefile: The full filepath of the data source :param plugins: The plugin dictionaries with their options @@ -144,20 +144,20 @@ def has_plugin(self) -> bool: LOGGER.exception(f"The {plugin} plugin crashed while reading {self.path}\n{moderror}") if supported: if self.dataformat and self.dataformat != supported: - LOGGER.debug(f"Inconsistent dataformat found, updating: {self.dataformat} -> {supported}") + LOGGER.bcdebug(f"Inconsistent dataformat found, updating: {self.dataformat} -> {supported}") self.dataformat: str = supported return True return False - def properties(self, tagname: str, run=None) -> Union[str, int]: + def properties(self, tagname: str, runitem=None) -> Union[str, int]: """ Gets the 'filepath[:regex]', 'filename[:regex]', 'filesize' or 'nrfiles' filesystem property. The filepath (with trailing "/") and filename can be parsed using an optional regular expression re.findall(regex, filepath/filename). The last match is returned for the filepath, the first match for the filename :param tagname: The name of the filesystem property key, e.g. 'filename', 'filename:sub-(.*?)_' or 'nrfiles' - :param run: If given and tagname == 'nrfiles' then the nrfiles is dependent on the other filesystem matching-criteria + :param runitem: If given and tagname == 'nrfiles' then the nrfiles is dependent on the other filesystem matching-criteria :return: The property value (posix with a trailing "/" if tagname == 'filepath') or '' if the property could not be parsed from the datasource """ @@ -190,10 +190,10 @@ def properties(self, tagname: str, run=None) -> Union[str, int]: return f"{size:.2f} {label[n]}B" if tagname == 'nrfiles' and self.path.is_file(): - if run: # Currently not used but keep the option open for future use - def match(file): return ((match_runvalue(file.parent, run['properties']['filepath']) or not run['properties']['filepath']) and - (match_runvalue(file.name, run['properties']['filename']) or not run['properties']['filename']) and - (match_runvalue(file.stat().st_size, run['properties']['filesize']) or not run['properties']['filesize'])) + if runitem: # Currently not used but keep the option open for future use + def match(file): return ((match_runvalue(file.parent, runitem.properties['filepath']) or not runitem.properties['filepath']) and + (match_runvalue(file.name, runitem.properties['filename']) or not runitem.properties['filename']) and + (match_runvalue(file.stat().st_size, runitem.properties['filesize']) or not runitem.properties['filesize'])) return len([file for file in self.path.parent.iterdir() if match(file)]) else: return len(list(self.path.parent.iterdir())) @@ -308,7 +308,7 @@ def subid_sesid(self, subid: str=None, sesid: str=None) -> Tuple[str, str]: subid_ = self.dynamicvalue(subid, runtime=True) sesid = self.dynamicvalue(sesid, runtime=True) if not subid_: - LOGGER.error(f"Could not parse required sub-