def add_chatroom():
    response = Response()
    try:

        add_data = request.get_json()

        # check required parament
        add_data["access_token"]
        add_data["chatroom_name"]
        add_data["chatroom_image_base64"]

        result = model.add_chatroom(add_data["access_token"],
                                    add_data["chatroom_name"],
                                    add_data["chatroom_image_base64"])
        if result == None:
            raise Exception(result)
        response.success(result)

    except KeyError as e:
        response.error("KeyError, maybe missing parameter: " + e.args[0])

    except Exception as e:
        response.error(str(e))

    return response.get_json()
Example #2
0
    def apply(self, input_message: Message,
              input_parameters: dict) -> Response:

        # Define a default response
        response = Response()
        self.response = response
        self.message = input_message

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

        # 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'] = input_parameters
        self.parameters = input_parameters

        response.debug(
            f"Applying Resultifier to Message with parameters {input_parameters}"
        )

        # call _resultify
        self._resultify(describe=False)

        # Clean up the KG (should only contain nodes used in the results)
        self._clean_up_kg()

        # Return the response and done
        return response
Example #3
0
def get_preferred_curies(curie: Union[str, List[str]],
                         log: Response) -> Dict[str, Dict[str, str]]:
    curies = convert_string_or_list_to_list(curie)
    try:
        synonymizer = NodeSynonymizer()
        log.debug(
            f"Sending NodeSynonymizer.get_canonical_curies() a list of {len(curies)} curies"
        )
        canonical_curies_dict = synonymizer.get_canonical_curies(curies)
        log.debug(f"Got response back from NodeSynonymizer")
    except Exception:
        tb = traceback.format_exc()
        error_type, error, _ = sys.exc_info()
        log.error(f"Encountered a problem using NodeSynonymizer: {tb}",
                  error_code=error_type.__name__)
        return {}
    else:
        if canonical_curies_dict is not None:
            unrecognized_curies = {
                input_curie
                for input_curie in canonical_curies_dict
                if not canonical_curies_dict.get(input_curie)
            }
            if unrecognized_curies:
                log.warning(
                    f"NodeSynonymizer did not return canonical info for: {unrecognized_curies}"
                )
            return canonical_curies_dict
        else:
            log.error(f"NodeSynonymizer returned None",
                      error_code="NodeNormalizationIssue")
            return {}
Example #4
0
    def apply(self, input_message, input_parameters):

        #### Define a default response
        response = Response()
        self.response = response
        self.message = input_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 = {
            'maximum_results': None,
            'minimum_confidence': None,
            'start_node': 1
        }

        #### 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.debug(f"Applying filter to Message with parameters {parameters}")

        #### First, as a test, blow away the results and see if we can recompute them
        #message.n_results = 0
        #message.results = []
        #self.__recompute_results()

        #### Apply scalar value filters first to do easy things and reduce the problem
        # TODO

        #### Complex logic filters probably come next. These may be hard
        # TODO

        #### Finally, if the maximum_results parameter is set, then limit the number of results to that last
        if parameters['maximum_results'] is not None:
           self.__apply_maximum_results_filter(parameters['maximum_results'])

        #### Return the response
        return response
def get_message():
    response = Response()
    chatroom_secret = request.args.get('chatroom_secret')
    messages = model.get_message_by_secret(chatroom_secret)
    if messages != None:
        response.success(messages)
        return response.get_json()
    else:
        response.error("invalid chatroom_secret")
        return response.get_json()
def get_chatroom_info():
    response = Response()
    chatroom_secret = request.args.get('chatroom_secret')
    chatroom = model.get_chatroominfo_by_secret(chatroom_secret)
    if chatroom != None:
        response.success(chatroom)
        return response.get_json()
    else:
        response.error("invalid chatroom_secret")
        return response.get_json()
def add_account():
    response = Response()
    access_token = request.args.get('facebook_token')
    result = model.add_account(access_token)
    if result != None:
        response.success()
        return response.get_json()
    else:
        response.error("invalid token")
        return response.get_json()
def check_token():
    response = Response()
    access_token = request.args.get('facebook_token')
    user = model.get_facebook_user_info(access_token)
    if user != None:
        response.success()
        return response.get_json()
    else:
        response.error("invalid token")
        return response.get_json()
Example #9
0
def main():

    # Note that most of this is just manually doing what ARAXQuery() would normally do for you
    response = Response()
    from actions_parser import ActionsParser
    actions_parser = ActionsParser()
    actions_list = [
        "create_message",
        "add_qnode(id=n00, curie=CHEMBL.COMPOUND:CHEMBL112)",  # acetaminophen
        "add_qnode(id=n01, type=protein, is_set=true)",
        "add_qedge(id=e00, source_id=n00, target_id=n01)",
        "expand(edge_id=e00, kp=BTE)",
        "return(message=true, store=false)",
    ]

    # Parse the raw action_list into commands and parameters
    result = actions_parser.parse(actions_list)
    response.merge(result)
    if result.status != 'OK':
        print(response.show(level=Response.DEBUG))
        return response
    actions = result.data['actions']

    from ARAX_messenger import ARAXMessenger
    messenger = ARAXMessenger()
    expander = ARAXExpander()
    for action in actions:
        if action['command'] == 'create_message':
            result = messenger.create_message()
            message = result.data['message']
            response.data = result.data
        elif action['command'] == 'add_qnode':
            result = messenger.add_qnode(message, action['parameters'])
        elif action['command'] == 'add_qedge':
            result = messenger.add_qedge(message, action['parameters'])
        elif action['command'] == 'expand':
            result = expander.apply(message, action['parameters'])
        elif action['command'] == 'return':
            break
        else:
            response.error(f"Unrecognized command {action['command']}",
                           error_code="UnrecognizedCommand")
            print(response.show(level=Response.DEBUG))
            return response

        # Merge down this result and end if we're in an error state
        response.merge(result)
        if result.status != 'OK':
            print(response.show(level=Response.DEBUG))
            return response

    # Show the final response
    # print(json.dumps(ast.literal_eval(repr(message.knowledge_graph)),sort_keys=True,indent=2))
    print(response.show(level=Response.DEBUG))
