def built_dicommap(dicomfile: Path, bidsmap: dict, template: dict) -> dict: """ All the logic to map dicomfields onto bids labels go into this function :param dicomfile: The full-path name of the source dicom-file :param bidsmap: The bidsmap as we had it :param template: Full BIDS heuristics data structure, with all options, BIDS labels and attributes, etc :return: The bidsmap with new entries in it """ # Get the bidsmodality and dirname (= bidslabel) from the pathname (samples/bidsmodality/[dirname/]dicomfile) suffix = dicomfile.parts[-2] if suffix in bids.bidsmodalities + (bids.unknownmodality, bids.ignoremodality): modality = suffix else: modality = dicomfile.parts[-3] # Input checks if not bids.is_dicomfile(dicomfile) or not template[ 'DICOM'] or not template['DICOM'][modality]: return bidsmap if modality not in bids.bidsmodalities + (bids.unknownmodality, bids.ignoremodality): raise ValueError( "Don't know what to do with this bidsmodality directory name: {}\n{}" .format(modality, dicomfile)) # Get bids-labels from the matching run in the template run = bids.get_run( template, 'DICOM', modality, suffix, dicomfile) # TODO: check if the dicomfile argument is not broken if not run: raise ValueError( f"Oops, this should not happen! BIDS modality '{modality}' or one of the bidslabels is not accounted for in the code\n{dicomfile}" ) # Copy the filled-in run over to the bidsmap if not bids.exist_run(bidsmap, 'DICOM', modality, run): bidsmap = bids.append_run(bidsmap, 'DICOM', modality, run) return bidsmap
def bidsmapper_plugin(session: Path, bidsmap_new: dict, bidsmap_old: dict, template: dict, store: dict) -> None: """ All the logic to map the DICOM/PAR source fields onto bids labels go into this function :param session: The full-path name of the subject/session raw data source folder :param bidsmap_new: The new study bidsmap that we are building :param bidsmap_old: The previous study bidsmap that has precedence over the template bidsmap :param template: The template bidsmap with the default heuristics :param store: The paths of the source- and target-folder :return: """ # Get started plugin = { 'dcm2niix2bids': bidsmap_new['Options']['plugins']['dcm2niix2bids'] } datasource = bids.get_datasource(session, plugin) dataformat = datasource.dataformat if not dataformat: return # Collect the different DICOM/PAR source files for all runs in the session sourcefiles = [] if dataformat == 'DICOM': for sourcedir in bidscoin.lsdirs(session): for n in range( 1 ): # Option: Use range(2) to scan two files and catch e.g. magnitude1/2 fieldmap files that are stored in one Series folder (but bidscoiner sees only the first file anyhow and it makes bidsmapper 2x slower :-() sourcefile = bids.get_dicomfile(sourcedir, n) if sourcefile.name: sourcefiles.append(sourcefile) elif dataformat == 'PAR': sourcefiles = bids.get_parfiles(session) else: LOGGER.exception(f"Unsupported dataformat '{dataformat}'") # Update the bidsmap with the info from the source files for sourcefile in sourcefiles: # Input checks if not sourcefile.name or (not template[dataformat] and not bidsmap_old[dataformat]): LOGGER.error( f"No {dataformat} source information found in the bidsmap and template for: {sourcefile}" ) return # See if we can find a matching run in the old bidsmap datasource = bids.DataSource(sourcefile, plugin, dataformat) run, match = bids.get_matching_run(datasource, bidsmap_old) # If not, see if we can find a matching run in the template if not match: run, _ = bids.get_matching_run(datasource, template) # See if we have collected the run somewhere in our new bidsmap if not bids.exist_run(bidsmap_new, '', run): # Communicate with the user if the run was not present in bidsmap_old or in template, i.e. that we found a new sample if not match: LOGGER.info( f"Discovered '{datasource.datatype}' {dataformat} sample: {sourcefile}" ) # Now work from the provenance store if store: targetfile = store['target'] / sourcefile.relative_to( store['source']) targetfile.parent.mkdir(parents=True, exist_ok=True) run['provenance'] = str(shutil.copy2(sourcefile, targetfile)) # Copy the filled-in run over to the new bidsmap bids.append_run(bidsmap_new, run) else: # Communicate with the user if the run was already present in bidsmap_old or in template LOGGER.debug( f"Known '{datasource.datatype}' {dataformat} sample: {sourcefile}" )
def bidsmapper_plugin(session: Path, bidsmap_new: dict, bidsmap_old: dict, template: dict, store: dict) -> None: """ All the heuristics spec2nii2bids attributes and properties onto bids labels and meta-data go into this plugin function. The function is expected to update / append new runs to the bidsmap_new data structure. The bidsmap options for this plugin are stored in: bidsmap_new['Options']['plugins']['spec2nii2bids'] :param session: The full-path name of the subject/session raw data source folder :param bidsmap_new: The new study bidsmap that we are building :param bidsmap_old: The previous study bidsmap that has precedence over the template bidsmap :param template: The template bidsmap with the default heuristics :param store: The paths of the source- and target-folder :return: """ # Get the plugin settings plugin = { 'spec2nii2bids': bidsmap_new['Options']['plugins']['spec2nii2bids'] } # Update the bidsmap with the info from the source files for sourcefile in [ file for file in session.rglob('*') if is_sourcefile(file) ]: datasource = bids.DataSource(sourcefile, plugin) dataformat = datasource.dataformat # Input checks if not template[dataformat] and not bidsmap_old[dataformat]: LOGGER.error( f"No {dataformat} source information found in the bidsmap and template for: {sourcefile}" ) return if not template.get(dataformat) and not bidsmap_old.get(dataformat): LOGGER.error( f"No {dataformat} source information found in the bidsmap and template for: {sourcefile}" ) return # See if we can find a matching run in the old bidsmap run, match = bids.get_matching_run(datasource, bidsmap_old) # If not, see if we can find a matching run in the template if not match: run, _ = bids.get_matching_run(datasource, template) # See if we have collected the run somewhere in our new bidsmap if not bids.exist_run(bidsmap_new, '', run): # Communicate with the user if the run was not present in bidsmap_old or in template, i.e. that we found a new sample if not match: LOGGER.info( f"Discovered '{datasource.datatype}' {dataformat} sample: {sourcefile}" ) # Now work from the provenance store if store: targetfile = store['target'] / sourcefile.relative_to( store['source']) targetfile.parent.mkdir(parents=True, exist_ok=True) run['provenance'] = str(shutil.copy2(sourcefile, targetfile)) # Copy the filled-in run over to the new bidsmap bids.append_run(bidsmap_new, run) else: # Communicate with the user if the run was already present in bidsmap_old or in template LOGGER.debug( f"Known '{datasource.datatype}' {dataformat} sample: {sourcefile}" )
def build_dicommap(runfolder: Path, bidsmap_new: dict, bidsmap_old: dict, template: dict, gui: object) -> dict: """ All the logic to map dicom-attributes (fields/tags) onto bids-labels go into this function :param runfolder: The full-path name of the series-folder containing source dicom-files :param bidsmap_new: The bidsmap that we are building :param bidsmap_old: Full BIDS heuristics data structure, with all options, BIDS labels and attributes, etc :param template: The bidsmap template with the default heuristics :param gui: If not None, the user will not be asked for help if an unknown run is encountered :return: The bidsmap with new entries in it """ # Input checks dicomfile = bids.get_dicomfile(runfolder) if not dicomfile.name or (not template['DICOM'] and not bidsmap_old['DICOM']): LOGGER.info('No DICOM information found in the bidsmap and template') return bidsmap_new # See if we can find a matching run in the old bidsmap run, modality, index = bids.get_matching_run(dicomfile, bidsmap_old) # If not, see if we can find a matching run in the template if index is None: run, modality, _ = bids.get_matching_run(dicomfile, template) # See if we have collected the run in our new bidsmap if not bids.exist_run(bidsmap_new, 'DICOM', '', run): # Copy the filled-in run over to the new bidsmap bidsmap_new = bids.append_run(bidsmap_new, 'DICOM', modality, run) # Communicate with the user if the run was not present in bidsmap_old or in template LOGGER.info(f"New '{modality}' sample found: {dicomfile}") # Launch a GUI to ask the user for help if the new run comes from the template (i.e. was not yet in the old bidsmap) if gui and gui.interactive == 2 and index is None: # Open the interactive edit window to get the new mapping dialog_edit = bidseditor.EditDialog(dicomfile, modality, bidsmap_new, template, gui.subprefix, gui.sesprefix) dialog_edit.exec() # Get the result if dialog_edit.result() == 1: # The user has finished the edit bidsmap_new = dialog_edit.target_bidsmap elif dialog_edit.result() in [ 0, 2 ]: # The user has canceled / aborted the edit answer = QMessageBox.question( None, 'BIDSmapper', 'Do you want to abort and quit the bidsmapper?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if answer == QMessageBox.No: pass if answer == QMessageBox.Yes: LOGGER.info('User has quit the bidsmapper') sys.exit() else: LOGGER.debug( f'Unexpected result {dialog_edit.result()} from the edit dialog' ) return bidsmap_new
def build_bidsmap(dataformat: str, sourcefile: Path, bidsmap_new: dict, bidsmap_old: dict, template: dict, store: dict, gui: object) -> dict: """ All the logic to map the Philips PAR/XML fields onto bids labels go into this function :param dataformat: The information source in the bidsmap that is used, e.g. 'DICOM' :param sourcefile: The full-path name of the source file :param bidsmap_new: The bidsmap that we are building :param bidsmap_old: Full BIDS heuristics data structure, with all options, BIDS labels and attributes, etc :param template: The bidsmap template with the default heuristics :param store: The paths of the source- and target-folder :param gui: If not None, the user will not be asked for help if an unknown run is encountered :return: The bidsmap with new entries in it """ # Input checks if not sourcefile.name or (not template[dataformat] and not bidsmap_old[dataformat]): LOGGER.info( f"No {dataformat} source information found in the bidsmap and template" ) return bidsmap_new # See if we can find a matching run in the old bidsmap run, modality, index = bids.get_matching_run(sourcefile, bidsmap_old, dataformat) # If not, see if we can find a matching run in the template if index is None: run, modality, _ = bids.get_matching_run(sourcefile, template, dataformat) # See if we have collected the run in our new bidsmap if not bids.exist_run(bidsmap_new, dataformat, '', run): # Now work from the provenance store if store: targetfile = store['target'] / sourcefile.relative_to( store['source']) targetfile.parent.mkdir(parents=True, exist_ok=True) sourcefile = Path(shutil.copy2(sourcefile, targetfile)) run['provenance'] = str(sourcefile.resolve()) # Communicate with the user if the run was not present in bidsmap_old or in template, i.e. that we found a new sample LOGGER.info(f"Found '{modality}' {dataformat} sample: {sourcefile}") # Copy the filled-in run over to the new bidsmap bidsmap_new = bids.append_run(bidsmap_new, dataformat, modality, run) # Launch a GUI to ask the user for help if the new run comes from the template (i.e. was not yet in the old bidsmap) if gui and gui.interactive == 2 and index is None: # Open the interactive edit window to get the new mapping dialog_edit = bidseditor.EditDialog(dataformat, sourcefile, modality, bidsmap_new, template, gui.subprefix, gui.sesprefix) dialog_edit.exec() # Get the result if dialog_edit.result() == 1: # The user has finished the edit bidsmap_new = dialog_edit.target_bidsmap elif dialog_edit.result() in [ 0, 2 ]: # The user has canceled / aborted the edit answer = QMessageBox.question( None, 'BIDSmapper', 'Do you want to abort and quit the bidsmapper?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if answer == QMessageBox.No: pass if answer == QMessageBox.Yes: LOGGER.info('User has quit the bidsmapper') sys.exit() else: LOGGER.debug( f'Unexpected result {dialog_edit.result()} from the edit dialog' ) return bidsmap_new
def bidsmapper_plugin(session: Path, bidsmap_new: dict, bidsmap_old: dict, template: dict, store: dict) -> None: """ All the logic to map the Nibabel header fields onto bids labels go into this function :param session: The full-path name of the subject/session raw data source folder :param bidsmap_new: The new study bidsmap that we are building :param bidsmap_old: The previous study bidsmap that has precedence over the template bidsmap :param template: The template bidsmap with the default heuristics :param store: The paths of the source- and target-folder :return: """ # Get started plugin = { 'nibabel2bids': bidsmap_new['Options']['plugins']['nibabel2bids'] } datasource = bids.get_datasource(session, plugin, recurse=2) if not datasource.dataformat: return if not (template[datasource.dataformat] or bidsmap_old[datasource.dataformat]): LOGGER.error( f"No {datasource.dataformat} source information found in the bidsmap and template" ) return # Collect the different DICOM/PAR source files for all runs in the session for sourcefile in [ file for file in session.rglob('*') if is_sourcefile(file) ]: # See if we can find a matching run in the old bidsmap datasource = bids.DataSource(sourcefile, plugin, datasource.dataformat) run, match = bids.get_matching_run(datasource, bidsmap_old) # If not, see if we can find a matching run in the template if not match: run, _ = bids.get_matching_run(datasource, template) # See if we have collected the run somewhere in our new bidsmap if not bids.exist_run(bidsmap_new, '', run): # Communicate with the user if the run was not present in bidsmap_old or in template, i.e. that we found a new sample if not match: LOGGER.info( f"Discovered '{datasource.datatype}' {datasource.dataformat} sample: {sourcefile}" ) # Now work from the provenance store if store: targetfile = store['target'] / sourcefile.relative_to( store['source']) targetfile.parent.mkdir(parents=True, exist_ok=True) run['provenance'] = str(shutil.copy2(sourcefile, targetfile)) # Copy the filled-in run over to the new bidsmap bids.append_run(bidsmap_new, run) else: # Communicate with the user if the run was already present in bidsmap_old or in template LOGGER.debug( f"Known '{datasource.datatype}' {datasource.dataformat} sample: {sourcefile}" )