Exemple #1
0
    def batch_get(self, uris, prefetch=True):
        # type: (Iterable[str], bool) -> List[ClarityElement]
        """
        Queries Clarity for a list of uris described by their REST API endpoint.
        If this query can be made as a single request it will be done that way.

        :param uris: A List of uris
        :param prefetch: Force load full content for each element.
        :return: A list of the elements returned by the query.
        """

        if not uris:
            return []  # just return an empty list if there were no uris

        if self.can_batch_get():
            links_root = ETree.Element("{http://genologics.com/ri}links")

            n_queries = 0

            querying_now = set()

            for uri in uris:
                uri = self._strip_params(uri)

                if uri in querying_now:
                    # already covered
                    continue

                obj = self._cache.get(uri)
                if prefetch and (obj is None or not obj.is_fully_retrieved()):
                    link = ETree.SubElement(links_root, "link")
                    link.set("uri", uri)
                    link.set("rel", self._plural_name)
                    querying_now.add(uri)
                    n_queries += 1

            if n_queries > 0:
                result_root = self.lims.request('post',
                                                self.uri + "/batch/retrieve",
                                                links_root)
                result_nodes = result_root.findall(
                    './' + self.element_class.UNIVERSAL_TAG)

                for node in result_nodes:
                    uri = node.get("uri")
                    uri = self._strip_params(uri)

                    old_obj = self._cache.get(uri)
                    if old_obj is not None:
                        old_obj.xml_root = node
                    else:
                        new_obj = self.element_class(self.lims,
                                                     uri=uri,
                                                     xml_root=node)
                        self._cache[uri] = new_obj

            return [self._cache[uri] for uri in uris]

        else:
            return [self.get(uri, force_full_get=prefetch) for uri in uris]
Exemple #2
0
    def _add_action_subnode(self, routing_node, action, workflow_or_stage_uri):
        """
        Generates a ElementTree.SubElement according to the action (assign / unassign) and the workflow/stage uri
        """
        if 'stage' in workflow_or_stage_uri:
            assign_node = ETree.SubElement(
                routing_node, action, {'stage-uri': workflow_or_stage_uri})
        else:
            assign_node = ETree.SubElement(
                routing_node, action, {'workflow-uri': workflow_or_stage_uri})

        return assign_node
Exemple #3
0
    def set_location_well(self, container, well):
        """"
        Sets this artifact's location (usually for sample creation) with
        the given well location, in the given container.

        :param container: The Sample's container
        :type container: s4.clarity.Container
        :param well: The well position in the form "<row>:<col>"
        :type well: str
        """
        location_node = self.make_subelement_with_parents("./location")
        ETree.SubElement(location_node, 'value').text = well

        # attach container node, which must have the uri
        ETree.SubElement(location_node, 'container', {'uri': container.uri})
Exemple #4
0
    def __set__(self, instance, value):
        """
        :type instance: s4.clarity._internal.element.WrappedXml
        :type value: s4.clarity._internal.element.ClarityElement
        """
        self._ensure_settable(instance)

        # a link is of the form:
        # <project limsid="SWI1" uri="https://qalocal/api/v2/projects/SWI1"/>

        node = instance.get_or_create_subnode(self.property_name)
        attribs = {}

        for attrname in self.link_attributes:
            if hasattr(value, attrname):
                attrvalue = getattr(value, attrname)

                if attrvalue is not None:
                    attribs[attrname] = attrvalue

        if node is None:
            ETree.SubElement(instance.xml_root, self.property_name, attribs)
        else:
            for k, v in attribs.items():
                node.set(k, v)
Exemple #5
0
    def raise_on_exception(cls, response, data=None):

        # Make sure we are an exception, if not carry on
        if not cls.is_response_exception(response):
            return

        root = ETree.XML(response.content)
        try:
            msg = root.find('message').text
        except AttributeError:
            msg = "No message provided by Clarity."
        try:
            msg += "\nSuggested actions: " + root.find(
                'suggested-actions').text
        except AttributeError:
            # no suggested-actions
            pass
        extra = root.get('category')
        if extra is not None:
            msg += "\nException category: " + extra
        extra = root.get('code')
        if extra is not None:
            msg += "\nException code: " + extra
        if "File does not exist" in msg:
            raise FileNotFoundException(msg)
        else:
            instance = cls(msg)
            instance.request_body = data.decode("UTF-8") if isinstance(
                data, bytes) else data
            raise instance
Exemple #6
0
    def add_role(self, new_role):
        credentials_node = self.xml_find('credentials')

        for child in credentials_node:
            if child.tag == "role" and child.get("name") == new_role.name:
                return

        ETree.SubElement(credentials_node, 'role', {"uri": new_role.uri})