Example #10
0
    def _convert_one_hop_query_graph_to_cypher_query(self, query_graph: QueryGraph, enforce_directionality: bool,
                                                     kp: str, log: Response) -> str:
        log.debug(f"Generating cypher for edge {query_graph.edges[0].id} query graph")
        try:
            # Build the match clause
            qedge = query_graph.edges[0]
            source_qnode = eu.get_query_node(query_graph, qedge.source_id)
            target_qnode = eu.get_query_node(query_graph, qedge.target_id)
            qedge_cypher = self._get_cypher_for_query_edge(qedge, enforce_directionality)
            source_qnode_cypher = self._get_cypher_for_query_node(source_qnode)
            target_qnode_cypher = self._get_cypher_for_query_node(target_qnode)
            match_clause = f"MATCH {source_qnode_cypher}{qedge_cypher}{target_qnode_cypher}"

            # Build the where clause
            where_fragments = []
            for qnode in [source_qnode, target_qnode]:
                if qnode.curie:
                    if type(qnode.curie) is str:
                        node_id_where_fragment = f"{qnode.id}.id='{qnode.curie}'"
                    else:
                        node_id_where_fragment = f"{qnode.id}.id in {qnode.curie}"
                    where_fragments.append(node_id_where_fragment)
                if qnode.type and isinstance(qnode.type, list):
                    if "KG2" in kp:
                        node_type_property = "category_label"
                    else:
                        node_type_property = "category"
                    where_fragments.append(f"{qnode.id}.{node_type_property} in {qnode.type}")
            if where_fragments:
                where_clause = f"WHERE {' AND '.join(where_fragments)}"
            else:
                where_clause = ""

            # Build the with clause
            source_qnode_col_name = f"nodes_{source_qnode.id}"
            target_qnode_col_name = f"nodes_{target_qnode.id}"
            qedge_col_name = f"edges_{qedge.id}"
            # This line grabs the edge's ID and a record of which of its nodes correspond to which qnode ID
            extra_edge_properties = "{.*, " + f"id:ID({qedge.id}), {source_qnode.id}:{source_qnode.id}.id, {target_qnode.id}:{target_qnode.id}.id" + "}"
            with_clause = f"WITH collect(distinct {source_qnode.id}) as {source_qnode_col_name}, " \
                          f"collect(distinct {target_qnode.id}) as {target_qnode_col_name}, " \
                          f"collect(distinct {qedge.id}{extra_edge_properties}) as {qedge_col_name}"

            # Build the return clause
            return_clause = f"RETURN {source_qnode_col_name}, {target_qnode_col_name}, {qedge_col_name}"

            cypher_query = f"{match_clause} {where_clause} {with_clause} {return_clause}"
            return cypher_query
        except Exception:
            tb = traceback.format_exc()
            error_type, error, _ = sys.exc_info()
            log.error(f"Problem generating cypher for query. {tb}", error_code=error_type.__name__)
            return ""
Example #11
0
 def _answer_one_hop_query_using_neo4j(self, cypher_query: str, qedge_id: str, kp: str, continue_if_no_results: bool,
                                       log: Response) -> List[Dict[str, List[Dict[str, any]]]]:
     log.info(f"Sending cypher query for edge {qedge_id} to {kp} neo4j")
     results_from_neo4j = self._run_cypher_query(cypher_query, kp, log)
     if log.status == 'OK':
         columns_with_lengths = dict()
         for column in results_from_neo4j[0]:
             columns_with_lengths[column] = len(results_from_neo4j[0].get(column))
         if any(length == 0 for length in columns_with_lengths.values()):
             if continue_if_no_results:
                 log.warning(f"No paths were found in {kp} satisfying this query graph")
             else:
                 log.error(f"No paths were found in {kp} satisfying this query graph", error_code="NoResults")
     return results_from_neo4j
Example #12
0
 def _run_arax_query(actions_list: List[str],
                     log: Response) -> DictKnowledgeGraph:
     araxq = ARAXQuery()
     sub_query_response = araxq.query({
         "previous_message_processing_plan": {
             "processing_actions": actions_list
         }
     })
     if sub_query_response.status != 'OK':
         log.error(
             f"Encountered an error running ARAXQuery within Expand: {sub_query_response.show(level=sub_query_response.DEBUG)}"
         )
         return dict()
     sub_query_message = araxq.message
     return sub_query_message.knowledge_graph
Example #13
0
 def _run_cypher_query(cypher_query: str, kp: str, log: Response) -> List[Dict[str, any]]:
     rtxc = RTXConfiguration()
     if kp == "KG2":  # Flip into KG2 mode if that's our KP (rtx config is set to KG1 info by default)
         rtxc.live = "KG2"
     try:
         driver = GraphDatabase.driver(rtxc.neo4j_bolt, auth=(rtxc.neo4j_username, rtxc.neo4j_password))
         with driver.session() as session:
             query_results = session.run(cypher_query).data()
         driver.close()
     except Exception:
         tb = traceback.format_exc()
         error_type, error, _ = sys.exc_info()
         log.error(f"Encountered an error interacting with {kp} neo4j. {tb}", error_code=error_type.__name__)
         return []
     else:
         return query_results
Example #14
0
 def request(self, action, xml):
     try:
         return Response(self.client.send(action, xml), action, True)
     except SocketError as e:
         if e.errno == errno.ECONNRESET:
             response_error = Response("ECONNRESET", action)
             response_error.error, response_error.error_msg = "ECONNRESET", "[Errno 104] Connection reset by peer"
             return response_error
         else:
             raise
Example #15
0
 def _log_proper_no_results_message(accepted_curies: Set[str],
                                    continue_if_no_results: bool,
                                    valid_prefixes: Set[str],
                                    log: Response):
     if continue_if_no_results:
         if not accepted_curies:
             log.warning(
                 f"BTE could not accept any of the input curies. Valid curie prefixes for BTE are: "
                 f"{valid_prefixes}")
         log.warning(
             f"No paths were found in BTE satisfying this query graph")
     else:
         if not accepted_curies:
             log.error(
                 f"BTE could not accept any of the input curies. Valid curie prefixes for BTE are: "
                 f"{valid_prefixes}",
                 error_code="InvalidPrefix")
         log.error(
             f"No paths were found in BTE satisfying this query graph",
             error_code="NoResults")
Example #16
0
 def _answer_query_using_bte(
         self, input_qnode: QNode, output_qnode: QNode, qedge: QEdge,
         answer_kg: DictKnowledgeGraph,
         valid_bte_inputs_dict: Dict[str, Set[str]],
         log: Response) -> Tuple[DictKnowledgeGraph, Set[str]]:
     accepted_curies = set()
     # Send this single-edge query to BTE, input curie by input curie (adding findings to our answer KG as we go)
     for curie in input_qnode.curie:
         # Consider all different combinations of qnode types (can be multiple if gene/protein)
         for input_qnode_type, output_qnode_type in itertools.product(
                 input_qnode.type, output_qnode.type):
             if eu.get_curie_prefix(
                     curie) in valid_bte_inputs_dict['curie_prefixes']:
                 accepted_curies.add(curie)
                 try:
                     loop = asyncio.new_event_loop()
                     seqd = SingleEdgeQueryDispatcher(
                         input_cls=input_qnode_type,
                         output_cls=output_qnode_type,
                         pred=qedge.type,
                         input_id=eu.get_curie_prefix(curie),
                         values=eu.get_curie_local_id(curie),
                         loop=loop)
                     log.debug(
                         f"Sending query to BTE: {curie}-{qedge.type if qedge.type else ''}->{output_qnode_type}"
                     )
                     seqd.query()
                     reasoner_std_response = seqd.to_reasoner_std()
                 except Exception:
                     trace_back = traceback.format_exc()
                     error_type, error, _ = sys.exc_info()
                     log.error(
                         f"Encountered a problem while using BioThings Explorer. {trace_back}",
                         error_code=error_type.__name__)
                     return answer_kg, accepted_curies
                 else:
                     answer_kg = self._add_answers_to_kg(
                         answer_kg, reasoner_std_response, input_qnode.id,
                         output_qnode.id, qedge.id, log)
     return answer_kg, accepted_curies
