Esempio n. 1
0
class MockWrappedXml(WrappedXml):
    """
    Class to host test properties
    """

    # Attribute Properties
    root_attribute = attribute_property("root-attr")
    root_attribute_as_number = attribute_property("root-attr", types.NUMERIC)
    missing_attribute = attribute_property("missing-attribute")

    # Subnode Properties
    subnode = subnode_property("sub_node")
    missing_subnode = subnode_property("missing-subnode")

    # Subnode Link Properties
    subnode_link_read_only = subnode_link(WrappedXml,
                                          "link_node",
                                          readonly=True)
    subnode_link = subnode_link(WrappedXml, "link_node")

    subnode_links = subnode_links(WrappedXml, "link_node")

    # Subnode Proptery Literal Dictionary
    # # prop_name, subprop_name, name_attribute='name', value_attribute='value', readonly=False):
    subnode_property_literal_dict_instance = subnode_property_literal_dict(
        "dict", "dict_entry")
    subnode_property_literal_dict__alt_names_instance = subnode_property_literal_dict(
        "dict",
        "dict_entry",
        name_attribute="alt_name",
        value_attribute="alt_val")

    subnode_property_dict_instance = subnode_property_dict(
        "dict", as_attributes=("name", "value"))
Esempio n. 2
0
class ContainerDimension(WrappedXml):
    is_alpha = subnode_property("is-alpha", types.BOOLEAN)
    offset = subnode_property("offset", types.NUMERIC)  # type: float
    size = subnode_property("size", types.NUMERIC)  # type: float

    @lazy_property
    def dimension_range(self):
        """
        List of the labels for the given dimension (row or column)

        :return: list[int]|list[str]
        """

        # Cast these to integers from floats to avoid deprecation warnings from range.
        start = int(self.offset)
        end = int(self.offset + self.size)

        if not self.is_alpha:
            return list(range(start, end))
        else:
            return list(map(chr, range(65 + start, 65 + end)))

    def as_index(self, label):
        if label.isdigit():
            return int(label) - int(self.offset)
        else:
            return ord(label[0]) - 65 - int(self.offset)

    def as_label(self, index):
        if self.is_alpha:
            return chr(65 + index + int(self.offset))
        else:
            return str(index + int(self.offset))
class Automation(s4.clarity._internal.ClarityElement):
    UNIVERSAL_TAG = "{http://genologics.com/ri/automation}automation"

    name = attribute_property('name')
    context = subnode_property('context')
    command_string = subnode_property('string')
    channel = subnode_property('channel')

    process_types = subnode_links(ProcessType, "process-type", "process-types")
Esempio n. 4
0
class Project(FieldsMixin, ClarityElement):
    UNIVERSAL_TAG = "{http://genologics.com/ri/project}project"
    ATTACH_TO_NAME = "Project"

    open_date = subnode_property("open-date", types.DATE)
    close_date = subnode_property("close-date", types.DATE)
    invoice_date = subnode_property("invoice-date", types.DATE)
    researcher = subnode_link(Researcher, "researcher",
                              attributes=('uri', ))  # type: Researcher
Esempio n. 5
0
class Container(FieldsMixin, ClarityElement):
    UNIVERSAL_TAG = "{http://genologics.com/ri/container}container"
    ATTACH_TO_NAME = "Container"

    container_type = subnode_link(ContainerType,
                                  "type",
                                  attributes=('name', 'uri'))
    occupied_wells = subnode_property("occupied-wells",
                                      typename=types.NUMERIC,
                                      readonly=True)
    state = subnode_property("state", typename=types.STRING, readonly=True)

    @property
    def type_name(self):
        """
        Read-only shortcut to containertype name, which we know without doing another GET.

        :type: str
        """
        typenode = self.xml_find('./type')
        return typenode.get('name')

    @property
    def placements(self):
        """
        Dict of string "Y:X" -> Artifacts.

        :type: dict[str, Artifact]
        """
        return self.xml_all_as_dict(
            "placement",
            lambda n: n.find("value").text,  # key
            lambda n: self.lims.artifacts.from_link_node(n)  # value
        )

    def artifact_at(self, well):
        """
        :param well: String matching "Y:X" where Y is a column index and X is a row index.
            The string may use letters or numbers depending on the container type.
        :type well: str

        :rtype: Artifact or None
        """
        try:
            return self.placements[well]
        except KeyError:
            raise KeyError("Container '%s' has no artifact at '%s'." %
                           (self.name, well))
class ProcessTemplate(s4.clarity._internal.FieldsMixin,
                      s4.clarity._internal.ClarityElement):
    UNIVERSAL_TAG = "{http://genologics.com/ri/processtemplate}process-template"

    type = subnode_link(ProcessType, 'type', attributes=('uri', ))

    is_default = subnode_property('is-default', types.BOOLEAN)
