Skip to content

Commit a45d0d1

Browse files
committed
Bugfix for refactoring of IO helper functions (bidscoin.plugins -> bidscoin.utilities) + minor tweaks
1 parent d5997bd commit a45d0d1

File tree

13 files changed

+46
-17
lines changed

13 files changed

+46
-17
lines changed

bidscoin/bcoin.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from importlib.util import find_spec
2727
if find_spec('bidscoin') is None:
2828
sys.path.append(str(Path(__file__).parents[1]))
29-
from bidscoin import templatefolder, pluginfolder, bidsmap_template, tutorialurl, trackusage, tracking, configfile, config, DEBUG
29+
from bidscoin import templatefolder, pluginfolder, bidsmap_template, tutorialurl, trackusage, tracking, configdir, configfile, config, DEBUG
3030

3131
yaml = YAML()
3232
yaml.representer.ignore_aliases = lambda *data: True # Expand aliases (https://stackoverflow.com/questions/58091449/disabling-alias-for-yaml-file-in-python)
@@ -625,6 +625,19 @@ def reportcredits(args: list) -> None:
625625
LOGGER.info(f"No DueCredit citation files found in {Path(args[0]).resolve()} and {report.parent}")
626626

627627

628+
def reset(delete: bool) -> None:
629+
"""
630+
Resets the configuration directory by deleting it if the `delete` parameter is set to True
631+
632+
:param delete: If set to True, the configuration directory will be removed
633+
:return: None
634+
"""
635+
636+
if not delete: return
637+
638+
shutil.rmtree(configdir)
639+
640+
628641
def settracking(value: str) -> None:
629642
"""
630643
Set or show usage tracking
@@ -677,6 +690,7 @@ def main():
677690
pulltutorialdata(tutorialfolder=args.download)
678691
test_bidscoin(bidsmapfile=args.test)
679692
test_bidsmap(bidsmapfile=args.bidsmaptest)
693+
reset(delete=args.reset)
680694
settracking(value=args.tracking)
681695
reportcredits(args=args.credits)
682696

bidscoin/bids.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,7 @@ def exist_run(self, runitem: RunItem, datatype: Union[str, DataType]='') -> bool
12621262

12631263
return False
12641264

1265-
def get_matching_run(self, sourcefile: Union[str, Path], dataformat, runtime=False) -> tuple[RunItem, str]:
1265+
def get_matching_run(self, sourcefile: Union[str, Path], dataformat: str='', runtime: bool=False) -> tuple[RunItem, str]:
12661266
"""
12671267
Find the first run in the bidsmap with properties and attributes that match with the data source. Only non-empty
12681268
properties and attributes are matched, except when runtime is True, then the empty attributes are also matched.
@@ -1271,20 +1271,29 @@ def get_matching_run(self, sourcefile: Union[str, Path], dataformat, runtime=Fal
12711271
ignoredatatypes (e.g. 'exclude') -> normal datatypes (e.g. 'anat') -> unknowndatatypes (e.g. 'extra_data')
12721272
12731273
:param sourcefile: The full filepath of the data source for which to get a run-item
1274-
:param dataformat: The dataformat section in the bidsmap in which a matching run is searched for, e.g. 'DICOM'
1274+
:param dataformat: The dataformat section in the bidsmap in which a matching run is searched for, e.g. 'DICOM'. Leave empty to recursively search through all dataformats
12751275
:param runtime: Dynamic <<values>> are expanded if True
12761276
:return: (run, provenance) A vanilla run that has all its attributes populated with the source file attributes.
12771277
If there is a match, the provenance of the bidsmap entry is returned, otherwise it will be ''
12781278
"""
12791279

1280+
# Iterate over all dataformats if dataformat is not given
1281+
if not dataformat:
1282+
runitem, provenance = RunItem(), ''
1283+
for dformat in self.dataformats:
1284+
runitem, provenance = self.get_matching_run(sourcefile, dformat.dataformat, runtime)
1285+
if provenance: break
1286+
return runitem, provenance
1287+
1288+
# Defaults
12801289
datasource = DataSource(sourcefile, self.plugins, dataformat, options=self.options)
12811290
unknowndatatypes = self.options.get('unknowntypes') or ['unknown_data']
12821291
ignoredatatypes = self.options.get('ignoretypes') or []
12831292
normaldatatypes = [dtype.datatype for dtype in self.dataformat(dataformat).datatypes if dtype not in unknowndatatypes + ignoredatatypes]
12841293
rundata = {'provenance': str(sourcefile), 'properties': {}, 'attributes': {}, 'bids': {}, 'meta': {}, 'events': {}}
12851294
"""The a run-item data structure. NB: Keep in sync with the RunItem() data attributes"""
12861295

1287-
# Loop through all datatypes and runs; all info goes cleanly into runitem (to avoid formatting problem of the CommentedMap)
1296+
# Iterate over all datatypes and runs; all info goes cleanly into runitem (to avoid formatting problem of the CommentedMap)
12881297
if 'fmap' in normaldatatypes:
12891298
normaldatatypes.insert(0, normaldatatypes.pop(normaldatatypes.index('fmap'))) # Put fmap at the front (to catch inverted polarity scans first
12901299
for datatype in ignoredatatypes + normaldatatypes + unknowndatatypes: # The ordered datatypes in which a matching run is searched for

bidscoin/bidscoiner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def bidscoiner(sourcefolder: str, bidsfolder: str, participant: list=(), force:
101101
# Load the data conversion plugins
102102
plugins = [plugin for name in bidsmap.plugins if (plugin := bcoin.import_plugin(name))]
103103
if not plugins:
104-
LOGGER.warning(f"The plugins listed in your bidsmap['Options'] did not have a usable `bidscoiner_plugin` function, nothing to do")
104+
LOGGER.warning(f"The {bidsmap.plugins.keys()} plugins listed in your bidsmap['Options'] did not have a usable `bidscoiner` interface, nothing to do")
105105
LOGGER.info('-------------- FINISHED! ------------')
106106
LOGGER.info('')
107107
return

bidscoin/bidseditor.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
sys.path.append(str(Path(__file__).parents[1]))
2525
from bidscoin import bcoin, bids, bidsversion, check_version, trackusage, bidsmap_template, __version__
2626
from bidscoin.bids import BidsMap, RunItem, DataType
27+
from bidscoin.utilities import is_dicomfile, is_parfile
2728
config.INVALID_KEY_BEHAVIOR = 'IGNORE'
2829

2930
ROW_HEIGHT = 22
@@ -958,7 +959,7 @@ def open_inspectwindow(self, index: int):
958959
datafile = Path(self.filesystem.fileInfo(index).absoluteFilePath())
959960
if datafile.is_file():
960961
ext = ''.join(datafile.suffixes).lower()
961-
if bids.is_dicomfile(datafile) or bids.is_parfile(datafile) or ext in sum((klass.valid_exts for klass in nib.imageclasses.all_image_classes), ('.nii.gz',)):
962+
if is_dicomfile(datafile) or is_parfile(datafile) or ext in sum((klass.valid_exts for klass in nib.imageclasses.all_image_classes), ('.nii.gz',)):
962963
self.popup = InspectWindow(datafile)
963964
self.popup.show()
964965
self.popup.scrollbar.setValue(0) # This can only be done after self.popup.show()
@@ -2041,11 +2042,11 @@ def __init__(self, filename: Path):
20412042
super().__init__()
20422043

20432044
ext = ''.join(filename.suffixes).lower()
2044-
if bids.is_dicomfile(filename):
2045+
if is_dicomfile(filename):
20452046
if filename.name == 'DICOMDIR':
20462047
LOGGER.bcdebug(f"Getting DICOM fields from {filename} will raise dcmread error below if pydicom => v3.0")
20472048
text = str(dcmread(filename, force=True))
2048-
elif bids.is_parfile(filename) or ext in ('.spar', '.txt', '.text', '.log'):
2049+
elif is_parfile(filename) or ext in ('.spar', '.txt', '.text', '.log'):
20492050
text = filename.read_text()
20502051
elif ext == '.7':
20512052
try:

bidscoin/bidsmapper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def bidsmapper(sourcefolder: str, bidsfolder: str, bidsmap: str, template: str,
103103
# Import the data scanning plugins
104104
plugins = [plugin for name in bidsmap_new.plugins if (plugin := bcoin.import_plugin(name))]
105105
if not plugins:
106-
LOGGER.warning(f"The plugins listed in your bidsmap['Options'] did not have a usable `bidsmapper_plugin` function, nothing to do")
106+
LOGGER.warning(f"The {bidsmap_new.plugins.keys()} plugins listed in your bidsmap['Options'] did not have a usable `bidsmapper` interface, nothing to do")
107107
LOGGER.info('-------------- FINISHED! ------------')
108108
LOGGER.info('')
109109
return bidsmap_new

bidscoin/cli/_bcoin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def get_parser() -> argparse.ArgumentParser:
5454
parser.add_argument('-t', '--test', help='Test the bidscoin installation and template bidsmap', nargs='?', metavar='TEMPLATE', const=bidsmap_template)
5555
parser.add_argument('-b', '--bidsmaptest', help='Test the run-items and their bidsnames of all normal runs in the study bidsmap. Provide the bids-folder or the bidsmap filepath', metavar='BIDSMAP')
5656
parser.add_argument('-c', '--credits', help='Show duecredit citations for your BIDS repository. You can also add duecredit summary arguments (without dashes), e.g. `style {apa,harvard1}` or `format {text,bibtex}`.', metavar='OPTIONS', nargs='+')
57+
parser.add_argument('-r', '--reset', help='Restore the settings, plugins and template bidsmaps in your home directory to their default values', action='store_true')
5758
parser.add_argument( '--tracking', help='Show the usage tracking info {show}, or set usage tracking to {yes} or {no}', choices=['yes','no','show'])
5859
parser.add_argument('-v', '--version', help='Show the installed version and check for updates', action='version', version=f"BIDS-version:\t\t{bidsversion()}\nBIDScoin-version:\t{__version__}, {versionmessage}")
5960

bidscoin/cli/_dicomsort.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescri
2222
' dicomsort raw/sub-011/ses-mri01\n'
2323
' dicomsort raw --subprefix sub- --sesprefix ses-\n'
2424
' dicomsort myproject/raw/DICOMDIR --subprefix pat^ --sesprefix\n'
25-
' dicomsort sub-011/ses-mri01/DICOMDIR -n {AcquisitionNumber:05d}_{InstanceNumber:05d}.dcm\n ')
25+
" dicomsort sub-011/ses-mri01/DICOMDIR -n '{AcquisitionNumber:05d}_{InstanceNumber:05d}.dcm'\n ")
2626
parser.add_argument('sourcefolder', help='The root folder containing the [sub/][ses/] dicomfiles or the DICOMDIR file')
2727
parser.add_argument('-i','--subprefix', help='Provide a prefix string to recursively sort sourcefolder/subject subfolders (e.g. "sub-" or "S_")', metavar='PREFIX')
2828
parser.add_argument('-j','--sesprefix', help='Provide a prefix string to recursively sort sourcefolder/subject/session subfolders (e.g. "ses-" or "T_")', metavar='PREFIX')

bidscoin/cli/_rawmapper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescri
2929
parser.add_argument('sourcefolder', help='The source folder with the raw data in sub-#/ses-#/series organization', metavar='FOLDER')
3030
parser.add_argument('-s','--sessions', help='Space separated list of selected sub-#/ses-# names/folders to be processed. Otherwise all sessions in the bidsfolder will be processed', nargs='+', metavar='SESSION')
3131
parser.add_argument('-f','--field', help='The fieldname(s) of the DICOM attribute(s) used to rename or map the subid/sesid foldernames', default=['PatientComments', 'ImageComments'], nargs='+', metavar='NAME')
32-
parser.add_argument('-w','--wildcard', help='The Unix style pathname pattern expansion that is used to select the series from which the dicomfield is being mapped (can contain wildcards)', default='*', metavar='PATTERN')
32+
parser.add_argument('-w','--wildcard', help='The Unix style pathname pattern expansion that is used to select the series folders from which the dicomfield is being mapped (can contain wildcards)', default='*', metavar='PATTERN')
3333
parser.add_argument('-o','--outfolder', help='The mapper-file is normally saved in sourcefolder or, when using this option, in outfolder', metavar='FOLDER')
3434
parser.add_argument('-r','--rename', help='Rename sub-subid/ses-sesid directories in the sourcefolder to sub-dcmval/ses-dcmval', action='store_true')
3535
parser.add_argument('-c','--clobber', help='Rename the sub/ses directories, even if the target-directory already exists', action='store_true')

bidscoin/plugins/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ def bidsmapper(self, session: Path, bidsmap_new: 'BidsMap', bidsmap_old: 'BidsMa
6363
"""
6464

6565
# See for every source file in the session if we already discovered it or not
66-
for sourcefile in session.rglob('*'):
66+
sourcefiles = session.rglob('*')
67+
if not sourcefiles:
68+
LOGGER.info(f"No {__name__} sourcedata found in: {session}")
69+
for sourcefile in sourcefiles:
6770

6871
# Check if the sourcefile is of a supported dataformat
6972
if is_hidden(sourcefile.relative_to(session)) or not (dataformat := self.has_support(sourcefile, dataformat='')):

bidscoin/plugins/dcm2niix2bids.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ def bidsmapper(self, session: Path, bidsmap_new: BidsMap, bidsmap_old: BidsMap,
140140
LOGGER.error(f"Unsupported dataformat '{dataformat}'")
141141

142142
# See for every data source in the session if we already discovered it or not
143+
if not sourcefiles:
144+
LOGGER.info(f"No {__name__} sourcedata found in: {session}")
143145
for sourcefile in sourcefiles:
144146

145147
# Check if the source files all have approximately the same size (difference < 50kB)

0 commit comments

Comments
 (0)