Example #17
0
def get_curie_synonyms(curie: Union[str, List[str]],
                       log: Response) -> List[str]:
    curies = convert_string_or_list_to_list(curie)
    try:
        synonymizer = NodeSynonymizer()
        log.debug(
            f"Sending NodeSynonymizer.get_equivalent_nodes() a list of {len(curies)} curies"
        )
        equivalent_curies_dict = synonymizer.get_equivalent_nodes(
            curies, kg_name="KG2")
        log.debug(f"Got response back from NodeSynonymizer")
    except Exception:
        tb = traceback.format_exc()
        error_type, error, _ = sys.exc_info()
        log.error(f"Encountered a problem using NodeSynonymizer: {tb}",
                  error_code=error_type.__name__)
        return []
    else:
        if equivalent_curies_dict is not None:
            curies_missing_info = {
                curie
                for curie in equivalent_curies_dict
                if not equivalent_curies_dict.get(curie)
            }
            if curies_missing_info:
                log.warning(
                    f"NodeSynonymizer did not find any equivalent curies for: {curies_missing_info}"
                )
            equivalent_curies = {
                curie
                for curie_dict in equivalent_curies_dict.values() if curie_dict
                for curie in curie_dict
            }
            all_curies = equivalent_curies.union(set(
                curies))  # Make sure even curies without synonyms are included
            return sorted(list(all_curies))
        else:
            log.error(f"NodeSynonymizer returned None",
                      error_code="NodeNormalizationIssue")
            return []
Example #18
0
    def apply(self, input_message, input_parameters, response=None):

        #### Define a default response
        if response is None:
            response = Response()
        self.response = response
        self.message = input_message

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

        # list of actions that have so far been created for ARAX_overlay
        allowable_actions = self.allowable_actions

        # check to see if an action is actually provided
        if 'action' not in input_parameters:
            response.error(
                f"Must supply an action. Allowable actions are: action={allowable_actions}",
                error_code="MissingAction")
        elif input_parameters['action'] not in allowable_actions:
            response.error(
                f"Supplied action {input_parameters['action']} is not permitted. Allowable actions are: {allowable_actions}",
                error_code="UnknownAction")

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

        # populate the parameters dict
        parameters = dict()
        for key, value in input_parameters.items():
            parameters[key] = value

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

        # convert the action string to a function call (so I don't need a ton of if statements
        getattr(
            self, '_' + self.__class__.__name__ + '__' + parameters['action']
        )(
        )  # thank you https://stackoverflow.com/questions/11649848/call-methods-by-string

        response.debug(
            f"Applying Overlay to Message with parameters {parameters}"
        )  # TODO: re-write this to be more specific about the actual action

        # TODO: add_pubmed_ids
        # TODO: compute_confidence_scores
        # TODO: finish COHD
        # TODO: Jaccard

        #### Return the response and done
        if self.report_stats:  # helper to report information in debug if class self.report_stats = True
            response = self.report_response_stats(response)
        return response
Example #19
0
    def _expand_node(self, qnode_id: str, kp_to_use: str,
                     continue_if_no_results: bool, query_graph: QueryGraph,
                     use_synonyms: bool, synonym_handling: str,
                     log: Response) -> DictKnowledgeGraph:
        # This function expands a single node using the specified knowledge provider
        log.debug(f"Expanding node {qnode_id} using {kp_to_use}")
        query_node = eu.get_query_node(query_graph, qnode_id)
        answer_kg = DictKnowledgeGraph()
        if log.status != 'OK':
            return answer_kg
        if not query_node.curie:
            log.error(
                f"Cannot expand a single query node if it doesn't have a curie",
                error_code="InvalidQuery")
            return answer_kg
        copy_of_qnode = eu.copy_qnode(query_node)

        if use_synonyms:
            self._add_curie_synonyms_to_query_nodes(qnodes=[copy_of_qnode],
                                                    log=log,
                                                    kp=kp_to_use)
        if copy_of_qnode.type in ["protein", "gene"]:
            copy_of_qnode.type = ["protein", "gene"]
        log.debug(f"Modified query node is: {copy_of_qnode.to_dict()}")

        # Answer the query using the proper KP
        valid_kps_for_single_node_queries = ["ARAX/KG1", "ARAX/KG2"]
        if kp_to_use in valid_kps_for_single_node_queries:
            from Expand.kg_querier import KGQuerier
            kg_querier = KGQuerier(log, kp_to_use)
            answer_kg = kg_querier.answer_single_node_query(copy_of_qnode)
            log.info(
                f"Query for node {copy_of_qnode.id} returned results ({eu.get_printable_counts_by_qg_id(answer_kg)})"
            )

            # Make sure all qnodes have been fulfilled (unless we're continuing if no results)
            if log.status == 'OK' and not continue_if_no_results:
                if copy_of_qnode.id not in answer_kg.nodes_by_qg_id or not answer_kg.nodes_by_qg_id[
                        copy_of_qnode.id]:
                    log.error(
                        f"Returned answer KG does not contain any results for QNode {copy_of_qnode.id}",
                        error_code="UnfulfilledQGID")
                    return answer_kg

            if synonym_handling != 'add_all':
                answer_kg, edge_node_usage_map = self._deduplicate_nodes(
                    dict_kg=answer_kg, edge_to_nodes_map={}, log=log)
            return answer_kg
        else:
            log.error(
                f"Invalid knowledge provider: {kp_to_use}. Valid options for single-node queries are "
                f"{', '.join(valid_kps_for_single_node_queries)}",
                error_code="InvalidKP")
            return answer_kg
Example #20
0
 def _verify_one_hop_query_graph_is_valid(query_graph: QueryGraph,
                                          log: Response):
     if len(query_graph.edges) != 1:
         log.error(
             f"answer_one_hop_query() was passed a query graph that is not one-hop: "
             f"{query_graph.to_dict()}",
             error_code="InvalidQuery")
     elif len(query_graph.nodes) > 2:
         log.error(
             f"answer_one_hop_query() was passed a query graph with more than two nodes: "
             f"{query_graph.to_dict()}",
             error_code="InvalidQuery")
     elif len(query_graph.nodes) < 2:
         log.error(
             f"answer_one_hop_query() was passed a query graph with less than two nodes: "
             f"{query_graph.to_dict()}",
             error_code="InvalidQuery")
