示例#1
0
def get_qg_without_kryptonite_portion(qg: QueryGraph) -> QueryGraph:
    kryptonite_qedge_keys = [
        qedge_key for qedge_key, qedge in qg.edges.items() if qedge.exclude
    ]
    normal_qedge_keys = set(qg.edges).difference(kryptonite_qedge_keys)
    qnode_keys_used_by_kryptonite_qedges = {
        qnode_key
        for qedge_key in kryptonite_qedge_keys for qnode_key in
        {qg.edges[qedge_key].subject, qg.edges[qedge_key].object}
    }
    qnode_keys_used_by_normal_qedges = {
        qnode_key
        for qedge_key in normal_qedge_keys for qnode_key in
        {qg.edges[qedge_key].subject, qg.edges[qedge_key].object}
    }
    qnode_keys_used_only_by_kryptonite_qedges = qnode_keys_used_by_kryptonite_qedges.difference(
        qnode_keys_used_by_normal_qedges)
    normal_qnode_keys = set(
        qg.nodes).difference(qnode_keys_used_only_by_kryptonite_qedges)
    return QueryGraph(nodes={
        qnode_key: qnode
        for qnode_key, qnode in qg.nodes.items()
        if qnode_key in normal_qnode_keys
    },
                      edges={
                          qedge_key: qedge
                          for qedge_key, qedge in qg.edges.items()
                          if qedge_key in normal_qedge_keys
                      })
示例#2
0
def test_example1():
    query_graph = {
        "edges": {
            "e00": {
                "subject": "n00",
                "object": "n01"
            },
            "e01": {
                "subject": "n00",
                "object": "n01",
                "predicate": "biolink:contraindicated_for",
                "exclude": True
            }
        },
        "nodes": {
            "n00": {
                "id": "MONDO:0001627",
                "category": "biolink:Disease"
            },
            "n01": {
                "category": "biolink:ChemicalSubstance"
            }
        }
    }

    from ARAX_messenger import ARAXMessenger
    response = ARAXResponse()
    messenger = ARAXMessenger()
    messenger.create_envelope(response)

    response.envelope.message.query_graph = QueryGraph().from_dict(query_graph)

    query_graph_info = QueryGraphInfo()
    result = query_graph_info.assess(response.envelope.message)
    response.merge(result)
    if result.status != 'OK':
        print(response.show(level=ARAXResponse.DEBUG))
        return response

    query_graph_info_dict = {
        'n_nodes': query_graph_info.n_nodes,
        'n_edges': query_graph_info.n_edges,
        'is_bifurcated_graph': query_graph_info.is_bifurcated_graph,
        'start_node': query_graph_info.start_node,
        'node_info': query_graph_info.node_info,
        'edge_info': query_graph_info.edge_info,
        'node_order': query_graph_info.node_order,
        'edge_order': query_graph_info.edge_order,
        'node_category_map': query_graph_info.node_category_map,
        'edge_predicate_map': query_graph_info.edge_predicate_map,
    }
    print(
        json.dumps(ast.literal_eval(repr(query_graph_info_dict)),
                   sort_keys=True,
                   indent=2))
示例#3
0
def get_required_portion_of_qg(query_graph: QueryGraph) -> QueryGraph:
    return QueryGraph(nodes={
        qnode_key: qnode
        for qnode_key, qnode in query_graph.nodes.items()
        if not qnode.option_group_id
    },
                      edges={
                          qedge_key: qedge
                          for qedge_key, qedge in query_graph.edges.items()
                          if not qedge.option_group_id
                      })
示例#4
0
def make_qg_use_old_snake_case_types(qg: QueryGraph) -> QueryGraph:
    # This is a temporary patch needed for KPs not yet TRAPI 1.0 compliant
    qg_copy = QueryGraph(nodes={qnode_key: copy_qnode(qnode) for qnode_key, qnode in qg.nodes.items()},
                         edges={qedge_key: copy_qedge(qedge) for qedge_key, qedge in qg.edges.items()})
    for qnode in qg_copy.nodes.values():
        if qnode.category:
            categories = convert_to_list(qnode.category)
            prefixless_categories = [category.split(":")[-1] for category in categories]
            qnode.category = [convert_string_to_snake_case(category) for category in prefixless_categories]
    for qedge in qg_copy.edges.values():
        if qedge.predicate:
            predicates = convert_to_list(qedge.predicate)
            qedge.predicate = [predicate.split(":")[-1] for predicate in predicates]
    return qg_copy
