def bound_data_spec(self, name): """ Returns either the dataset/field that has been passed to the study __init__ matching the dataset/field name provided or the derived dataset that is to be generated using the pipeline associated with the generated data_spec Parameters ---------- name : Str Name of the data spec to the find the corresponding primary dataset or derived dataset to be generated """ if isinstance(name, BaseDatum): name = name.name try: data = self._inputs[name] except KeyError: try: data = self._bound_specs[name] except KeyError: if name in self._data_specs: raise NiAnalysisMissingDataException( "Acquired (i.e. non-generated) dataset '{}' " "was not supplied when the study '{}' was " "initiated".format(name, self.name)) else: raise NiAnalysisNameError( name, "'{}' is not a recognised dataset_spec name " "for {} studies.".format(name, self.__class__.__name__)) return data
def option_spec(cls, name): try: return cls._option_specs[name] except KeyError: raise NiAnalysisNameError( name, "No option spec named '{}' in {} (available: " "'{}')".format(name, cls.__name__, "', '".join(cls._option_specs.keys())))
def strip_prefix(self, name): if not name.startswith(self.name + '_'): raise NiAnalysisNameError( name, "'{}' is not explicitly provided in SubStudySpec " "name map and doesn't start with the SubStudySpec " "prefix '{}_'".format(name, self.name)) return name[len(self.name) + 1:]
def sub_study_spec(cls, name): try: return cls._sub_study_specs[name] except KeyError: raise NiAnalysisNameError( name, "'{}' not found in sub-studes ('{}')" .format(name, "', '".join(cls._sub_study_specs)))
def sub_study(self, name): try: return self._sub_studies[name] except KeyError: raise NiAnalysisNameError( name, "'{}' not found in sub-studes ('{}')" .format(name, "', '".join(self._sub_studies)))
def _get_option(self, name): try: option = self._options[name] except KeyError: try: option = self._option_specs[name] except KeyError: raise NiAnalysisNameError( name, "{} does not have an option named '{}'".format(self, name)) return option
def inverse_map(self, name): try: return self._inv_map[name] except KeyError: if name not in chain(self.study_class.data_spec_names(), self.study_class.option_spec_names()): raise NiAnalysisNameError( name, "'{}' doesn't match any datasets, fields or options" " in the study class {} ('{}')" .format(name, self.study_class.__name__, "', '".join( self.study_class.data_spec_names()))) return self.apply_prefix(name)
def add_input(self, input_name): """ Adds a new input to the pipeline. Useful if extending a pipeline in a derived Study class Parameters ---------- input_name : str Name of the input to add to the pipeline """ if input_name not in self.study.data_spec_names(): raise NiAnalysisNameError( input_name, "'{}' is not a name of a specified dataset or field in {} " "Study".format(input_name, self.study.name)) self._inputs.append(input_name)
def __init__(self, name, archive, runner, inputs, options=None, **kwargs): options = [] if options is None else options try: if not issubclass(type(self).__dict__['__metaclass__'], MultiStudyMetaClass): raise KeyError except KeyError: raise NiAnalysisUsageError( "Need to set MultiStudyMetaClass (or sub-class) as " "the metaclass of all classes derived from " "MultiStudy") super(MultiStudy, self).__init__(name, archive, runner, inputs, options=options, **kwargs) self._sub_studies = {} for sub_study_spec in self.sub_study_specs(): # Create copies of the input datasets to pass to the # __init__ method of the generated sub-studies sub_study_cls = sub_study_spec.study_class mapped_inputs = [] for inpt in inputs: try: mapped_inputs.append( inpt.renamed(sub_study_spec.map(inpt.name))) except NiAnalysisNameError: pass # Ignore datasets not required for sub-study mapped_options = [] for opt_name in sub_study_cls.option_spec_names(): mapped_name = sub_study_spec.inverse_map(opt_name) option = self._get_option(mapped_name) mapped_options.append(option.renamed(opt_name)) # Create sub-study sub_study = sub_study_spec.study_class( name + '_' + sub_study_spec.name, archive, runner, mapped_inputs, options=mapped_options, enforce_inputs=False) # # Set sub-study as attribute # setattr(self, sub_study_spec.name, sub_study) # Append to dictionary of sub_studies if sub_study_spec.name in self._sub_studies: raise NiAnalysisNameError( sub_study_spec.name, "Duplicate sub-study names '{}'" .format(sub_study_spec.name)) self._sub_studies[sub_study_spec.name] = sub_study
def map(self, name): try: return self._name_map[name] except KeyError: mapped = self.strip_prefix(name) if mapped not in chain(self.study_class.data_spec_names(), self.study_class.option_spec_names()): raise NiAnalysisNameError( name, "'{}' has a matching prefix '{}_' but '{}' doesn't" " match any datasets, fields or options in the " "study class {} ('{}')" .format(name, self.name, mapped, self.study_class.__name__, "', '".join( self.study_class.data_spec_names()))) return mapped
def data_spec(cls, name): """ Return the dataset_spec, i.e. the template of the dataset expected to be supplied or generated corresponding to the dataset_spec name. Parameters ---------- name : Str Name of the dataset_spec to return """ if isinstance(name, BaseDatum): name = name.name try: return cls._data_specs[name] except KeyError: raise NiAnalysisNameError( name, "No dataset spec named '{}' in {} (available: " "'{}')".format(name, cls.__name__, "', '".join(cls._data_specs.keys())))
def __init__(self, name, archive, runner, inputs, options=None, subject_ids=None, visit_ids=None, enforce_inputs=True, reprocess=False): try: if not issubclass( type(self).__dict__['__metaclass__'], StudyMetaClass): raise KeyError except KeyError: raise NiAnalysisUsageError( "Need to have StudyMetaClass (or a sub-class) as " "the metaclass of all classes derived from Study") if isinstance(options, dict): # Convert dictionary of key-value pairs into list of Option # objects options = [Option(k, v) for k, v in options.items()] elif options is None: options = [] self._name = name self._archive = archive self._runner = runner self._inputs = {} # "Bind" data specs in the class to the current study object # this will allow them to prepend the study name to the name # of the dataset self._bound_specs = {} for opt in options: try: opt_spec = self._option_specs[opt.name] except KeyError: raise NiAnalysisNameError( "Provided option '{}' is not present in the " "allowable options for {} classes ('{}')".format( opt.name, type(self).__name__, "', '".join(self.default_options))) if not isinstance(opt.value, opt_spec.dtype): raise NiAnalysisUsageError( "Incorrect datatype for '{}' option provided " "to '{}' {}, {} ({}). Should be {}".format( opt.name, name, type(self).__name__, type(opt.value), opt_spec.dtype)) if (opt_spec.choices is not None and opt.value not in opt_spec.choices): raise NiAnalysisUsageError( "Provided value for '{}' option in '{}' {}, {}, is " "not a valid choice. Can be one of {}".format( opt.name, name, type(self).__name__, opt.value, ','.join(opt_spec.choices))) self._options = dict((o.name, o) for o in options) self._subject_ids = subject_ids self._visit_ids = visit_ids self._tree_cache = None # Add each "input dataset" checking to see whether the given # dataset_spec name is valid for the study type for inpt in inputs: if inpt.name not in self._data_specs: raise NiAnalysisNameError( inpt.name, "Match name '{}' isn't in data specs of {} ('{}')".format( inpt.name, self.__class__.__name__, "', '".join(self._data_specs))) self._inputs[inpt.name] = inpt.bind(self) for spec in self.data_specs(): if not spec.derived: # Emit a warning if an acquired dataset has not been # provided for an "acquired dataset" if spec.name not in self._inputs: msg = (" acquired dataset '{}' was not given as an " "input of {}.".format(spec.name, self)) if spec.optional: logger.info('Optional' + msg) else: if enforce_inputs: raise NiAnalysisMissingInputError( 'Non-optional' + msg + " Pipelines " "depending on this dataset will not run") else: self._bound_specs[spec.name] = spec.bind(self) self._reprocess = reprocess # Record options accessed before a pipeline is created # so they can be attributed to the pipeline after creation self._pre_options = defaultdict(list)
def input(self, name): try: return self._inputs[name] except KeyError: raise NiAnalysisNameError( name, "{} doesn't have an input named '{}'".format(self, name))