Example #21
0
    def _extract_query_subgraph(qedge_ids_to_expand: List[str],
                                query_graph: QueryGraph,
                                log: Response) -> QueryGraph:
        # This function extracts a sub-query graph containing the provided qedge IDs from a larger query graph
        sub_query_graph = QueryGraph(nodes=[], edges=[])

        for qedge_id in qedge_ids_to_expand:
            # Make sure this query edge actually exists in the query graph
            if not any(qedge.id == qedge_id for qedge in query_graph.edges):
                log.error(
                    f"An edge with ID '{qedge_id}' does not exist in Message.QueryGraph",
                    error_code="UnknownValue")
                return None
            qedge = next(qedge for qedge in query_graph.edges
                         if qedge.id == qedge_id)

            # Make sure this qedge's qnodes actually exist in the query graph
            if not eu.get_query_node(query_graph, qedge.source_id):
                log.error(
                    f"Qedge {qedge.id}'s source_id refers to a qnode that does not exist in the query graph: "
                    f"{qedge.source_id}",
                    error_code="InvalidQEdge")
                return None
            if not eu.get_query_node(query_graph, qedge.target_id):
                log.error(
                    f"Qedge {qedge.id}'s target_id refers to a qnode that does not exist in the query graph: "
                    f"{qedge.target_id}",
                    error_code="InvalidQEdge")
                return None
            qnodes = [
                eu.get_query_node(query_graph, qedge.source_id),
                eu.get_query_node(query_graph, qedge.target_id)
            ]

            # Add (copies of) this qedge and its two qnodes to our new query sub graph
            qedge_copy = eu.copy_qedge(qedge)
            if not any(qedge.id == qedge_copy.id
                       for qedge in sub_query_graph.edges):
                sub_query_graph.edges.append(qedge_copy)
            for qnode in qnodes:
                qnode_copy = eu.copy_qnode(qnode)
                if not any(qnode.id == qnode_copy.id
                           for qnode in sub_query_graph.nodes):
                    sub_query_graph.nodes.append(qnode_copy)

        return sub_query_graph
Example #22
0
    def apply(self, input_message, input_parameters, response=None):

        if response is None:
            response = Response()
        self.response = response
        self.message = input_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 = self.parameters
        parameters['kp'] = "ARAX/KG1"
        parameters['enforce_directionality'] = False
        parameters['use_synonyms'] = True
        parameters['synonym_handling'] = 'map_back'
        parameters['continue_if_no_results'] = False
        for key, value in input_parameters.items():
            if key and key not in parameters:
                response.error(f"Supplied parameter {key} is not permitted",
                               error_code="UnknownParameter")
            else:
                if type(value) is str and value.lower() == "true":
                    value = True
                elif type(value) is str and value.lower() == "false":
                    value = False
                parameters[key] = value

        # Default to expanding the entire query graph if the user didn't specify what to expand
        if not parameters['edge_id'] and not parameters['node_id']:
            parameters['edge_id'] = [
                edge.id for edge in self.message.query_graph.edges
            ]
            parameters['node_id'] = self._get_orphan_query_node_ids(
                self.message.query_graph)

        if response.status != 'OK':
            return response

        response.data['parameters'] = parameters
        self.parameters = parameters

        # Do the actual expansion
        response.debug(
            f"Applying Expand to Message with parameters {parameters}")
        input_edge_ids = eu.convert_string_or_list_to_list(
            parameters['edge_id'])
        input_node_ids = eu.convert_string_or_list_to_list(
            parameters['node_id'])
        kp_to_use = self.parameters['kp']
        continue_if_no_results = self.parameters['continue_if_no_results']

        # Convert message knowledge graph to dictionary format, for faster processing
        dict_kg = eu.convert_standard_kg_to_dict_kg(
            self.message.knowledge_graph)

        # Expand any specified edges
        if input_edge_ids:
            query_sub_graph = self._extract_query_subgraph(
                input_edge_ids, self.message.query_graph)
            if response.status != 'OK':
                return response
            self.response.debug(
                f"Query graph for this Expand() call is: {query_sub_graph.to_dict()}"
            )

            # Expand the query graph edge by edge (much faster for neo4j queries, and allows easy integration with BTE)
            ordered_qedges_to_expand = self._get_order_to_expand_edges_in(
                query_sub_graph)
            node_usages_by_edges_map = dict()

            for qedge in ordered_qedges_to_expand:
                answer_kg, edge_node_usage_map = self._expand_edge(
                    qedge, kp_to_use, dict_kg, continue_if_no_results,
                    self.message.query_graph)
                if response.status != 'OK':
                    return response
                node_usages_by_edges_map[qedge.id] = edge_node_usage_map

                self._process_and_merge_answer(answer_kg, dict_kg)
                if response.status != 'OK':
                    return response

                self._prune_dead_end_paths(dict_kg, query_sub_graph,
                                           node_usages_by_edges_map)
                if response.status != 'OK':
                    return response

        # Expand any specified nodes
        if input_node_ids:
            for qnode_id in input_node_ids:
                answer_kg = self._expand_node(qnode_id, kp_to_use,
                                              continue_if_no_results,
                                              self.message.query_graph)
                if response.status != 'OK':
                    return response

                self._process_and_merge_answer(answer_kg, dict_kg)
                if response.status != 'OK':
                    return response

        # Convert message knowledge graph back to API standard format
        self.message.knowledge_graph = eu.convert_dict_kg_to_standard_kg(
            dict_kg)

        # Return the response and done
        kg = self.message.knowledge_graph
        response.info(
            f"After Expand, Message.KnowledgeGraph has {len(kg.nodes)} nodes and {len(kg.edges)} edges"
        )
        return response