示例#5
0
 def _answer_query_using_plover(
     qg: QueryGraph, log: ARAXResponse
 ) -> Tuple[Dict[str, Dict[str, Set[Union[str, int]]]], int]:
     rtxc = RTXConfiguration()
     rtxc.live = "Production"
     log.debug(f"Sending query to Plover")
     response = requests.post(f"{rtxc.plover_url}/query",
                              json=qg.to_dict(),
                              headers={'accept': 'application/json'})
     if response.status_code == 200:
         log.debug(f"Got response back from Plover")
         return response.json(), response.status_code
     else:
         log.warning(
             f"Plover returned a status code of {response.status_code}. Response was: {response.text}"
         )
         return dict(), response.status_code
示例#6
0
def get_node_pairs_to_overlay(subject_qnode_key: str, object_qnode_key: str, query_graph: QueryGraph,
                              knowledge_graph: KnowledgeGraph, log: ARAXResponse) -> Set[Tuple[str, str]]:
    """
    This function determines which combinations of subject/object nodes in the KG need to be overlayed (e.g., have a
    virtual edge added between). It makes use of Resultify to determine what combinations of subject and object nodes
    may actually appear together in the same Results. (See issue #1069.) If it fails to narrow the node pairs for
    whatever reason, it defaults to returning all possible combinations of subject/object nodes.
    """
    log.debug(f"Narrowing down {subject_qnode_key}--{object_qnode_key} node pairs to overlay")
    kg_nodes_by_qg_id = get_node_ids_by_qg_id(knowledge_graph)
    kg_edges_by_qg_id = get_edge_ids_by_qg_id(knowledge_graph)
    # Grab the portion of the QG already 'expanded' (aka, present in the KG)
    sub_query_graph = QueryGraph(nodes={key:qnode for key, qnode in query_graph.nodes.items() if key in set(kg_nodes_by_qg_id)},
                                 edges={key:qedge for key, qedge in query_graph.edges.items() if key in set(kg_edges_by_qg_id)})

    # Compute results using Resultify so we can see which nodes appear in the same results
    resultifier = ARAXResultify()
    sub_response = ARAXResponse()
    sub_response.envelope = Response()
    sub_response.envelope.message = Message()
    sub_message = sub_response.envelope.message
    sub_message.query_graph = sub_query_graph
    sub_message.knowledge_graph = KnowledgeGraph(nodes=knowledge_graph.nodes.copy(),
                                                 edges=knowledge_graph.edges.copy())
    #sub_response.envelope.message = sub_message
    resultify_response = resultifier.apply(sub_response, {})

    # Figure out which node pairs appear together in one or more results
    if resultify_response.status == 'OK':
        node_pairs = set()
        for result in sub_message.results:
            subject_curies_in_this_result = {node_binding.id for key, node_binding_list in result.node_bindings.items() for node_binding in node_binding_list if
                                            key == subject_qnode_key}
            object_curies_in_this_result = {node_binding.id for key, node_binding_list in result.node_bindings.items() for node_binding in node_binding_list if
                                            key == object_qnode_key}
            pairs_in_this_result = set(itertools.product(subject_curies_in_this_result, object_curies_in_this_result))
            node_pairs = node_pairs.union(pairs_in_this_result)
        log.debug(f"Identified {len(node_pairs)} node pairs to overlay (with help of resultify)")
        if node_pairs:
            return node_pairs
    # Back up to using the old (O(n^2)) method of all combinations of subject/object nodes in the KG
    log.warning(f"Failed to narrow down node pairs to overlay; defaulting to all possible combinations")
    return set(itertools.product(kg_nodes_by_qg_id[subject_qnode_key], kg_nodes_by_qg_id[object_qnode_key]))