Exemple #7
0
    def _create_routing_node(self):
        """
        Generates the XML for  workflow/stage assignment/unassignment
        """
        routing_node = ETree.Element(
            "{http://genologics.com/ri/routing}routing")
        for action, routes in self.routing_dict.items():
            for workflow_or_stage_uri, artifact_set in routes.items():
                if artifact_set:
                    assign_node = self._add_action_subnode(
                        routing_node, action, workflow_or_stage_uri)
                    # create an artifact assign node for each samples
                    for artifact in artifact_set:
                        ETree.SubElement(assign_node, "artifact",
                                         {"uri": artifact.uri})

        return routing_node
Exemple #8
0
 def get_or_create_subnode(self, path):
     node = self.xml_find(path)
     if node is None:
         parent = self.xml_root
         for node_name in path.split('/'):
             node = parent.find(node_name)
             if node is None:
                 node = ETree.SubElement(parent, node_name)
             parent = node
     return node
Exemple #9
0
    def _dict_into_node(self, instance, value, parent, name, node=None):
        if node is None:
            node = parent.find('./' + name) or ETree.SubElement(parent, name)

        if type(value) == dict:
            for k, v in value.items():
                if k in self.as_attributes:
                    node.set(k, self._value_to_string(v))
                else:
                    self._dict_into_node(instance, v, node, k)
        elif type(value) == list:
            for subvalue in value:
                # call self again, but value = subvalue, prechosen node
                self._dict_into_node(instance, subvalue, parent, name, node)
                # get a new node
                node = ETree.SubElement(parent, name)
            # release the last node in the list
            parent.remove(node)
        else:
            node.text = self._value_to_string(value)
Exemple #10
0
    def test_decimal_commas_bug(self):
        # some Clarity installs return numeric values with commas in place of decimal points due to locale setting

        test_parsed_xml = ETree.fromstring("""
<art:artifact xmlns:art="http://genologics.com/ri/artifact">
   <udf:field xmlns:udf="http://genologics.com/ri/userdefined" type="Numeric" name="udfname">0,005</udf:field>
</art:artifact>
""")

        element = Artifact(lims=self.lims, xml_root=test_parsed_xml)
        self.assertEqual(element['udfname'], 0.005)
Exemple #11
0
    def make_subelement_with_parents(self, xpath):
        node = self.xml_root

        if not xpath.startswith("."):
            raise Exception("xpath must start with . to make all subelements.")

        for subelement_name in xpath.split('/')[1:]:
            subnode = node.find("./" + subelement_name)
            if subnode is None:
                subnode = ETree.SubElement(node, subelement_name)
            node = subnode

        return node
Exemple #12
0
    def _get_or_make_dict(self, instance):

        node = instance.xml_find('./' + self.property_name)

        # we check node in case we've gotten a new xml tree and still have the old dict.
        if self._dict is not None and self._dict.top_node == node:
            return self._dict

        if node is None:
            node = ETree.SubElement(instance.xml_root, self.property_name)

        return _ClarityLiteralDict(node, self.subprop_name,
                                   self.name_attribute, self.value_attribute)
Exemple #13
0
 def element_from_xml(element_class, xml, **extra_kwargs):
     try:
         return element_class(lims=FakeLims(),
                              xml_root=ETree.fromstring(xml),
                              **extra_kwargs)
     except TypeError as e:
         str(e)
         if "__init__() takes at least" in str(e):
             raise TypeError(
                 "Unable to instantiate %s, provide extra args in extra_kwargs. %s"
                 % (element_class.__name__, e))
         else:
             raise e
Exemple #14
0
    def _get_or_create_node(self, key):
        """
        Get a field XML node, or create and append it to the XML fields node if it doesn't exist.

        :type key: str
        :rtype: ETree.Element
        """
        field_node = self._real_dict.get(key)
        if field_node is None:
            field_node = ETree.SubElement(self._root_node, FIELD_TAG)
            field_node.set('name', key)
            self._real_dict[key] = field_node
        return field_node
Exemple #15
0
    def batch_update(self, elements):
        # type: (Iterable[ClarityElement]) -> None
        """
        Persists the ClarityElements back to Clarity. Will preform
        this action as a single query if possible.

        :param elements: All ClarityElements to save the state of.
        :raises ClarityException: if Clarity returns an exception as XML
        """

        if not elements:
            return

        if self.can_batch_update():
            details_root = ETree.Element(self.batch_tag)

            for el in elements:
                details_root.append(el.xml_root)

            self.lims.request('post', self.uri + "/batch/update", details_root)

        else:
            for el in elements:
                self.lims.request('post', el.uri, el.xml_root)