class ARAXQueryGraphInterpreter:

    #### Constructor
    def __init__(self):
        self.response = Response()
        self.message = None
        self.parameters = None

        self.query_graph_templates = None
        self.query_graph_tree = None

        self.read_query_graph_templates()

    # #### Create a fresh Message object and fill with defaults
    def translate_to_araxi(self, message, describe=False):
        """
        Translate an input query_graph into ARAXi
        :return: Response object with execution information and the DSL command set
        :rtype: Response
        """

        #### Get a default response
        response = self.response
        debug = False

        #### Ensure that query_graph_templates is ready
        if self.query_graph_templates is None:
            response.error(
                "QueryGraph templates cannot be read from reference file",
                error_code="QueryGraphInterpreterMissingTemplates")
            return response

        query_graph_info = QueryGraphInfo()

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

        query_graph_template = query_graph_info.query_graph_templates[
            'detailed']

        # Check the number of nodes since the tree is based on the number of nodes
        n_nodes = query_graph_template['n_nodes']
        if n_nodes not in self.query_graph_tree['n_nodes']:
            response.error(
                "QueryGraphInterpreter finds more nodes than supported in this QueryGraph",
                error_code="QueryGraphInterpreterUnsupportedGraph")
            return response

        # Set up a list of tree pointers with both a pointer to the next dict as well as the running score total
        # There are potentially multiple matches to track especially since we permit less specific matches
        tree_pointers = [{
            'pointer': self.query_graph_tree['n_nodes'][n_nodes],
            'score': 0
        }]

        # Now look over each component looking for matches
        possible_next_steps = []
        for component in query_graph_template['components']:
            if debug: print(f"- Component is {component}")
            possible_next_steps = []

            #### If the component is a node, then score it
            if component['component_type'] == 'node':

                # Go through the list of possible things it could be and those or lesser possible next steps
                if component['has_curie'] and component[
                        'has_type'] and component['type_value']:
                    possible_next_steps.append({
                        'content': f"curie,type={component['type_value']}",
                        'score': 10000
                    })
                    possible_next_steps.append({
                        'content': 'curie',
                        'score': 1000
                    })
                    possible_next_steps.append({'content': '', 'score': 0})

                elif component['has_curie']:
                    possible_next_steps.append({
                        'content': 'curie',
                        'score': 1000
                    })
                    possible_next_steps.append({'content': '', 'score': 0})

                elif component['has_type'] and component['type_value']:
                    possible_next_steps.append({
                        'content': f"type={component['type_value']}",
                        'score': 100
                    })
                    possible_next_steps.append({
                        'content': 'type',
                        'score': 10
                    })
                    possible_next_steps.append({'content': '', 'score': 0})

                elif component['has_type']:
                    possible_next_steps.append({
                        'content': 'type',
                        'score': 10
                    })
                    possible_next_steps.append({'content': '', 'score': 0})

                else:
                    possible_next_steps.append({'content': '', 'score': 0})

            # Else it's an edge. Don't do anything with those currently
            else:
                possible_next_steps.append({'content': '', 'score': 0})

            # For each of the current tree pointers
            new_tree_pointers = []
            for tree_pointer in tree_pointers:

                # Consider each of the new possibilities
                for possible_next_step in possible_next_steps:
                    component_string = f"{component['component_id']}({possible_next_step['content']})"
                    if debug: print(f"  - component_string={component_string}")

                    # If this component is a possible next step in the tree, then add the next step to new_tree_pointers
                    if component_string in tree_pointer['pointer']:
                        if debug:
                            print(
                                f"    - Found this component with score {possible_next_step['score']}"
                            )
                        new_tree_pointers.append({
                            'pointer':
                            tree_pointer['pointer'][component_string],
                            'score':
                            tree_pointer['score'] + possible_next_step['score']
                        })
                        #tree_pointer = tree_pointer[component_string]

            # When we're done, reset the surviving tree pointers
            tree_pointers = new_tree_pointers

        # Now determine the best scoring match and assign that one
        query_graph_template_name = '??'
        best_score = -1
        for tree_pointer in tree_pointers:
            if 'name' in tree_pointer['pointer']:
                if debug:
                    print(
                        f"==> Found template is {tree_pointer['pointer']['name']} with score {tree_pointer['score']}"
                    )
                if tree_pointer['score'] > best_score:
                    query_graph_template_name = tree_pointer['pointer']['name']
                    best_score = tree_pointer['score']

        # If the final best template name is a real one in templates, then get the ARAXI for it
        if query_graph_template_name in self.query_graph_templates[
                'templates']:
            araxi_commands = self.query_graph_templates['templates'][
                query_graph_template_name]['DSL']

            # Need to remap the theoretical node and edge ids into the actual ones
            new_araxi_commands = []
            for command in araxi_commands:
                node_index = 0
                new_command = command
                for node in query_graph_info.node_order:
                    template_id = f"n{node_index:02}"
                    new_command = re.sub(template_id, node['id'], new_command)
                    node_index += 1

                edge_index = 0
                for edge in query_graph_info.edge_order:
                    template_id = f"e{edge_index:02}"
                    new_command = re.sub(template_id, edge['id'], new_command)
                    edge_index += 1

                new_araxi_commands.append(new_command)

            # TODO: Create the restated_question from the template
            response.data['araxi_commands'] = new_araxi_commands
            return response

        response.error(
            "QueryGraphInterpreter cannot interpret this QueryGraph",
            error_code="QueryGraphInterpreterUnsupportedGraph")
        return response

    # #### Read the YAML file containing the current QueryGraph templates
    def read_query_graph_templates(self):
        """
        Read the YAML file containing the current QueryGraph templates
        :rtype: None
        """

        # The template file is stored right next to this code
        template_file = os.path.dirname(os.path.abspath(
            __file__)) + "/ARAX_query_graph_interpreter_templates.yaml"

        # If the template file is not found, record an error and return
        if not os.path.exists(template_file):
            self.response.error(
                "QueryGraphInterpreter templates file is missing",
                error_code="QueryGraphInterpreterTemplateMissing")
            return self.response

        # Open the file and try to load it
        with open(template_file, 'r') as stream:
            try:
                self.query_graph_templates = yaml.safe_load(stream)
            except yaml.YAMLError as exc:
                self.response.error(
                    f"Error parsing YAML file template_file: {exc}",
                    error_code="CannotParseQueryGraphInterpreterTemplate")
                return self.response

        # Make sure the version is as expected
        if 'ARAX_QG_DSL_mapping' not in self.query_graph_templates:
            self.response.error(
                f"Missing version number in QueryGraphInterpreter templates file {template_file}",
                error_code="MissingQueryGraphInterpreterTemplateFileVersion")
            self.query_graph_templates = None
            return self.response
        if self.query_graph_templates['ARAX_QG_DSL_mapping'] != 0.1:
            self.response.error(
                f"Incorrect version number in QueryGraphInterpreter templates file {template_file}",
                error_code="BadQueryGraphInterpreterTemplateFileVersion")
            self.query_graph_templates = None
            return self.response

        # We will create dict lookup table of all the template string [e.g. 'n00(curie)-e00()-n01(type)' -> template_name]
        self.query_graph_templates['template_strings'] = {}

        # We will also create dict tree of all templates organized by the number of nodes and then by each component
        self.query_graph_tree = {'n_nodes': {}}

        # Loop over all the templates in the YAML file
        for template_name, template in self.query_graph_templates[
                'templates'].items():

            # Initialize an empty string to build the template in
            template_string = ''
            i = 0

            # Determine the number of components and nodes in this template and start building a tree
            n_components = len(template['template'])
            n_nodes = int((n_components + 1) / 2)
            if n_nodes not in self.query_graph_tree['n_nodes']:
                self.query_graph_tree['n_nodes'][n_nodes] = {}

            # previous_dict will contain the last place we left off in the tree
            previous_dict = self.query_graph_tree['n_nodes'][n_nodes]

            # Loop over each component building the tree
            for component in template['template']:

                # If this branch of the tree doesn't exist yet, add it
                if component not in previous_dict:
                    previous_dict[component] = {}

                # This location will be the next starting point
                previous_dict = previous_dict[component]

                # Append to the end of the template string
                if i > 0:
                    template_string += '-'
                template_string += component

                # If this is the last component, then attach the name of this template at the end
                i += 1
                if i == n_components:
                    previous_dict['name'] = template_name

            # Store the created template string and name
            self.query_graph_templates['template_strings'][
                template_string] = template_name