示例#7
0
def make_qg_use_old_types(qg: QueryGraph) -> QueryGraph:
    # This is a temporary patch until we switch to KG2.5+
    predicates_with_commas = {"positively_regulates_entity_to_entity": "positively_regulates,_entity_to_entity",
                              "negatively_regulates_entity_to_entity": "negatively_regulates,_entity_to_entity",
                              "positively_regulates_process_to_process": "positively_regulates,_process_to_process",
                              "regulates_process_to_process": "regulates,_process_to_process",
                              "negatively_regulates_process_to_process": "negatively_regulates,_process_to_process"}
    qg_copy = QueryGraph(nodes={qnode_key: copy_qnode(qnode) for qnode_key, qnode in qg.nodes.items()},
                         edges={qedge_key: copy_qedge(qedge) for qedge_key, qedge in qg.edges.items()})
    for qnode in qg_copy.nodes.values():
        if qnode.category:
            categories = convert_to_list(qnode.category)
            prefixless_categories = [category.split(":")[-1] for category in categories]
            qnode.category = [convert_string_to_snake_case(category) for category in prefixless_categories]
    for qedge in qg_copy.edges.values():
        if qedge.predicate:
            predicates = convert_to_list(qedge.predicate)
            prefixless_predicates = [predicate.split(":")[-1] for predicate in predicates]
            qedge.predicate = [predicates_with_commas.get(predicate, predicate) for predicate in prefixless_predicates]
    return qg_copy
示例#8
0
def get_qg_expanded_thus_far(qg: QueryGraph,
                             kg: QGOrganizedKnowledgeGraph) -> QueryGraph:
    expanded_qnodes = {
        qnode_key
        for qnode_key in qg.nodes if kg.nodes_by_qg_id.get(qnode_key)
    }
    expanded_qedges = {
        qedge_key
        for qedge_key in qg.edges if kg.edges_by_qg_id.get(qedge_key)
    }
    qg_expanded_thus_far = QueryGraph(nodes={
        qnode_key: copy.deepcopy(qg.nodes[qnode_key])
        for qnode_key in expanded_qnodes
    },
                                      edges={
                                          qedge_key:
                                          copy.deepcopy(qg.edges[qedge_key])
                                          for qedge_key in expanded_qedges
                                      })
    return qg_expanded_thus_far
示例#9
0
 def _answer_query_using_plover(qg: QueryGraph, log: ARAXResponse) -> Tuple[Dict[str, Dict[str, Union[set, dict]]], int]:
     rtxc = RTXConfiguration()
     rtxc.live = "Production"
     # First prep the query graph (requires some minor additions for Plover)
     dict_qg = qg.to_dict()
     dict_qg["include_metadata"] = True  # Ask plover to return node/edge objects (not just IDs)
     dict_qg["respect_predicate_symmetry"] = True  # Ignore direction for symmetric predicate, enforce for asymmetric
     # Allow subclass_of reasoning for qnodes with a small number of curies
     for qnode in dict_qg["nodes"].values():
         if qnode.get("ids") and len(qnode["ids"]) < 5:
             if "allow_subclasses" not in qnode or qnode["allow_subclasses"] is None:
                 qnode["allow_subclasses"] = True
     # Then send the actual query
     response = requests.post(f"{rtxc.plover_url}/query", json=dict_qg, timeout=60,
                              headers={'accept': 'application/json'})
     if response.status_code == 200:
         log.debug(f"Got response back from Plover")
         return response.json(), response.status_code
     else:
         log.warning(f"Plover returned a status code of {response.status_code}. Response was: {response.text}")
         return dict(), response.status_code
示例#10
0
    def __get_next_free_edge_key(self):

        #### Set up local references to the message and verify the query_graph nodes
        message = self.envelope.message
        if message.query_graph is None:
            message.query_graph = QueryGraph()
            message.query_graph.nodes = {}
            message.query_graph.edges = {}
        if message.query_graph.edges is None:
            message.query_graph.edges = {}
        qedges = message.query_graph.edges

        #### Find the first unused key
        index = 0
        while 1:
            pad = '0'
            if index > 9:
                pad = ''
            potential_edge_key = f"e{pad}{str(index)}"
            if potential_edge_key not in qedges:
                return potential_edge_key
            index += 1