Esempio n. 7
0
class Udf(ClarityElement):
    UNIVERSAL_TAG = "{http://genologics.com/ri/configuration}udfconfig"
    CREATION_TAG = "{http://genologics.com/ri/configuration}field"

    # Alternate name to avoid collision with built-in 'type'
    field_type = attribute_property("type")  # type: str
    attach_to_name = subnode_property("attach-to-name")  # type: str
    show_in_lablink = subnode_property("show-in-lablink",
                                       types.BOOLEAN)  # type: bool
    allow_non_preset_values = subnode_property("allow-non-preset-values",
                                               types.BOOLEAN)  # type: bool
    first_preset_is_default_value = subnode_property(
        "first-preset-is-default-value", types.BOOLEAN)  # type: bool
    show_in_tables = subnode_property("show-in-tables",
                                      types.BOOLEAN)  # type: bool
    is_editable = subnode_property("is-editable", types.BOOLEAN)  # type: bool
    is_deviation = subnode_property("is-deviation",
                                    types.BOOLEAN)  # type: bool
    is_controlled_vocabulary = subnode_property("is-controlled-vocabulary",
                                                types.BOOLEAN)  # type: bool
    is_required = subnode_property("is-required", types.BOOLEAN)  # type: bool
    attach_to_category = subnode_property("attach-to-category")  # type: str

    # Only valid for Numeric types
    min_value = subnode_property("min-value", types.NUMERIC)  # type: float
    max_value = subnode_property("max-value", types.NUMERIC)  # type: float
    precision = subnode_property("precision", types.NUMERIC)  # type: float

    @property
    def presets(self):
        """
        :type: list
        """
        preset_nodes = self.xml_root.findall('preset')
        return [
            types.clarity_string_to_obj(self.field_type, preset_node.text)
            for preset_node in preset_nodes
        ]

    def add_preset(self, new_preset_value):
        """
        Add a new preset value to the end of the list. Ignores values that are already present.

        :type new_preset_value: str|unicode|int|float|datetime.date|bool
        :param new_preset_value: the preset value to add, with a type appropriate to the UDF. The value is not
            validated to be the correct type.
        """
        preset = self._find_preset_by_value(new_preset_value)

        if preset is not None:
            return

        self._add_preset_internal(new_preset_value)

    def remove_preset(self, preset_value):
        """
        Remove a preset value from the list.

        :type preset_value: str|unicode|int|float|datetime.date|bool
        :param preset_value: the preset value to remove, with a type appropriate to the UDF. The value is
            not validated to be the correct type.
        """
        preset = self._find_preset_by_value(preset_value)

        if preset is not None:
            self.xml_root.remove(preset)

    def set_default_preset(self, default_preset_value):
        """
        Sets a preset value as the default (puts first in the list). Adds value if it isn't already preset.

        :type default_preset_value: str|unicode|int|float|datetime.date|bool
        :param default_preset_value: the new default preset value, with a type appropriate to the UDF. The value is
            not validated to be the correct type.
        :raises Exception: if the udf's first-preset-is-default property is currently false
        """
        if not self.first_preset_is_default_value:
            raise Exception(
                "Setting the default value will have no effect, as first-preset-is-default-value is false."
            )

        current_preset_nodes = self.xml_findall('preset')

        # Initialize the new list of presets with the default
        new_preset_values = [types.obj_to_clarity_string(default_preset_value)]

        for preset in current_preset_nodes:
            # Only grab values other than the new default, in case it was already in there
            if types.clarity_string_to_obj(
                    self.field_type, preset.text) != default_preset_value:
                new_preset_values.append(preset.text)

            self.xml_root.remove(preset)

        for preset_value in new_preset_values:
            self._add_preset_internal(preset_value)

    def _find_preset_by_value(self, preset_value):
        all_presets = self.xml_root.findall("preset")

        for preset in all_presets:
            if types.clarity_string_to_obj(self.field_type,
                                           preset.text) == preset_value:
                return preset

    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)