def main():

    #### Some qnode examples
    test_query_graphs = [
        [{
            'id': 'n10',
            'curie': 'DOID:9281'
        }, {
            'id': 'n11',
            'type': 'protein'
        }, {
            'id': 'e10',
            'source_id': 'n10',
            'target_id': 'n11'
        }],
        [{
            'id': 'n10',
            'curie': 'DOID:9281'
        }, {
            'id': 'n11',
            'type': 'protein'
        }, {
            'id': 'n12',
            'type': 'chemical_substance'
        }, {
            'id': 'e10',
            'source_id': 'n10',
            'target_id': 'n11'
        }, {
            'id': 'e11',
            'source_id': 'n11',
            'target_id': 'n12'
        }],
        [{
            'id': 'n10',
            'curie': 'DOID:9281'
        }, {
            'id': 'n11',
            'type': 'chemical_substance'
        }, {
            'id': 'e10',
            'source_id': 'n10',
            'target_id': 'n11'
        }],
        [{
            'id': 'n10',
            'curie': 'DOID:9281',
            'type': 'disease'
        }, {
            'id': 'n11',
            'type': 'chemical_substance'
        }, {
            'id': 'e10',
            'source_id': 'n10',
            'target_id': 'n11'
        }],
    ]

    #interpreter = ARAXQueryGraphInterpreter()
    #print(json.dumps(interpreter.query_graph_tree,sort_keys=True,indent=2))
    #return

    for test_query_graph in test_query_graphs:

        #### Create a response object for each test
        response = Response()

        #### Create a template Message
        messenger = ARAXMessenger()
        result = messenger.create_message()
        response.merge(result)
        message = messenger.message

        for parameters in test_query_graph:
            if 'n' in parameters['id']:
                result = messenger.add_qnode(message, parameters)
                response.merge(result)
                if result.status != 'OK':
                    print(response.show(level=Response.DEBUG))
                    return response
            elif 'e' in parameters['id']:
                result = messenger.add_qedge(message, parameters)
                response.merge(result)
                if result.status != 'OK':
                    print(response.show(level=Response.DEBUG))
                    return response
            else:
                response.error(f"Unrecognized type {parameters['id']}")
                return response

        interpreter = ARAXQueryGraphInterpreter()
        result = interpreter.translate_to_araxi(message)
        response.merge(result)
        if result.status != 'OK':
            print(response.show(level=Response.DEBUG))
            return response

        araxi_commands = result.data['araxi_commands']
        print(araxi_commands)
Example #25
0
    def _deduplicate_nodes(
            dict_kg: DictKnowledgeGraph,
            edge_to_nodes_map: Dict[str, Dict[str, str]], log: Response
    ) -> Tuple[DictKnowledgeGraph, Dict[str, Dict[str, str]]]:
        log.debug(f"Deduplicating nodes")
        deduplicated_kg = DictKnowledgeGraph(
            nodes={qnode_id: dict()
                   for qnode_id in dict_kg.nodes_by_qg_id},
            edges={qedge_id: dict()
                   for qedge_id in dict_kg.edges_by_qg_id})
        updated_edge_to_nodes_map = {
            edge_id: dict()
            for edge_id in edge_to_nodes_map
        }
        curie_mappings = dict()

        # First deduplicate the nodes
        for qnode_id, nodes in dict_kg.nodes_by_qg_id.items():
            # Load preferred curie info from NodeSynonymizer for nodes we haven't seen before
            unmapped_node_ids = set(nodes).difference(set(curie_mappings))
            log.debug(
                f"Getting preferred curies for {qnode_id} nodes returned in this step"
            )
            canonicalized_nodes = eu.get_preferred_curies(
                list(unmapped_node_ids), log) if unmapped_node_ids else dict()
            if log.status != 'OK':
                return deduplicated_kg, updated_edge_to_nodes_map

            for node_id in unmapped_node_ids:
                # Figure out the preferred curie/name for this node
                node = nodes.get(node_id)
                canonicalized_node = canonicalized_nodes.get(node_id)
                if canonicalized_node:
                    preferred_curie = canonicalized_node.get(
                        'preferred_curie', node_id)
                    preferred_name = canonicalized_node.get(
                        'preferred_name', node.name)
                    preferred_type = eu.convert_string_or_list_to_list(
                        canonicalized_node.get('preferred_type', node.type))
                    curie_mappings[node_id] = preferred_curie
                else:
                    # Means the NodeSynonymizer didn't recognize this curie
                    preferred_curie = node_id
                    preferred_name = node.name
                    preferred_type = node.type
                    curie_mappings[node_id] = preferred_curie

                # Add this node into our deduplicated KG as necessary # TODO: merge certain fields, like uri?
                if preferred_curie not in deduplicated_kg.nodes_by_qg_id[
                        qnode_id]:
                    node.id = preferred_curie
                    node.name = preferred_name
                    node.type = preferred_type
                    deduplicated_kg.add_node(node, qnode_id)

        # Then update the edges to reflect changes made to the nodes
        for qedge_id, edges in dict_kg.edges_by_qg_id.items():
            for edge_id, edge in edges.items():
                edge.source_id = curie_mappings.get(edge.source_id)
                edge.target_id = curie_mappings.get(edge.target_id)
                if not edge.source_id or not edge.target_id:
                    log.error(
                        f"Could not find preferred curie mappings for edge {edge_id}'s node(s)"
                    )
                    return deduplicated_kg, updated_edge_to_nodes_map
                deduplicated_kg.add_edge(edge, qedge_id)

                # Update the edge-to-node map for this edge (used down the line for pruning)
                for qnode_id, corresponding_node_id in edge_to_nodes_map[
                        edge_id].items():
                    updated_edge_to_nodes_map[edge_id][
                        qnode_id] = curie_mappings.get(corresponding_node_id)

        log.debug(
            f"After deduplication, answer KG counts are: {eu.get_printable_counts_by_qg_id(deduplicated_kg)}"
        )
        return deduplicated_kg, updated_edge_to_nodes_map
