def _create(cls, lims, creation_tag=None, udfs=None, **kwargs): """Create an instance from attributes and return it""" if not udfs: udfs = {} instance = cls(lims, _create_new=True) if creation_tag: instance.root = ElementTree.Element( nsmap(cls._PREFIX + ':' + creation_tag)) elif cls._TAG: instance.root = ElementTree.Element( nsmap(cls._PREFIX + ':' + cls._TAG)) else: instance.root = ElementTree.Element( nsmap(cls._PREFIX + ':' + cls.__name__.lower())) for key in udfs: instance.udf[key] = udfs[key] for attribute in kwargs: if hasattr(instance, attribute): setattr(instance, attribute, kwargs.get(attribute)) else: raise TypeError( "%s create: got an unexpected keyword argument '%s'" % (cls.__name__, attribute)) return instance
def _update_elems(self): self._elems = [] if self._udt: elem = self.rootnode.find(nsmap('udf:type')) if elem is not None: self._udt = elem.attrib['name'] self._elems = elem.findall(nsmap('udf:field')) else: tag = nsmap('udf:field') for elem in self.rootnode.getchildren(): if elem.tag == tag: self._elems.append(elem)
def _update_elems(self): self._elems = [] if self._udt: elem = self.rootnode.find(nsmap('udf:type')) if elem is not None: self._udt = elem.attrib['name'] self._elems = elem.findall(nsmap('udf:field')) else: tag = nsmap('udf:field') for elem in self.rootnode.getchildren(): if elem.tag == tag: self._elems.append(elem)
def _create(cls, lims, creation_tag=None, **kwargs): """Create an instance from attributes and return it""" instance = cls(lims, _create_new=True) if creation_tag: instance.root = ElementTree.Element(nsmap(cls._PREFIX + ':' + creation_tag)) elif cls._TAG: instance.root = ElementTree.Element(nsmap(cls._PREFIX + ':' + cls._TAG)) else: instance.root = ElementTree.Element(nsmap(cls._PREFIX + ':' + cls.__name__.lower())) for attribute in kwargs: if hasattr(instance, attribute): setattr(instance, attribute, kwargs.get(attribute)) else: raise TypeError("%s create: got an unexpected keyword argument '%s'" % (cls.__name__, attribute)) return instance
def create(cls, lims, **kwargs): """Create an instance from attributes then post it to the LIMS""" instance = cls(lims, _create_new=True) if cls._TAG: instance.root = ElementTree.Element(nsmap(cls._PREFIX + ":" + cls._TAG)) else: instance.root = ElementTree.Element(nsmap(cls._PREFIX + ":" + cls.__name__.lower())) for attribute in kwargs: if hasattr(instance, attribute): setattr(instance, attribute, kwargs.get(attribute)) else: raise TypeError("%s create: got an unexpected keyword argument '%s'" % (cls.__name__, attribute)) data = lims.tostring(ElementTree.ElementTree(instance.root)) instance.root = lims.post(uri=lims.get_uri(cls._URI), data=data) instance._uri = instance.root.attrib["uri"] return instance
def set_udt(self, name): assert isinstance(name, str) if not self._udt: raise AttributeError('cannot set name for a UDF dictionary') self._udt = name elem = self.rootnode.find(nsmap('udf:type')) assert elem is not None elem.set('name', name)
def set_udt(self, name): assert isinstance(name, str) if not self._udt: raise AttributeError('cannot set name for a UDF dictionary') self._udt = name elem = self.rootnode.find(nsmap('udf:type')) assert elem is not None elem.set('name', name)
class Sample(Entity): "Customer's sample to be analyzed; associated with a project." _URI = 'samples' _PREFIX = 'smp' name = StringDescriptor('name') date_received = StringDescriptor('date-received') date_completed = StringDescriptor('date-completed') project = EntityDescriptor('project', Project) submitter = EntityDescriptor('submitter', Researcher) # artifact: defined below udf = UdfDictionaryDescriptor() udt = UdtDictionaryDescriptor() notes = EntityListDescriptor('note', Note) files = EntityListDescriptor(nsmap('file:file'), File) externalids = ExternalidListDescriptor() # biosource XXX @classmethod def create(cls, lims, container, position, **kwargs): """Create an instance of Sample from attributes then post it to the LIMS Udfs can be sent in with the kwarg `udfs`. It should be a dictionary-like. """ if not isinstance(container, Container): raise TypeError('%s is not of type Container' % container) udfs = kwargs.pop("udfs", None) instance = super(Sample, cls)._create(lims, creation_tag='samplecreation', **kwargs) location = ElementTree.SubElement(instance.root, 'location') ElementTree.SubElement(location, 'container', dict(uri=container.uri)) position_element = ElementTree.SubElement(location, 'value') position_element.text = position # NOTE: This is a quick fix. I assume that it must be possible to initialize samples # with UDFs for key, value in udfs.items(): attrib = { "name": key, "xmlns:udf": "http://genologics.com/ri/userdefined", } udf = ElementTree.SubElement(instance.root, 'udf:field', attrib=attrib) udf.text = value data = lims.tostring(ElementTree.ElementTree(instance.root)) instance.root = lims.post(uri=lims.get_uri(cls._URI), data=data) instance._uri = instance.root.attrib['uri'] return instance
class Project(Entity): "Project concerning a number of samples; associated with a researcher." _URI = 'projects' _PREFIX = 'prj' name = StringDescriptor('name') open_date = StringDescriptor('open-date') close_date = StringDescriptor('close-date') invoice_date = StringDescriptor('invoice-date') researcher = EntityDescriptor('researcher', Researcher) udf = UdfDictionaryDescriptor() udt = UdtDictionaryDescriptor() files = EntityListDescriptor(nsmap('file:file'), File) externalids = ExternalidListDescriptor()
class ProcessTypeParameter(object): instance = None name = None root = None tag = 'parameter' string = StringDescriptor('string') run_program_per_event = StringDescriptor('run-program-per-event') channel = StringDescriptor('channel') invocation_type = StringDescriptor('invocation-type') file = EntityListDescriptor(nsmap('file:file'), File) def __init__(self, pt_instance, node): self.instance = pt_instance self.root = node self.name = self.root.attrib['name'] def get(self): pass
class Sample(Entity): "Customer's sample to be analyzed; associated with a project." _URI = 'samples' _TAG = 'sample' _PREFIX = 'smp' name = StringDescriptor('name') date_received = StringDescriptor('date-received') date_completed = StringDescriptor('date-completed') project = EntityDescriptor('project', Project) submitter = EntityDescriptor('submitter', Researcher) # artifact: defined below udf = UdfDictionaryDescriptor() udt = UdtDictionaryDescriptor() notes = EntityListDescriptor('note', Note) files = EntityListDescriptor(nsmap('file:file'), File) externalids = ExternalidListDescriptor() # biosource XXX control_type = EntityDescriptor('control-type', Controltype) @classmethod def create(cls, lims, container, position, udfs=None, **kwargs): """Create an instance of Sample from attributes then post it to the LIMS""" if udfs is None: udfs = {} if not isinstance(container, Container): raise TypeError('%s is not of type Container' % container) instance = super(Sample, cls)._create(lims, creation_tag='samplecreation', udfs=udfs, **kwargs) location = ElementTree.SubElement(instance.root, 'location') ElementTree.SubElement(location, 'container', dict(uri=container.uri)) position_element = ElementTree.SubElement(location, 'value') position_element.text = position data = lims.tostring(ElementTree.ElementTree(instance.root)) instance.root = lims.post(uri=lims.get_uri(cls._URI), data=data) instance._uri = instance.root.attrib['uri'] return instance
class Artifact(Entity): "Any process input or output; analyte or file." _URI = 'artifacts' _TAG = 'artifact' _PREFIX = 'art' name = StringDescriptor('name') type = StringDescriptor('type') output_type = StringDescriptor('output-type') parent_process = EntityDescriptor('parent-process', Process) volume = StringDescriptor('volume') concentration = StringDescriptor('concentration') qc_flag = StringDescriptor('qc-flag') location = LocationDescriptor('location') working_flag = BooleanDescriptor('working-flag') samples = EntityListDescriptor('sample', Sample) udf = UdfDictionaryDescriptor() files = EntityListDescriptor(nsmap('file:file'), File) reagent_labels = ReagentLabelList() # artifact_flags XXX # artifact_groups XXX def input_artifact_list(self): """Returns the input artifact ids of the parrent process.""" input_artifact_list = [] try: for tuple in self.parent_process.input_output_maps: if tuple[1]['limsid'] == self.id: input_artifact_list.append(tuple[0]['uri']) # ['limsid']) except: pass return input_artifact_list def get_state(self): "Parse out the state value from the URI." parts = urlparse(self.uri) params = parse_qs(parts.query) try: return params['state'][0] except (KeyError, IndexError): return None @property def container(self): "The container where the artifact is located, or None" try: return self.location[0] except: return None def stateless(self): "returns the artefact independently of it's state" parts = urlparse(self.uri) if 'state' in parts[4]: stateless_uri = urlunparse( [parts[0], parts[1], parts[2], parts[3], '', '']) return Artifact(self.lims, uri=stateless_uri) else: return self # XXX set_state ? state = property(get_state) stateless = property(stateless) def _get_workflow_stages_and_statuses(self): self.get() result = [] rootnode = self.root.find('workflow-stages') for node in rootnode.findall('workflow-stage'): result.append((Stage(self.lims, uri=node.attrib['uri']), node.attrib['status'], node.attrib['name'])) return result workflow_stages_and_statuses = property(_get_workflow_stages_and_statuses)
class Process(Entity): "Process (instance of Processtype) executed producing ouputs from inputs." _URI = 'processes' _PREFIX = 'prc' type = EntityDescriptor('type', Processtype) date_run = StringDescriptor('date-run') technician = EntityDescriptor('technician', Researcher) protocol_name = StringDescriptor('protocol-name') input_output_maps = InputOutputMapList() udf = UdfDictionaryDescriptor() udt = UdtDictionaryDescriptor() files = EntityListDescriptor(nsmap('file:file'), File) process_parameter = StringDescriptor('process-parameter') instrument = EntityDescriptor('instrument', Instrument) # process_parameters XXX def outputs_per_input(self, inart, ResultFile=False, SharedResultFile=False, Analyte=False): """Getting all the output artifacts related to a particual input artifact""" inouts = [ io for io in self.input_output_maps if io[0]['limsid'] == inart ] if ResultFile: inouts = [ io for io in inouts if io[1]['output-type'] == 'ResultFile' ] elif SharedResultFile: inouts = [ io for io in inouts if io[1]['output-type'] == 'SharedResultFile' ] elif Analyte: inouts = [io for io in inouts if io[1]['output-type'] == 'Analyte'] outs = [io[1]['uri'] for io in inouts] return outs def input_per_sample(self, sample): """gettiung all the input artifacts dereved from the specifyed sample""" ins_all = self.all_inputs() ins = [] for inp in ins_all: for samp in inp.samples: if samp.name == sample and inp not in ins: ins.append(inp) return ins def all_inputs(self, unique=True, resolve=False): """Retrieving all input artifacts from input_output_maps if unique is true, no duplicates are returned. """ # if the process has no input, that is not standard and we want to know about it try: ids = [io[0]['limsid'] for io in self.input_output_maps] except TypeError: logger.error("Process ", self, " has no input artifacts") raise TypeError if unique: ids = list(frozenset(ids)) if resolve: return self.lims.get_batch( [Artifact(self.lims, id=id) for id in ids if id is not None]) else: return [Artifact(self.lims, id=id) for id in ids if id is not None] def all_outputs(self, unique=True, resolve=False): """Retrieving all output artifacts from input_output_maps if unique is true, no duplicates are returned. """ # Given how ids is structured, io[1] might be None : some process don't have an output. ids = [ io[1]['limsid'] for io in self.input_output_maps if io[1] is not None ] if unique: ids = list(frozenset(ids)) if resolve: return self.lims.get_batch( [Artifact(self.lims, id=id) for id in ids if id is not None]) else: return [Artifact(self.lims, id=id) for id in ids if id is not None] def shared_result_files(self): """Retreve all resultfiles of output-generation-type PerAllInputs.""" artifacts = self.all_outputs(unique=True) return [a for a in artifacts if a.output_type == 'SharedResultFile'] def result_files(self): """Retreve all resultfiles of output-generation-type perInput.""" artifacts = self.all_outputs(unique=True) return [a for a in artifacts if a.output_type == 'ResultFile'] def analytes(self): """Retreving the output Analytes of the process, if existing. If the process is not producing any output analytes, the input analytes are returned. Input/Output is returned as a information string. Makes aggregate processes and normal processes look the same.""" info = 'Output' artifacts = self.all_outputs(unique=True) analytes = [a for a in artifacts if a.type == 'Analyte'] if len(analytes) == 0: artifacts = self.all_inputs(unique=True) analytes = [a for a in artifacts if a.type == 'Analyte'] info = 'Input' return analytes, info def parent_processes(self): """Retrieving all parent processes through the input artifacts""" return [i_a.parent_process for i_a in self.all_inputs(unique=True)] def output_containers(self): """Retrieve all unique output containers""" cs = [] for o_a in self.all_outputs(unique=True): if o_a.container: cs.append(o_a.container) return list(frozenset(cs)) @property def step(self): """Retrive the Step coresponding to this process. They share the same id""" return Step(self.lims, id=self.id)
def __get__(self, instance, cls): instance.get() result = [] for node in instance.root.findall(nsmap('ri:externalid')): result.append((node.attrib.get('id'), node.attrib.get('uri'))) return result
def __setitem__(self, key, value): self._lookup[key] = value for node in self._elems: if node.attrib['name'] != key: continue vtype = node.attrib['type'].lower() if value is None: pass elif vtype == 'string': if not self._is_string(value): raise TypeError('String UDF requires str or unicode value') elif vtype == 'str': if not self._is_string(value): raise TypeError('String UDF requires str or unicode value') elif vtype == 'text': if not self._is_string(value): raise TypeError('Text UDF requires str or unicode value') elif vtype == 'numeric': if not isinstance(value, (int, float, Decimal)): raise TypeError('Numeric UDF requires int or float value') value = str(value) elif vtype == 'boolean': if not isinstance(value, bool): raise TypeError('Boolean UDF requires bool value') value = value and 'true' or 'false' elif vtype == 'date': if not isinstance(value, datetime.date): # Too restrictive? raise TypeError('Date UDF requires datetime.date value') value = str(value) elif vtype == 'uri': if not self._is_string(value): raise TypeError( 'URI UDF requires str or punycode (unicode) value') value = str(value) else: raise NotImplemented("UDF type '%s'" % vtype) if not isinstance(value, str): if not self._is_string(value): value = str(value).encode('UTF-8') node.text = value break else: # Create new entry; heuristics for type if self._is_string(value): vtype = '\n' in value and 'Text' or 'String' elif isinstance(value, bool): vtype = 'Boolean' value = value and 'true' or 'false' elif isinstance(value, (int, float, Decimal)): vtype = 'Numeric' value = str(value) elif isinstance(value, datetime.date): vtype = 'Date' value = str(value) else: raise NotImplementedError("Cannot handle value of type '%s'" " for UDF" % type(value)) if self._udt: root = self.rootnode.find(nsmap('udf:type')) else: root = self.rootnode elem = ElementTree.SubElement(root, nsmap('udf:field'), type=vtype, name=key) if not isinstance(value, str): if not self._is_string(value): value = str(value).encode('UTF-8') elem.text = value #update the internal elements and lookup with new values self._update_elems() self._prepare_lookup()
def __get__(self, instance, cls): instance.get() result = [] for node in instance.root.findall(nsmap('ri:externalid')): result.append((node.attrib.get('id'), node.attrib.get('uri'))) return result
def __setitem__(self, key, value): self._lookup[key] = value for node in self._elems: if node.attrib['name'] != key: continue vtype = node.attrib['type'].lower() if value is None: pass elif vtype == 'string': if not self._is_string(value): raise TypeError('String UDF requires str or unicode value') elif vtype == 'str': if not self._is_string(value): raise TypeError('String UDF requires str or unicode value') elif vtype == 'text': if not self._is_string(value): raise TypeError('Text UDF requires str or unicode value') elif vtype == 'numeric': if not isinstance(value, (int, float, Decimal)): raise TypeError('Numeric UDF requires int or float value') value = str(value) elif vtype == 'boolean': if not isinstance(value, bool): raise TypeError('Boolean UDF requires bool value') value = value and 'true' or 'false' elif vtype == 'date': if not isinstance(value, datetime.date): # Too restrictive? raise TypeError('Date UDF requires datetime.date value') value = str(value) elif vtype == 'uri': if not self._is_string(value): raise TypeError('URI UDF requires str or punycode (unicode) value') value = str(value) else: raise NotImplemented("UDF type '%s'" % vtype) if not isinstance(value, str): if not self._is_string(value): value = str(value).encode('UTF-8') node.text = value break else: # Create new entry; heuristics for type if self._is_string(value): vtype = '\n' in value and 'Text' or 'String' elif isinstance(value, bool): vtype = 'Boolean' value = value and 'true' or 'false' elif isinstance(value, (int, float, Decimal)): vtype = 'Numeric' value = str(value) elif isinstance(value, datetime.date): vtype = 'Date' value = str(value) else: raise NotImplementedError("Cannot handle value of type '%s'" " for UDF" % type(value)) if self._udt: root = self.rootnode.find(nsmap('udf:type')) else: root = self.rootnode elem = ElementTree.SubElement(root, nsmap('udf:field'), type=vtype, name=key) if not isinstance(value, str): if not self._is_string(value): value = str(value).encode('UTF-8') elem.text = value #update the internal elements and lookup with new values self._update_elems() self._prepare_lookup()