示例#1
0
    def add_qnode(self, response, input_parameters, describe=False):
        """
        Adds a new QNode object to the QueryGraph inside the Message object
        :return: ARAXResponse object with execution information
        :rtype: ARAXResponse
        """

        # #### Command definition for autogenerated documentation
        command_definition = {
            'dsl_command': 'add_qnode()',
            'description':
            """The `add_qnode` method adds an additional QNode to the QueryGraph in the Message object.""",
            'parameters': {
                'key': {
                    'is_required':
                    False,
                    'examples': ['n00', 'n01'],
                    'default':
                    '',
                    'type':
                    'string',
                    'description':
                    """Any string that is unique among all QNode key fields, with recommended format n00, n01, n02, etc.
                        If no value is provided, autoincrementing values beginning for n00 are used.""",
                },
                'id': {
                    'is_required':
                    False,
                    'examples':
                    ['DOID:9281', '[UniProtKB:P12345,UniProtKB:Q54321]'],
                    'type':
                    'string',
                    'description':
                    'Any compact URI (CURIE) (e.g. DOID:9281) (May also be a list like [UniProtKB:P12345,UniProtKB:Q54321])',
                },
                'name': {
                    'is_required':
                    False,
                    'examples': ['hypertension', 'insulin'],
                    'type':
                    'string',
                    'description':
                    'Any name of a bioentity that will be resolved into a CURIE if possible or result in an error if not (e.g. hypertension, insulin)',
                },
                'category': {
                    'is_required':
                    False,
                    'examples': ['protein', 'chemical_substance', 'disease'],
                    'type':
                    'ARAXnode',
                    'description':
                    'Any valid Translator bioentity category (e.g. protein, chemical_substance, disease)',
                },
                'is_set': {
                    'is_required':
                    False,
                    'enum':
                    ["true", "false", "True", "False", "t", "f", "T", "F"],
                    'examples': ['true', 'false'],
                    'type':
                    'boolean',
                    'description':
                    'If set to true, this QNode represents a set of nodes that are all in common between the two other linked QNodes (assumed to be false if not specified or value is not recognized as true/t case insensitive)'
                },
                'option_group_id': {
                    'is_required':
                    False,
                    'examples': ['1', 'a', 'b2', 'option'],
                    'type':
                    'string',
                    'description':
                    'A group identifier indicating a group of nodes and edges should either all be included or all excluded. An optional match for all elements in this group. If not included Node will be treated as required.'
                },
            }
        }

        if describe:
            return command_definition

        #### Extract the message to work on
        message = response.envelope.message

        #### Basic checks on arguments
        if not isinstance(input_parameters, dict):
            response.error("Provided parameters is not a dict",
                           error_code="ParametersNotDict")
            return response

        #### Define a complete set of allowed parameters and their defaults
        parameters = {
            'key': None,
            'id': None,
            'name': None,
            'category': None,
            'is_set': None,
            'option_group_id': None,
        }

        #### Loop through the input_parameters and override the defaults and make sure they are allowed
        for key, value in input_parameters.items():
            if key not in parameters:
                response.error(f"Supplied parameter {key} is not permitted",
                               error_code="UnknownParameter")
            else:
                parameters[key] = value

        #### Check for option_group_id and is_set:
        if parameters['option_group_id'] is not None and parameters[
                'id'] is None and parameters['name'] is None:
            if parameters['is_set'] is None:
                parameters['is_set'] = 'true'
                response.warning(
                    f"An 'option_group_id' was set to {parameters['option_group_id']}, but 'is_set' was not an included parameter. It must be true when an 'option_group_id' is given, so automatically setting to true. Avoid this warning by explictly setting to true."
                )
            elif not (parameters['is_set'].lower() == 'true'
                      or parameters['is_set'].lower() == 't'):
                response.error(
                    f"When an 'option_group_id' is given 'is_set' must be set to true. However, supplied input for parameter 'is_set' was {parameters['is_set']}.",
                    error_code="InputMismatch")

        #### Return if any of the parameters generated an error (showing not just the first one)
        if response.status != 'OK':
            return response

        #### Now apply the filters. Order of operations is probably quite important
        #### Scalar value filters probably come first like minimum_confidence, then complex logic filters
        #### based on edge or node properties, and then finally maximum_results
        response.info(
            f"Adding a QueryNode to Message with input parameters {parameters}"
        )

        #### Make sure there's a query_graph already here
        if message.query_graph is None:
            message.query_graph = QueryGraph()
            message.query_graph.nodes = {}
            message.query_graph.edges = {}
        if message.query_graph.nodes is None:
            message.query_graph.nodes = {}

        #### Set up the NodeSynonymizer to find curies and names
        synonymizer = NodeSynonymizer()

        # Create the QNode and set the key
        qnode = QNode()
        if parameters['key'] is not None:
            key = parameters['key']
        else:
            key = self.__get_next_free_node_key()

        if parameters['option_group_id'] is not None:
            qnode.option_group_id = parameters['option_group_id']

        # Set the is_set parameter to what the user selected
        if parameters['is_set'] is not None:
            qnode.is_set = (parameters['is_set'].lower() == 'true'
                            or parameters['is_set'].lower() == 't')

        #### If the id is specified, try to find that
        if parameters['id'] is not None:

            # If the id is a scalar then treat it here as a list of one
            if isinstance(parameters['id'], str):
                id_list = [parameters['id']]
                is_id_a_list = False
                if parameters['is_set'] is not None and qnode.is_set is True:
                    response.error(
                        f"Specified id '{parameters['id']}' is a scalar, but is_set=true, which doesn't make sense",
                        error_code="IdScalarButIsSetTrue")
                    return response

            # Or else set it up as a list
            elif isinstance(parameters['id'], list):
                id_list = parameters['id']
                is_id_a_list = True
                qnode.id = []
                if parameters['is_set'] is None:
                    response.warning(
                        f"Specified id '{parameters['id']}' is a list, but is_set was not set to true. It must be true in this context, so automatically setting to true. Avoid this warning by explictly setting to true."
                    )
                    qnode.is_set = True
                else:
                    if qnode.is_set == False:
                        response.warning(
                            f"Specified id '{parameters['id']}' is a list, but is_set=false, which doesn't make sense, so automatically setting to true. Avoid this warning by explictly setting to true."
                        )
                        qnode.is_set = True

            # Or if it's neither a list or a string, then error out. This cannot be handled at present
            else:
                response.error(
                    f"Specified id '{parameters['id']}' is neither a string nor a list. This cannot to handled",
                    error_code="IdNotListOrScalar")
                return response

            # Loop over the available ids and create the list
            for id in id_list:
                response.debug(f"Looking up id {id} in NodeSynonymizer")
                synonymizer_results = synonymizer.get_canonical_curies(
                    curies=[id])

                # If nothing was found, we won't bail out, but rather just issue a warning that this id is suspect
                if synonymizer_results[id] is None:
                    response.warning(
                        f"A node with id {id} is not in our knowledge graph KG2, but will continue with it"
                    )
                    if is_id_a_list:
                        qnode.id.append(id)
                    else:
                        qnode.id = id

                # And if it is found, keep the same id but report the preferred id
                else:

                    response.info(f"id {id} is found. Adding it to the qnode")
                    if is_id_a_list:
                        qnode.id.append(id)
                    else:
                        qnode.id = id

                if 'category' in parameters and parameters[
                        'category'] is not None:
                    if isinstance(parameters['category'], str):
                        qnode.category = parameters['category']
                    else:
                        qnode.category = parameters['category'][0]

            message.query_graph.nodes[key] = qnode
            return response

        #### If the name is specified, try to find that
        if parameters['name'] is not None:
            name = parameters['name']
            response.debug(
                f"Looking up id for name '{name}' in NodeSynonymizer")
            synonymizer_results = synonymizer.get_canonical_curies(
                curies=[name], names=[name])

            if synonymizer_results[name] is None:
                response.error(
                    f"A node with name '{name}' is not in our knowledge graph",
                    error_code="UnresolvableNodeName")
                return response

            qnode.id = synonymizer_results[name]['preferred_curie']
            response.info(
                f"Creating QueryNode with id '{qnode.id}' for name '{name}'")
            if parameters['category'] is not None:
                qnode.category = parameters['category']
            message.query_graph.nodes[key] = qnode
            return response

        #### If the category is specified, just add that category. There should be checking that it is legal. FIXME
        if parameters['category'] is not None:
            qnode.category = parameters['category']
            if parameters['is_set'] is not None:
                qnode.is_set = (parameters['is_set'].lower() == 'true')
            message.query_graph.nodes[key] = qnode
            return response

        #### If we get here, it means that all three main parameters are null. Just a generic node with no category or anything. This is okay.
        message.query_graph.nodes[key] = qnode
        return response
示例#2
0
def copy_qnode(old_qnode: QNode) -> QNode:
    new_qnode = QNode()
    for node_property in new_qnode.to_dict():
        value = getattr(old_qnode, node_property)
        setattr(new_qnode, node_property, value)
    return new_qnode