Example #26
0
    def _expand_edge(
            self, qedge: QEdge, kp_to_use: str, dict_kg: DictKnowledgeGraph,
            continue_if_no_results: bool, query_graph: QueryGraph,
            use_synonyms: bool, synonym_handling: str, log: Response
    ) -> Tuple[DictKnowledgeGraph, Dict[str, Dict[str, str]]]:
        # This function answers a single-edge (one-hop) query using the specified knowledge provider
        log.info(f"Expanding edge {qedge.id} using {kp_to_use}")
        answer_kg = DictKnowledgeGraph()
        edge_to_nodes_map = dict()

        # Create a query graph for this edge (that uses synonyms as well as curies found in prior steps)
        edge_query_graph = self._get_query_graph_for_edge(
            qedge, query_graph, dict_kg, use_synonyms, kp_to_use, log)
        if log.status != 'OK':
            return answer_kg, edge_to_nodes_map
        if not any(qnode for qnode in edge_query_graph.nodes if qnode.curie):
            log.error(
                f"Cannot expand an edge for which neither end has any curies. (Could not find curies to use from "
                f"a prior expand step, and neither qnode has a curie specified.)",
                error_code="InvalidQuery")
            return answer_kg, edge_to_nodes_map

        valid_kps = ["ARAX/KG1", "ARAX/KG2", "BTE", "COHD", "NGD"]
        if kp_to_use not in valid_kps:
            log.error(
                f"Invalid knowledge provider: {kp_to_use}. Valid options are {', '.join(valid_kps)}",
                error_code="InvalidKP")
            return answer_kg, edge_to_nodes_map
        else:
            if kp_to_use == 'BTE':
                from Expand.bte_querier import BTEQuerier
                kp_querier = BTEQuerier(log)
            elif kp_to_use == 'COHD':
                from Expand.COHD_querier import COHDQuerier
                kp_querier = COHDQuerier(log)
            elif kp_to_use == 'NGD':
                from Expand.ngd_querier import NGDQuerier
                kp_querier = NGDQuerier(log)
            else:
                from Expand.kg_querier import KGQuerier
                kp_querier = KGQuerier(log, kp_to_use)
            answer_kg, edge_to_nodes_map = kp_querier.answer_one_hop_query(
                edge_query_graph)
            if log.status != 'OK':
                return answer_kg, edge_to_nodes_map
            log.debug(
                f"Query for edge {qedge.id} returned results ({eu.get_printable_counts_by_qg_id(answer_kg)})"
            )

            # Do some post-processing (deduplicate nodes, remove self-edges..)
            if synonym_handling != 'add_all':
                answer_kg, edge_to_nodes_map = self._deduplicate_nodes(
                    answer_kg, edge_to_nodes_map, log)
            if eu.qg_is_fulfilled(edge_query_graph, answer_kg):
                answer_kg = self._remove_self_edges(answer_kg,
                                                    edge_to_nodes_map,
                                                    qedge.id,
                                                    edge_query_graph.nodes,
                                                    log)

            # Make sure our query has been fulfilled (unless we're continuing if no results)
            if not eu.qg_is_fulfilled(edge_query_graph, answer_kg):
                if continue_if_no_results:
                    log.warning(
                        f"No paths were found in {kp_to_use} satisfying this query graph"
                    )
                else:
                    log.error(
                        f"No paths were found in {kp_to_use} satisfying this query graph",
                        error_code="NoResults")

            return answer_kg, edge_to_nodes_map
Example #27
0
def main():

    #### Create a response object
    response = Response()

    #### Some qnode examples
    test_query_graphs = [
        [{
            'id': 'n10',
            'curie': 'DOID:9281'
        }, {
            'id': 'n11',
            'type': 'protein'
        }, {
            'id': 'e10',
            'source_id': 'n10',
            'target_id': 'n11'
        }],
        [{
            'id': 'n10',
            'curie': 'DOID:9281'
        }, {
            'id': 'n11',
            'type': 'protein'
        }, {
            'id': 'n12',
            'type': 'drug'
        }, {
            'id': 'e10',
            'source_id': 'n10',
            'target_id': 'n11'
        }, {
            'id': 'e11',
            'source_id': 'n11',
            'target_id': 'n12'
        }],
    ]

    for test_query_graph in test_query_graphs:

        #### Create a template Message
        messenger = ARAXMessenger()
        result = messenger.create_message()
        response.merge(result)
        message = messenger.message

        for parameters in test_query_graph:
            if 'n' in parameters['id']:
                result = messenger.add_qnode(message, parameters)
                response.merge(result)
                if result.status != 'OK':
                    print(response.show(level=Response.DEBUG))
                    return response
            elif 'e' in parameters['id']:
                result = messenger.add_qedge(message, parameters)
                response.merge(result)
                if result.status != 'OK':
                    print(response.show(level=Response.DEBUG))
                    return response
            else:
                response.error(f"Unrecognized type {parameters['id']}")
                return response

        interpreter = ARAXQueryGraphInterpreter()
        result = interpreter.translate_to_araxi(message)
        response.merge(result)
        if result.status != 'OK':
            print(response.show(level=Response.DEBUG))
            return response

        araxi_commands = result.data['araxi_commands']
        print(araxi_commands)

        #### Show the final result
        print('-------------------------')
        print(response.show(level=Response.DEBUG))
Example #28
0
    def parse(self, input_actions):

        #### Define a default response
        response = Response()
        response.info(f"Parsing input actions list")

        #### Basic error checking of the input_actions
        if not isinstance(input_actions, list):
            response.error("Provided input actions is not a list",
                           error_code="ActionsNotList")
            return response
        if len(input_actions) == 0:
            response.error("Provided input actions is an empty list",
                           error_code="ActionsListEmpty")
            return response

        #### Iterate through the list, checking the items
        actions = []
        n_lines = 1
        for action in input_actions:
            response.debug(f"Parsing action: {action}")

            # If this line is empty, then skip
            match = re.match(r"\s*$", action)
            if match:
                continue

            # If this line begins with a #, it is a comment, then skip
            match = re.match(r"#", action)
            if match:
                continue

            #### First look for a naked command without parentheses
            match = re.match(r"\s*([A-Za-z_]+)\s*$", action)
            if match is not None:
                action = {
                    "line": n_lines,
                    "command": match.group(1),
                    "parameters": None
                }
                actions.append(action)

            #### Then look for and parse a command with parentheses and a comma-separated parameter list
            if match is None:
                match = re.match(r"\s*([A-Za-z_]+)\((.*)\)\s*$", action)
                if match is not None:
                    command = match.group(1)
                    param_string = match.group(2)

                    #### Split the parameters on comma and process those
                    param_string_list = re.split(",", param_string)
                    parameters = {}

                    #### If a value is of the form key=[value1,value2] special code is needed to recompose that
                    mode = 'normal'
                    list_buffer = []
                    key = ''
                    for param_item in param_string_list:
                        param_item = param_item.strip()
                        if mode == 'normal':

                            #### Split on the first = only (might be = in the value)
                            values = re.split("=", param_item, 1)
                            key = values[0]
                            #### If there isn't a value after an =, then just set to string true
                            value = 'true'
                            if len(values) > 1:
                                value = values[1]
                            key = key.strip()
                            value = value.strip()

                            #### If the value begins with a "[", then this is a list
                            match = re.match(r"\[(.+)$", value)
                            if match:
                                #### If it also ends with a "]", then this is a list of one element
                                match2 = re.match(r"\[(.*)\]$", value)
                                if match2:
                                    if match2.group(1) == '':
                                        parameters[key] = []
                                    else:
                                        parameters[key] = [match2.group(1)]
                                else:
                                    mode = 'in_list'
                                    list_buffer = [match.group(1)]
                            else:
                                parameters[key] = value

                        #### Special processing if we're in the middle of a list
                        elif mode == 'in_list':
                            match = re.match(r"(.*)\]$", param_item)
                            if match:
                                mode = 'normal'
                                list_buffer.append(match.group(1))
                                parameters[key] = list_buffer
                            else:
                                list_buffer.append(param_item)
                        else:
                            eprint("Inconceivable!")
                    if mode == 'in_list':
                        parameters[key] = list_buffer

                    #### Store the parsed result in a dict and add to the list
                    action = {
                        "line": n_lines,
                        "command": command,
                        "parameters": parameters
                    }
                    actions.append(action)
                else:
                    response.error(f"Unable to parse action {action}",
                                   error_code="ActionsListEmpty")
            n_lines += 1

        #### Put the actions in the response data envelope and return
        response.data["actions"] = actions
        return response
