def dicom_values(self, tags): """ Returns a dictionary with the DICOM header fields corresponding to the given tag names Parameters ---------- tags : List[Tuple[str, str]] List of DICOM tag values as 2-tuple of strings, e.g. [('0080', '0020')] repository_login : <repository-login-object> A login object for the repository to avoid having to relogin for every dicom_header call. Returns ------- dct : Dict[Tuple[str, str], str|int|float] """ try: if (self._path is None and self._repository is not None and hasattr(self.repository, 'dicom_header')): hdr = self.repository.dicom_header(self) dct = {t: hdr[t] for t in tags} else: # Get the DICOM object for the first file in the fileset dcm = self.dicom(0) dct = {t: dcm[t].value for t in tags} except KeyError as e: raise ArcanaNameError( e.args[0], "{} does not have dicom tag {}".format( self, e.args[0])) return dct
def session(self, subject_id): try: return self._sessions[subject_id] except KeyError: raise ArcanaNameError( subject_id, ("{} doesn't have a session named '{}'".format( self, subject_id)))
def spec(self, name): """ Returns either the input corresponding to a fileset or field field spec or a spec or parameter that has either been passed to the study as an input or can be derived. Parameters ---------- name : Str | BaseData | Parameter An parameter, fileset or field or name of one """ if isinstance(name, (BaseData, Parameter)): name = name.name try: spec = self._inputs[name] except KeyError: try: spec = self._bound_specs[name] except KeyError: if name in self._data_specs: raise ArcanaMissingDataException( "Acquired (i.e. non-generated) fileset '{}' " "was not supplied when the study '{}' was " "initiated".format(name, self.name)) else: try: spec = self._param_specs[name] except KeyError: raise ArcanaNameError( name, "'{}' is not a recognised spec name " "for {} studies:\n{}.".format( name, self.__class__.__name__, '\n'.join(sorted(self.spec_names())))) return spec
def sub_study_spec(cls, name): try: return cls._sub_study_specs[name] except KeyError: raise ArcanaNameError( 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 ArcanaNameError( name, "'{}' not found in sub-studes ('{}')".format( name, "', '".join(self._sub_studies)))
def parameter_spec(cls, name): try: return cls._param_specs[name] except KeyError: raise ArcanaNameError( name, "No parameter spec named '{}' in {} (available: " "'{}')".format(name, cls.__name__, "', '".join(list(cls._param_specs.keys()))))
def _get_parameter(self, name): try: parameter = self._parameters[name] except KeyError: try: parameter = self._param_specs[name] except KeyError: raise ArcanaNameError( name, "Invalid parameter, '{}', in {} (valid '{}')".format( name, self._param_error_location, "', '".join(self.parameter_spec_names()))) return parameter
def map(self, name): try: return self._name_map[name] except KeyError: if name not in self.study_class.spec_names(): raise ArcanaNameError( name, "'{}' doesn't match any filesets, fields, parameters " "in the study class {} ('{}')".format( name, self.name, self.study_class.__name__, "', '".join(self.study_class.spec_names()))) return self.apply_prefix(name)
def data_spec(cls, name): """ Return the fileset_spec, i.e. the template of the fileset expected to be supplied or generated corresponding to the fileset_spec name. Parameters ---------- name : Str Name of the fileset_spec to return """ if isinstance(name, BaseData): name = name.name try: return cls._data_specs[name] except KeyError: raise ArcanaNameError( name, "No fileset spec named '{}' in {} (available: " "'{}')".format(name, cls.__name__, "', '".join(list(cls._data_specs.keys()))))
def field(self, name, study=None): try: return self._fields[(name, study)] except KeyError: available = [d.name for d in self.fields if d.from_study == study] other_studies = [ d.from_study for d in self.fields if d.name == name ] if other_studies: msg = ( ". NB: matching field(s) found for '{}' study(ies)".format( name, other_studies)) else: msg = '' raise ArcanaNameError( name, ("{} doesn't have a field named '{}' " "(available '{}')".format( self, name, ("for study '{}'" if study is not None else ''), "', '".join(available), other_studies), msg))
def visit(self, id): # @ReservedAssignment try: return self._visits[id] except KeyError: raise ArcanaNameError( id, ("{} doesn't have a visit named '{}'".format(self, id)))
def input(self, name): try: return self._inputs[name] except KeyError: raise ArcanaNameError( name, "{} doesn't have an input named '{}'".format(self, name))
def __init__(self, name, repository, processor, inputs, environment=None, parameters=None, subject_ids=None, visit_ids=None, enforce_inputs=True, reprocess=False, fill_tree=False): try: # This works for PY3 as the metaclass inserts it itself if # it isn't provided metaclass = type(self).__dict__['__metaclass__'] if not issubclass(metaclass, StudyMetaClass): raise KeyError except KeyError: raise ArcanaUsageError( "Need to have StudyMetaClass (or a sub-class) as " "the metaclass of all classes derived from Study") if isinstance(repository, basestring): repository = DirectoryRepository(repository, depth=None) if isinstance(processor, basestring): processor = LinearProcessor(processor) if environment is None: environment = StaticEnvironment() self._name = name self._repository = repository self._processor = processor.bind(self) self._environment = environment self._inputs = {} self._subject_ids = subject_ids self._visit_ids = visit_ids self._tree = self.repository.cached_tree(subject_ids=subject_ids, visit_ids=visit_ids, fill=fill_tree) if not self.subject_ids: raise ArcanaUsageError( "No subject IDs provided and destination repository " "is empty") if not self.visit_ids: raise ArcanaUsageError( "No visit IDs provided and destination repository " "is empty") self._reprocess = reprocess # For recording which parameters are accessed # during pipeline generation so they can be attributed to the # pipeline after it is generated (and then saved in the # provenance self._pipeline_to_generate = None self._referenced_parameters = None # Set parameters if parameters is None: parameters = {} elif not isinstance(parameters, dict): # Convert list of parameters into dictionary parameters = {o.name: o for o in parameters} self._parameters = {} for param_name, param in list(parameters.items()): if not isinstance(param, Parameter): param = Parameter(param_name, param) try: param_spec = self._param_specs[param_name] except KeyError: raise ArcanaNameError( param_name, "Provided parameter '{}' is not present in the " "allowable parameters for {} classes ('{}')".format( param_name, type(self).__name__, "', '".join(self.parameter_spec_names()))) param_spec.check_valid(param, context='{}(name={})'.format( type(self).__name__, name)) self._parameters[param_name] = param # Convert inputs to a dictionary if passed in as a list/tuple if not isinstance(inputs, dict): inputs = {i.name: i for i in inputs} # Add each "input fileset" checking to see whether the given # fileset_spec name is valid for the study types for inpt_name, inpt in list(inputs.items()): try: spec = self.data_spec(inpt_name) except ArcanaNameError: raise ArcanaNameError( inpt.name, "Input name '{}' isn't in data specs of {} ('{}')".format( inpt.name, self.__class__.__name__, "', '".join(self._data_specs))) else: if isinstance(spec, BaseFileset): if isinstance(inpt, BaseField): raise ArcanaUsageError( "Passed field ({}) as input to fileset spec" " {}".format(inpt, spec)) if spec.derived: try: # FIXME: should provide requirement manager to # converter_from but it hasn't been implemented yet spec.format.converter_from(inpt.format) except ArcanaNoConverterError as e: raise ArcanaNoConverterError( "{}, which is requried to convert:\n{} " "to\n{}.".format(e, inpt, spec)) else: if inpt.format not in spec.valid_formats: raise ArcanaUsageError( "Cannot pass {} as an input to {} as it is " "not in one of the valid formats ('{}')". format( inpt, spec, "', '".join(f.name for f in spec.valid_formats))) elif not isinstance(inpt, BaseField): raise ArcanaUsageError( "Passed fileset ({}) as input to field spec {}".format( inpt, spec)) self._inputs[inpt_name] = inpt.bind(self) # "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 fileset self._bound_specs = {} for spec in self.data_specs(): if spec.name not in self.input_names: if not spec.derived and spec.default is None: # Emit a warning if an acquired fileset has not been # provided for an "acquired fileset" msg = (" acquired fileset '{}' was not given as" " an input of {}.".format(spec.name, self)) if spec.optional: logger.info('Optional' + msg) else: if enforce_inputs: raise ArcanaMissingInputError( 'Non-optional' + msg + " Pipelines " "depending on this fileset will not " "run") else: self._bound_specs[spec.name] = spec.bind(self)
def __init__(self, name, repository, processor, inputs, parameters=None, **kwargs): try: # This works for PY3 as the metaclass inserts it itself if # it isn't provided metaclass = type(self).__dict__['__metaclass__'] if not issubclass(metaclass, MultiStudyMetaClass): raise KeyError except KeyError: raise ArcanaUsageError( "Need to set MultiStudyMetaClass (or sub-class) as " "the metaclass of all classes derived from " "MultiStudy") super(MultiStudy, self).__init__(name, repository, processor, inputs, parameters=parameters, **kwargs) self._sub_studies = {} for sub_study_spec in self.sub_study_specs(): sub_study_cls = sub_study_spec.study_class # Map inputs, data_specs to the sub_study mapped_inputs = {} for data_name in sub_study_cls.data_spec_names(): mapped_name = sub_study_spec.map(data_name) if mapped_name in self.input_names: mapped_inputs[data_name] = self.input(mapped_name) else: try: inpt = self.spec(mapped_name) except ArcanaMissingDataException: pass else: if inpt.derived: mapped_inputs[data_name] = inpt # Map parameters to the sub_study mapped_parameters = {} for param_name in sub_study_cls.parameter_spec_names(): mapped_name = sub_study_spec.map(param_name) parameter = self._get_parameter(mapped_name) mapped_parameters[param_name] = parameter # Create sub-study sub_study = sub_study_spec.study_class( name + '_' + sub_study_spec.name, repository, processor, mapped_inputs, parameters=mapped_parameters, enforce_inputs=False) # Append to dictionary of sub_studies if sub_study_spec.name in self._sub_studies: raise ArcanaNameError( sub_study_spec.name, "Duplicate sub-study names '{}'".format( sub_study_spec.name)) self._sub_studies[sub_study_spec.name] = sub_study