Esempio n. 8
0
class ContainerType(ClarityElement):
    UNIVERSAL_TAG = "{http://genologics.com/ri/containertype}container-type"

    is_tube = subnode_property("is-tube", types.BOOLEAN)  # type: bool
    x_dimension = subnode_element(ContainerDimension,
                                  "x-dimension")  # type: ContainerDimension
    y_dimension = subnode_element(ContainerDimension,
                                  "y-dimension")  # type: ContainerDimension

    def well_to_rc(self, well):
        """
        Converts a Clarity well position to the zero based index of the row and column.

        Example::

             'B:4' -> (1, 3)

        :param well: A Clarity formatted well position
        :type well: str
        :return: The zero based index of the row and the column.
        :rtype: tuple[int]
        """
        location_pieces = well.split(":")

        return self.y_dimension.as_index(
            location_pieces[0]), self.x_dimension.as_index(location_pieces[1])

    def rc_to_well(self, rc):
        """
        Converts a zero based index of the row and column to a Clarity well position.

        Example::

             (1, 3) -> 'B:4'

        :param rc: The zero based index of the row and the column.
        :type rc: tuple[int]
        :return: A Clarity formatted well position
        :rtype: str
        """
        return "%s:%s" % (self.y_dimension.as_label(
            rc[0]), self.x_dimension.as_label(rc[1]))

    def row_order_wells(self):
        """
        Wells in row order, e.g., A:1, B:1, ...
        Unavailable wells are omitted

        :rtype: list[str]
        """
        l = []
        for y in self.y_dimension.dimension_range:
            for x in self.x_dimension.dimension_range:
                well_name = "%s:%s" % (str(y), str(x))

                if well_name not in self.unavailable_wells:
                    l.append(well_name)
        return l

    def column_order_wells(self):
        """
        Wells in column order, e.g., A:1, A:2, ...
        Unavailable wells are omitted

        :rtype: list[str]
        """
        l = []
        for x in self.x_dimension.dimension_range:
            for y in self.y_dimension.dimension_range:
                well_name = "%s:%s" % (str(y), str(x))

                if well_name not in self.unavailable_wells:
                    l.append(well_name)
        return l

    @lazy_property
    def unavailable_wells(self):
        """
        :type: set[str]
        """
        unavailable_well_nodes = self.xml_findall("unavailable-well")
        return set(node.text for node in unavailable_well_nodes)

    @lazy_property
    def total_capacity(self):
        """
        :type: int
        """
        return len(self.x_dimension.dimension_range) * len(
            self.y_dimension.dimension_range) - len(self.unavailable_wells)

    def x_dimension_range(self):
        """
        :deprecated: use :class:`ContainerType.x_dimension.dimension_range` instead
        """
        return self.x_dimension.dimension_range

    def y_dimension_range(self):
        """
        :deprecated: use :class:`ContainerType.y_dimension.dimension_range` instead
        """
        return self.y_dimension.dimension_range
Esempio n. 9
0
class StepConfiguration(ClarityElement):
    UNIVERSAL_TAG = "{http://genologics.com/ri/protocolconfiguration}step"

    def __init__(self, protocol, node):
        """
        :type protocol: Protocol
        """
        super(StepConfiguration, self).__init__(protocol.lims, uri=None, xml_root=node)
        self.protocol = protocol

    properties = subnode_property_literal_dict('step-properties', 'step-property')
    protocol_step_index = subnode_property("protocol-step-index", types.NUMERIC)
    queue_fields = subnode_element_list(ProtocolStepField, "queue-fields", "queue-field")
    step_fields = subnode_element_list(ProtocolStepField, "step-fields", "step-field")
    sample_fields = subnode_element_list(ProtocolStepField, "sample-fields", "sample-field")
    triggers = subnode_property_list_of_dicts('epp-triggers/epp-trigger', as_attributes=[
        'status', 'point', 'type', 'name'
    ])
    transitions = subnode_property_list_of_dicts('transitions/transition', as_attributes=[
        'name', 'sequence', 'next-step-uri'
    ], order_by=lambda x: int(x.get('sequence')))
    
    def refresh(self):
        """
        :raise Exception: Unable to refresh step directly, use protocol
        """
        # FIXME?
        raise Exception("Unable to refresh step directly, use protocol")

    def put_and_parse(self, alternate_uri=None):
        self.protocol.put_and_parse(alternate_uri)

    def post_and_parse(self, alternate_uri=None):
        self.protocol.post_and_parse(alternate_uri)

    @lazy_property
    def process_type(self):
        """
        :type: ProcessType
        """
        pt_display_name = self.get_node_text('process-type')

        results = self.lims.process_types.query(displayname=pt_display_name)
        if results:
            return results[0]
        else:
            raise Exception("Process type '%s' not found in Clarity", pt_display_name)

    @lazy_property
    def required_reagent_kits(self):
        """
        :type: ReagentKit
        """
        reagent_kits = self.xml_findall("./required-reagent-kits/reagent-kit")
        return [ReagentKit(self.lims, p.get("uri")) for p in reagent_kits]

    @lazy_property
    def permitted_control_types(self):
        """
        :type: ControlType
        """
        control_types = self.xml_findall("./permitted-control-types/control-type")
        return [ControlType(self.lims, p.get("uri")) for p in control_types]

    @lazy_property
    def queue(self):
        """
        :type: Queue
        """
        return self.lims.queues.from_limsid(self.limsid)
Esempio n. 10
0
class SubNode(WrappedXml):
    node_one = subnode_property("node_one")
    node_two = subnode_property("node_two")