Example #29
0
    def reassign_curies(self, message, input_parameters, describe=False):
        """
        Reassigns CURIEs to the target Knowledge Provider
        :param message: Translator standard Message object
        :type message: Message
        :param input_parameters: Dict of input parameters to control the method
        :type input_parameters: Message
        :return: Response object with execution information
        :rtype: Response
        """

        # #### Internal documentation setup
        allowable_parameters = {
            'knowledge_provider': {
                'Name of the Knowledge Provider CURIE space to map to. Default=KG1. Also currently supported KG2'
            },
            'mismap_result': {
                'Desired action when mapping fails: ERROR or WARNING. Default is ERROR'
            },
        }
        if describe:
            allowable_parameters[
                'dsl_command'] = '`reassign_curies()`'  # can't get this name at run-time, need to manually put it in per https://www.python.org/dev/peps/pep-3130/
            allowable_parameters[
                'brief_description'] = """The `reassign_curies` method reassigns all the CURIEs in the Message QueryGraph to the specified
                knowledge provider. Allowed values are KG1 or KG2. Default is KG1 if not specified."""
            return allowable_parameters

        #### Define a default response
        response = Response()
        self.response = response
        self.message = 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 = {
            'knowledge_provider': 'KG1',
            'mismap_result': 'ERROR',
        }

        #### 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

        # Check that the knowledge_provider is valid:
        if parameters['knowledge_provider'] != 'KG1' and parameters[
                'knowledge_provider'] != 'KG2':
            response.error(
                f"Specified knowledge provider must be 'KG1' or 'KG2', not '{parameters['knowledge_provider']}'",
                error_code="UnknownKP")
            return response

        #### Now try to assign the CURIEs
        response.info(
            f"Reassigning the CURIEs in QueryGraph to {parameters['knowledge_provider']} space"
        )

        #### 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 KGNodeIndex
        kgNodeIndex = KGNodeIndex()

        # Loops through the QueryGraph nodes and adjust them
        for qnode in message.query_graph.nodes:

            # If the CURIE is None, then there's nothing to do
            curie = qnode.curie
            if curie is None:
                continue

            # Map the CURIE to the desired Knowledge Provider
            if parameters['knowledge_provider'] == 'KG1':
                if kgNodeIndex.is_curie_present(curie) is True:
                    mapped_curies = [curie]
                else:
                    mapped_curies = kgNodeIndex.get_KG1_curies(curie)
            elif parameters['knowledge_provider'] == 'KG2':
                if kgNodeIndex.is_curie_present(curie, kg_name='KG2'):
                    mapped_curies = [curie]
                else:
                    mapped_curies = kgNodeIndex.get_curies_and_types(
                        curie, kg_name='KG2')
            else:
                response.error(
                    f"Specified knowledge provider must be 'KG1' or 'KG2', not '{parameters['knowledge_provider']}'",
                    error_code="UnknownKP")
                return response

            # Try to find a new CURIE
            new_curie = None
            if len(mapped_curies) == 0:
                if parameters['mismap_result'] == 'WARNING':
                    response.warning(
                        f"Did not find a mapping for {curie} to KP '{parameters['knowledge_provider']}'. Leaving as is"
                    )
                else:
                    response.error(
                        f"Did not find a mapping for {curie} to KP '{parameters['knowledge_provider']}'. This is an error"
                    )
            elif len(mapped_curies) == 1:
                new_curie = mapped_curies[0]
            else:
                original_curie_is_fine = False
                for potential_curie in mapped_curies:
                    if potential_curie == curie:
                        original_curie_is_fine = True
                if original_curie_is_fine:
                    new_curie = curie
                else:
                    new_curie = mapped_curies[0]
                    response.warning(
                        f"There are multiple possible CURIEs in KP '{parameters['knowledge_provider']}'. Selecting the first one {new_curie}"
                    )

            # If there's no CURIE, then nothing to do
            if new_curie is None:
                pass
            # If it's the same
            elif new_curie == curie:
                response.debug(
                    f"CURIE {curie} is fine for KP '{parameters['knowledge_provider']}'"
                )
            else:
                response.info(
                    f"Remapping CURIE {curie} to {new_curie} for KP '{parameters['knowledge_provider']}'"
                )

        #### Return the response
        return response
Example #30
0
    def add_qedge(self, message, input_parameters, describe=False):
        """
        Adds a new QEdge object to the QueryGraph inside the Message object
        :return: Response object with execution information
        :rtype: Response
        """

        # #### Internal documentation setup
        allowable_parameters = {
            'id': {
                'Any string that is unique among all QEdge id fields, with recommended format e00, e01, e02, etc.'
            },
            'source_id': {
                'id of the source QNode already present in the QueryGraph (e.g. n01, n02)'
            },
            'target_id': {
                'id of the target QNode already present in the QueryGraph (e.g. n01, n02)'
            },
            'type': {
                'Any valid Translator/BioLink relationship type (e.g. physically_interacts_with, participates_in)'
            },
        }
        if describe:
            #allowable_parameters['action'] = { 'None' }
            #allowable_parameters = dict()
            allowable_parameters[
                'dsl_command'] = '`add_qedge()`'  # can't get this name at run-time, need to manually put it in per https://www.python.org/dev/peps/pep-3130/
            allowable_parameters[
                'brief_description'] = """The `add_qedge` method adds an additional QEdge to the QueryGraph in the Message object. Currently
                source_id and target_id 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."""
            return allowable_parameters

        #### Define a default response
        response = Response()
        self.response = response
        self.message = 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 = {
            'id': None,
            'source_id': None,
            'target_id': None,
            'type': 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['id'] is not None:
            id = parameters['id']
        else:
            id = self.__get_next_free_edge_id()
        qedge.id = id

        #### Get the list of available node_ids
        qnodes = message.query_graph.nodes
        ids = {}
        for qnode in qnodes:
            id = qnode.id
            ids[id] = 1

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

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

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

        #### Add it to the query_graph edge list
        message.query_graph.edges.append(qedge)

        #### Return the response
        return response