示例#11
0
    def create_envelope(self, response, describe=False):
        """
        Creates a basic empty ARAXResponse object with basic boilerplate metadata
        :return: ARAXResponse object with execution information and the new message object inside the data envelope
        :rtype: ARAXResponse
        """

        # #### Command definition for autogenerated documentation
        command_definition = {
            'dsl_command': 'create_envelope()',
            'description':
            """The `create_envelope` command creates a basic empty Response object with basic boilerplate metadata
                such as reasoner_id, schema_version, etc. filled in. This DSL command takes no arguments. This command is not explicitly
                necessary, as it is called implicitly when needed. e.g. If a DSL program begins with add_qnode(), the
                create_envelope() will be executed automatically if there is not yet a ARAXResponse. If there is already ARAXResponse in memory,
                then this command will destroy the previous one (in memory) and begin a new envelope.""",
            'parameters': {}
        }

        if describe:
            return command_definition

        #### Store the passed response object
        self.response = response

        #### Create the top-level Response object called an envelope
        response.info("Creating an empty template TRAPI Response")
        envelope = Response()
        response.envelope = envelope
        self.envelope = envelope

        # Create a Message object and place it in the envelope
        message = Message()
        response.envelope.message = message
        self.message = message

        #### Fill it with default information
        envelope.id = None
        envelope.type = "translator_reasoner_response"
        envelope.reasoner_id = "ARAX"
        envelope.tool_version = RTXConfiguration().version
        envelope.schema_version = "1.0.0"
        envelope.status = "OK"
        envelope.description = "Created empty template response"
        envelope.context = "https://raw.githubusercontent.com/biolink/biolink-model/master/context.jsonld"
        envelope.logs = response.messages
        envelope.datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        #### Create an empty master knowledge graph
        message.knowledge_graph = KnowledgeGraph()
        message.knowledge_graph.nodes = {}
        message.knowledge_graph.edges = {}

        #### Create an empty query graph
        message.query_graph = QueryGraph()
        message.query_graph.nodes = {}
        message.query_graph.edges = {}

        #### Create empty results
        message.results = []

        #### Return the response
        response.data['envelope'] = envelope
        return response