Exemple #16
0
    def batch_create(self, elements):
        # type: (Iterable[ClarityElement]) -> List[ClarityElement]
        """
        Creates new records in Clarity for each element and returns these new records as ClarityElements.
        If this operation can be performed in a single network operation it will be.

        :param elements: A list of new ClarityElements that have not been persisted to Clarity yet.
        :return: New ClarityElement records from Clarity, created with the data supplied to the method.
        :raises ClarityException: if Clarity returns an exception as XML
        """

        if not elements:
            return []

        if self.can_batch_create():
            details_root = ETree.Element(self.batch_tag)

            for el in elements:
                details_root.append(el.xml_root)

            links = self.lims.request('post', self.uri + "/batch/create",
                                      details_root)

            return self.from_link_nodes(links)

        else:
            objects = []

            for el in elements:
                new_obj = self.element_class(self.lims,
                                             xml_root=self.lims.request(
                                                 'post', el.uri, el.xml_root))
                self._cache[new_obj.uri] = new_obj
                objects.append(new_obj)

            return objects
Exemple #17
0
    def new(self, **kwargs):
        # type: (**str) -> ClarityElement
        """
        Create a new ClarityElement pre-populated with the provided values.
        This object has yet to be persisted to Clarity.

        :param kwargs: Key/Value list of attribute name/value pairs to initialize the element with.
        :return: A new ClarityElement, pre-populated with provided values.
        """

        # creating some types requires using special tag, ie samples
        # are created by posting a 'samplecreation' element, not a 'sample'
        el_tag = getattr(self.element_class, 'CREATION_TAG',
                         self.element_class.UNIVERSAL_TAG)

        # create xml_root, call class constructor
        new_xml_root = ETree.Element(el_tag)
        new_obj = self.element_class(self.lims, xml_root=new_xml_root)

        # set attributes from kwargs to new_object
        for k, v in kwargs.items():
            setattr(new_obj, k, v)

        return new_obj
Exemple #18
0
 def _normalize_xml(xml_string):
     xml_obj_one = ETree.fromstring(xml_string)
     return ETree.tostring(xml_obj_one)
Exemple #19
0
 def xml(self):
     """:rtype: str|bytes"""
     return ETree.tostring(self.xml_root)
Exemple #20
0
 def _add_preset_internal(self, preset_value):
     preset_node = ETree.SubElement(self.xml_root, 'preset')
     preset_node.text = types.obj_to_clarity_string(preset_value)
Exemple #21
0
    def _new_step_from_configuration(self, input_uri_list, controls,
                                     containertype, reagenttype):
        """
        Creates a new step in clarity.

        :param input_uri_list: A list of artifacts that will be use to run the step
        :param controls: The list of controls that will be included in this step
        :param containertype: The container that output artifacts will be placed in.
        :param reagenttype: The name of the reagent category to use for the step.
        """
        log.info("Creating %s (user: %s)", self.__class__.__name__,
                 self.lims.username)

        if not input_uri_list and not controls:
            raise StepRunnerException(
                "Unable to create new step with no input artifacts or contols."
            )

        root = ETree.Element("{http://genologics.com/ri/step}step-creation")
        ETree.SubElement(root, "configuration", {'uri': self.step_config.uri})

        inputsnode = ETree.SubElement(root, "inputs")

        if input_uri_list:
            for input_uri in input_uri_list:
                attrib = {
                    'uri': input_uri,
                    'replicates': str(self.replicates_for_inputuri(input_uri))
                }
                ETree.SubElement(inputsnode, "input", attrib)

        if controls:
            for control in controls:
                attrib = {
                    'control-type-uri': control.uri,
                    'replicates': str(self.replicates_for_control(control))
                }
                ETree.SubElement(inputsnode, "input", attrib)

        if containertype is None:
            permitted_containers = self.step_config.xml_find(
                "./permitted-containers")
            if permitted_containers is not None and len(
                    permitted_containers) > 0:
                containertype = permitted_containers[0].text

        if containertype is not None:
            node = ETree.SubElement(root, "container-type")
            node.text = containertype
        else:
            log.warning("No container type specified for step.")

        if reagenttype is None:
            permitted_reagents = self.step_config.xml_find(
                "./permitted-reagent-categories")
            if permitted_reagents is not None and len(permitted_reagents) > 0:
                reagenttype = permitted_reagents[0].text

        if reagenttype is not None:
            node = ETree.SubElement(root, "reagent-category")
            node.text = reagenttype

        step_xml_root = self.lims.request("post",
                                          self.lims.root_uri + "/steps", root)
        step = Step(self.lims,
                    uri=step_xml_root.get("uri"),
                    xml_root=step_xml_root,
                    limsid=step_xml_root.get("limsid"))

        log.info("%s started step %s" % (self.__class__.__name__, step.uri))

        step.wait_for_epp()

        return step
Exemple #22
0
 def special_type(self, value):
     special_type = self.xml_find("./special-type")
     if special_type is None:
         special_type = ETree.SubElement(self.xml_root, "special-type")
     special_type.set("name", value)
Exemple #23
0
 def __setitem__(self, key, value):
     node = self._node_for(key)
     if node is None:
         node = ETree.SubElement(self.top_node, self.subnode_name,
                                 {self.name_attribute: key})
     node.set(self.value_attribute, value)