示例#12
0
    def add_qedge(self, response, input_parameters, describe=False):
        """
        Adds a new QEdge 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_qedge()',
            'description':
            """The `add_qedge` command adds an additional QEdge to the QueryGraph in the Message object. Currently
                subject and object QNodes must already be present in the QueryGraph. The specified type is not currently checked that it is a
                valid Translator/BioLink relationship type, but it should be.""",
            'parameters': {
                'key': {
                    'is_required':
                    False,
                    'examples': ['e00', 'e01'],
                    'default':
                    '',
                    'type':
                    'string',
                    'description':
                    """Any string that is unique among all QEdge key fields, with recommended format e00, e01, e02, etc.
                        If no value is provided, autoincrementing values beginning for e00 are used.""",
                },
                'subject': {
                    'is_required':
                    True,
                    'examples': ['n00', 'n01'],
                    'type':
                    'string',
                    'description':
                    'key of the source QNode already present in the QueryGraph (e.g. n00, n01)',
                },
                'object': {
                    'is_required':
                    True,
                    'examples': ['n01', 'n02'],
                    'type':
                    'string',
                    'description':
                    'key of the target QNode already present in the QueryGraph (e.g. n01, n02)',
                },
                'predicate': {
                    'is_required':
                    False,
                    'examples': [
                        'protein', 'physically_interacts_with',
                        'participates_in'
                    ],
                    'type':
                    'ARAXedge',
                    'description':
                    'Any valid Translator/BioLink relationship predicate (e.g. physically_interacts_with, participates_in)',
                },
                '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.'
                },
                'exclude': {
                    'is_required':
                    False,
                    'enum': ['true', 'false'],
                    'examples': ['true', 'false'],
                    'type':
                    'boolean',
                    'description':
                    'If set to true, results with this node will be excluded. If set to false or not included nodes will be treated as part of a normal query.'
                },
            }
        }

        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,
            'subject': None,
            'object': None,
            'predicate': None,
            'option_group_id': None,
            'exclude': 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
        #### Return if any of the parameters generated an error (showing not just the first one)
        if response.status != 'OK':
            return response

        #### Store these final parameters for convenience
        response.data['parameters'] = parameters
        self.parameters = parameters

        #### 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 QueryEdge to Message with 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.edges is None:
            message.query_graph.edges = {}

        #### Create a QEdge
        qedge = QEdge()
        if parameters['key'] is not None:
            key = parameters['key']
        else:
            key = self.__get_next_free_edge_key()

        #### Get the list of available node_keys
        qnodes = message.query_graph.nodes

        #### Add the subject
        if parameters['subject'] is not None:
            if parameters['subject'] not in qnodes:
                response.error(
                    f"While trying to add QEdge, there is no QNode with key {parameters['subject']}",
                    error_code="UnknownSourceKey")
                return response
            qedge.subject = parameters['subject']
        else:
            response.error(
                f"While trying to add QEdge, subject is a required parameter",
                error_code="MissingSourceKey")
            return response

        #### Add the object
        if parameters['object'] is not None:
            if parameters['object'] not in qnodes:
                response.error(
                    f"While trying to add QEdge, there is no QNode with key {parameters['object']}",
                    error_code="UnknownTargetKey")
                return response
            qedge.object = parameters['object']
        else:
            response.error(
                f"While trying to add QEdge, object is a required parameter",
                error_code="MissingTargetKey")
            return response

        #### Add the predicate if any. Need to verify it's an allowed predicate. FIXME
        if parameters['predicate'] is not None:
            qedge.predicate = parameters['predicate']

        if parameters['exclude'] is not None:
            if parameters['exclude'] in {'t', 'T', 'true', 'True'}:
                qedge.exclude = True
            elif parameters['exclude'] in {'f', 'F', 'false', 'False'}:
                qedge.exclude = False
            elif parameters['exclude'] not in {True, False}:
                response.error(
                    f"Supplied input, {parameters['exclude']}, for the 'exclude' parameter is not valid. Acceptable inputs are t, T, f, F, true, True, false, and False.",
                    error_code="UnknownInput")
        else:
            qedge.exclude = False

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

        #### Add it to the query_graph edge list
        message.query_graph.edges[key] = qedge

        #### Return the response
        return response
示例#13
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
示例#14
0
def test_example1():

    test_query_graphs = [
        {
            "description": "Two nodes, one edge linking them, 1 CURIE",
            "nodes": {
                "n00": {
                    "ids": ["MONDO:0001627"]
                },
                "n01": {
                    "categories": ["biolink:ChemicalEntity"]
                }
            },
            "edges": {
                "e00": {
                    "subject": "n00",
                    "object": "n01",
                    "predicates": ["biolink:physically_interacts_with"]
                }
            }
        },
        {
            "description":
            "Two nodes, two edges linking them, 1 CURIE, one of which is excluded",
            "nodes": {
                "n00": {
                    "ids": ["MONDO:0001627"]
                },
                "n01": {
                    "categories": ["biolink:ChemicalEntity"]
                }
            },
            "edges": {
                "e00": {
                    "subject": "n00",
                    "object": "n01"
                },
                "e01": {
                    "subject": "n00",
                    "object": "n01",
                    "predicates": ["biolink:contraindicated_for"],
                    "exclude": True
                }
            }
        },
        {
            "description":
            "Two nodes, one edge linking them, both nodes are CURIEs",
            "nodes": {
                "n00": {
                    "ids": ["MONDO:0001627"]
                },
                "n01": {
                    "ids": ["CHEMBL.COMPOUND:CHEMBL112"]
                }
            },
            "edges": {
                "e00": {
                    "subject": "n00",
                    "object": "n01"
                }
            }
        },
        {
            "description":
            "Three nodes, 2 edges, 1 CURIE, simple linear chain",
            "nodes": {
                "n00": {
                    "ids": ["MONDO:0001627"]
                },
                "n01": {
                    "categories": ["biolink:ChemicalEntity"]
                },
                "n02": {
                    "categories": ["biolink:Protein"]
                }
            },
            "edges": {
                "e00": {
                    "subject": "n00",
                    "object": "n01",
                    "predicates": ["biolink:physically_interacts_with"]
                },
                "e01": {
                    "subject": "n01",
                    "object": "n02"
                }
            }
        },
        {
            "description":
            "Three nodes, 2 edges, but the CURIE is in the middle. What does that even mean?",
            "nodes": {
                "n00": {
                    "categories": ["biolink:ChemicalEntity"]
                },
                "n01": {
                    "ids": ["MONDO:0001627"]
                },
                "n02": {
                    "categories": ["biolink:Protein"]
                }
            },
            "edges": {
                "e00": {
                    "subject": "n00",
                    "object": "n01",
                    "predicates": ["biolink:physically_interacts_with"]
                },
                "e01": {
                    "subject": "n01",
                    "object": "n02"
                }
            }
        },
        {
            "description": "Four nodes, 3 edges, 1 CURIE, simple linear chain",
            "nodes": {
                "n00": {
                    "ids": ["MONDO:0001627"]
                },
                "n01": {
                    "categories": ["biolink:ChemicalEntity"]
                },
                "n02": {
                    "categories": ["biolink:Protein"]
                },
                "n03": {
                    "categories": ["biolink:Disease"]
                }
            },
            "edges": {
                "e00": {
                    "subject": "n00",
                    "object": "n01",
                    "predicates": ["biolink:physically_interacts_with"]
                },
                "e01": {
                    "subject": "n01",
                    "object": "n02"
                },
                "e02": {
                    "subject": "n02",
                    "object": "n03"
                }
            }
        },
        {
            "description": "Two nodes, one edge linking them, 0 CURIEs",
            "nodes": {
                "n00": {
                    "categories": ["biolink:Drug"]
                },
                "n01": {
                    "categories": ["biolink:ChemicalEntity"]
                }
            },
            "edges": {
                "e00": {
                    "subject": "n00",
                    "object": "n01",
                    "predicates": ["biolink:physically_interacts_with"]
                }
            }
        },
        {
            "description": "One node only",
            "nodes": {
                "n00": {
                    "ids": ["MONDO:0001627"]
                }
            },
            "edges": {}
        },
    ]

    from ARAX_messenger import ARAXMessenger

    for test_query_graph in test_query_graphs:
        response = ARAXResponse()
        messenger = ARAXMessenger()
        messenger.create_envelope(response)

        print(
            '=================================================================='
        )
        description = test_query_graph['description']
        del test_query_graph['description']
        print(f"Query Graph '{description}'")

        response.envelope.message.query_graph = QueryGraph().from_dict(
            test_query_graph)

        query_graph_info = QueryGraphInfo()
        result = query_graph_info.assess(response.envelope.message)
        response.merge(result)
        if result.status != 'OK':
            print(response.show(level=ARAXResponse.DEBUG))
            return response

        query_graph_info_dict = {
            'n_nodes':
            query_graph_info.n_nodes,
            'n_edges':
            query_graph_info.n_edges,
            'is_bifurcated_graph':
            query_graph_info.is_bifurcated_graph,
            'start_node':
            query_graph_info.start_node['key'],
            'simple_query_graph_template':
            query_graph_info.query_graph_templates['simple'],
            #'start_node': query_graph_info.start_node,
            #'node_info': query_graph_info.node_info,
            #'edge_info': query_graph_info.edge_info,
            #'node_order': query_graph_info.node_order,
            #'edge_order': query_graph_info.edge_order,
            #'node_category_map': query_graph_info.node_category_map,
            #'edge_predicate_map': query_graph_info.edge_predicate_map,
        }
        print(
            json.dumps(ast.literal_eval(repr(query_graph_info_dict)),
                       sort_keys=True,
                       indent=2))
示例#15
0
def copy_qg(qg: QueryGraph) -> QueryGraph:
    return QueryGraph(nodes={qnode_key: copy_qnode(qnode) for qnode_key, qnode in qg.nodes.items()},
                      edges={qedge_key: copy_qedge(qedge) for qedge_key, qedge in qg.edges.items()})