From 823cbb3732ecb09ac40bc9c50a8458416221d385 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Sat, 20 Jul 2024 17:59:44 -0400 Subject: [PATCH 01/13] new codes/{code_id}/terms endpoint --- .../common_routes/codes/codes_controller.py | 29 +++++++++++- .../common_routes/common_neo4j_logic.py | 46 ++++++++++++++++++- src/ubkg_api/cypher/code_code_id_terms.cypher | 19 ++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/ubkg_api/cypher/code_code_id_terms.cypher diff --git a/src/ubkg_api/common_routes/codes/codes_controller.py b/src/ubkg_api/common_routes/codes/codes_controller.py index e224133..373204f 100644 --- a/src/ubkg_api/common_routes/codes/codes_controller.py +++ b/src/ubkg_api/common_routes/codes/codes_controller.py @@ -1,5 +1,6 @@ from flask import Blueprint, jsonify, current_app, make_response #, request -from ..common_neo4j_logic import codes_code_id_codes_get_logic, codes_code_id_concepts_get_logic +from ..common_neo4j_logic import (codes_code_id_codes_get_logic, codes_code_id_concepts_get_logic, + codes_code_id_terms_get_logic) from utils.http_error_string import get_404_error_string, validate_query_parameter_names, \ validate_parameter_value_in_enum from utils.http_parameter import parameter_as_list @@ -54,3 +55,29 @@ def codes_code_id_concepts_get(code_id): return make_response(err, 404) return jsonify(result) +@codes_blueprint.route('//terms', methods=['GET']) +def codes_code_id_terms_get(code_id): + """ + Returns an array of terms linked to the specified Code node. + :param code_id: the code identifier + """ + + # Validate parameters. + # Validate sab parameter. + err = validate_query_parameter_names(parameter_name_list=['term_type']) + if err != 'ok': + return make_response(err, 400) + + # Obtain a list of sab parameter values. + term_type = parameter_as_list(param_name='term_type') + + neo4j_instance = current_app.neo4jConnectionHelper.instance() + result = codes_code_id_terms_get_logic(neo4j_instance, code_id=code_id,term_type=term_type) + + if result is None or result == []: + # Empty result + err = get_404_error_string(prompt_string='No Terms linked to the Code specified', + custom_request_path=f'CodeID = {code_id}') + return make_response(err, 404) + + return jsonify(result) \ No newline at end of file diff --git a/src/ubkg_api/common_routes/common_neo4j_logic.py b/src/ubkg_api/common_routes/common_neo4j_logic.py index 2978695..d8cc8ed 100644 --- a/src/ubkg_api/common_routes/common_neo4j_logic.py +++ b/src/ubkg_api/common_routes/common_neo4j_logic.py @@ -1082,5 +1082,49 @@ def sources_get_logic(neo4j_instance, sab=None, context=None) -> dict: except KeyError: pass - # The query has a single record. return source + +def codes_code_id_terms_get_logic(neo4j_instance,code_id: str, term_type=None) -> dict: + """ + Obtains information on terms that link to a code. + + The return from the query is simple, and there is no need for a model class. + + :param neo4j_instance: neo4j connection + :param code_id: a UBKG Code in format SAB:CodeId + :param term_type: an optional list of acronyms for a code type + + """ + terms: [dict] = [] + + # Load and parameterize query. + querytxt = loadquerystring('code_code_id_terms.cypher') + + # Filter by code_id. + querytxt = querytxt.replace('$code_id', f"'{code_id}'") + + # Filter by code SAB. + if len(term_type) == 0: + querytxt = querytxt.replace('$termtype_filter', '') + else: + querytxt = querytxt.replace('$termtype_filter', f" AND TYPE(r) IN {term_type}") + + print(querytxt) + # Set timeout for query based on value in app.cfg. + query = neo4j.Query(text=querytxt, timeout=neo4j_instance.timeout) + + with neo4j_instance.driver.session() as session: + recds: neo4j.Result = session.run(query) + for record in recds: + term = record.get('response') + try: + terms.append(term) + + except KeyError: + pass + + # The query has either zero records or one record + if len(terms) == 1: + return term + else: + return terms diff --git a/src/ubkg_api/cypher/code_code_id_terms.cypher b/src/ubkg_api/cypher/code_code_id_terms.cypher new file mode 100644 index 0000000..d2854a4 --- /dev/null +++ b/src/ubkg_api/cypher/code_code_id_terms.cypher @@ -0,0 +1,19 @@ +// Used in the codes/{code_id}/terms endpoint +// This query returns the list of Term nodes +// that link to the specified code. +// The list can be filtered to return only those terms of a particular "term type"--which, in the +// UBKG, corresponds to the type of relationship between the term and the code. + +// The function that loads this query will replace values for code_id and termtype_filter. + +CALL +{ + WITH $code_id AS query + MATCH (p:Concept)-[:CODE]->(c:Code)-[r]->(t:Term) + WHERE c.CodeID = query + AND r.CUI = p.CUI + $termtype_filter + RETURN DISTINCT c.CodeID AS code, type(r) as term_type, t.name as term +} +WITH code, COLLECT(DISTINCT {term_type: term_type, term: term}) AS terms +RETURN {code:code,terms:terms} AS response \ No newline at end of file From 488c626af8a14e3e231e9b85e9c5b8622c1da78c Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Sat, 20 Jul 2024 18:24:42 -0400 Subject: [PATCH 02/13] new codes/{code_id}/terms endpoint --- dd-api-spec.yaml | 54 ++++++++++++++++++ .../common_routes/common_neo4j_logic.py | 1 - ubkg-api-spec.yaml | 55 +++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/dd-api-spec.yaml b/dd-api-spec.yaml index dd0d2c0..1bb2bae 100644 --- a/dd-api-spec.yaml +++ b/dd-api-spec.yaml @@ -78,6 +78,38 @@ paths: description: no Concept nodes for the specified Code '5XX': description: Unknown error + /codes/{code_id}/terms: + get: + operationId: codes_code_id_terms_get + summary: Returns the set of Term nodes that link to the specified Code node, subject to constraints specified in parameters. + parameters: + - name: code_id + in: path + required: true + description: The CodeID for a Code node, in format SAB:CODE. + schema: + type: string + example: SNOMEDCT_US:254837009 + - name: term_type + in: query + required: false + description: optional term type for the term. Can be either a list of values delimited with commas (e.g., ?term_type=PT,SY,FN) or with individual key-value pairs (e.g., ?term_type=PT&term_type=SY&term_type=FN) + schema: + type: string + example: PT + responses: + '200': + description: An array of Term nodes that have relationships with the specified Code node + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CodeTerms' + '404': + description: no Term nodes for the specified Code + '5XX': + description: Unknown error /concepts/{concept_id}/codes: get: operationId: concepts_concept_id_codes_get @@ -909,6 +941,28 @@ components: description: an identifier for the source of the code. Corresponds to the Source Abbreviaion (SAB) of a Code in the UMLS. type: string example: CCF + CodeTerms: # Schema name + description: Term nodes that link to the specified Code. + type: object + properties: + code: + description: the Code to which Terms link + type: string + example: SNOMEDCT_US:254837009 + terms: + description: terms linked to the code, subject to parameters + type: array + items: + type: object + properties: + term: + type: string + description: term string + example: Malignant tumor of breast + term_type: + type: string + description: term type for the term - an acronym of usually two or three letters + example: PT ConceptDetail: # Schema name type: object description: Information on a Concept node. diff --git a/src/ubkg_api/common_routes/common_neo4j_logic.py b/src/ubkg_api/common_routes/common_neo4j_logic.py index d8cc8ed..206ca91 100644 --- a/src/ubkg_api/common_routes/common_neo4j_logic.py +++ b/src/ubkg_api/common_routes/common_neo4j_logic.py @@ -1109,7 +1109,6 @@ def codes_code_id_terms_get_logic(neo4j_instance,code_id: str, term_type=None) - else: querytxt = querytxt.replace('$termtype_filter', f" AND TYPE(r) IN {term_type}") - print(querytxt) # Set timeout for query based on value in app.cfg. query = neo4j.Query(text=querytxt, timeout=neo4j_instance.timeout) diff --git a/ubkg-api-spec.yaml b/ubkg-api-spec.yaml index 4196b1e..6b361ae 100644 --- a/ubkg-api-spec.yaml +++ b/ubkg-api-spec.yaml @@ -76,6 +76,38 @@ paths: description: no Concept nodes for the specified Code '5XX': description: Unknown error + /codes/{code_id}/terms: + get: + operationId: codes_code_id_terms_get + summary: Returns the set of Term nodes that link to the specified Code node, subject to constraints specified in parameters. + parameters: + - name: code_id + in: path + required: true + description: The CodeID for a Code node, in format SAB:CODE. + schema: + type: string + example: SNOMEDCT_US:254837009 + - name: term_type + in: query + required: false + description: optional term type for the term. Can be either a list of values delimited with commas (e.g., ?term_type=PT,SY,FN) or with individual key-value pairs (e.g., ?term_type=PT&term_type=SY&term_type=FN) + schema: + type: string + example: PT + responses: + '200': + description: An array of Term nodes that have relationships with the specified Code node + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CodeTerms' + '404': + description: no Term nodes for the specified Code + '5XX': + description: Unknown error /concepts/{concept_id}/codes: get: operationId: concepts_concept_id_codes_get @@ -901,6 +933,29 @@ components: description: an identifier for the source of the code. Corresponds to the Source Abbreviaion (SAB) of a Code in the UMLS. type: string example: CCF + CodeTerms: # Schema name + description: Term nodes that link to the specified Code. + type: object + properties: + code: + description: the Code to which Terms link + type: string + example: SNOMEDCT_US:254837009 + terms: + description: terms linked to the code, subject to parameters + type: array + items: + type: object + properties: + term: + type: string + description: term string + example: Malignant tumor of breast + term_type: + type: string + description: term type for the term - an acronym of usually two or three letters + example: PT + ConceptDetail: # Schema name type: object description: Information on a Concept node. From e5a7c52505fe3a882ed84e3da369af08c4cfa210 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Thu, 25 Jul 2024 07:20:53 -0400 Subject: [PATCH 03/13] new /concepts//subgraphs/sequential endpoint --- .../common_routes/common_neo4j_logic.py | 41 ++++++++++ .../concepts/concepts_controller.py | 81 ++++++++++++++++++- .../concepts_subgraph_sequential.cypher | 69 ++++++++++++++++ 3 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/ubkg_api/cypher/concepts_subgraph_sequential.cypher diff --git a/src/ubkg_api/common_routes/common_neo4j_logic.py b/src/ubkg_api/common_routes/common_neo4j_logic.py index 206ca91..9a8f97b 100644 --- a/src/ubkg_api/common_routes/common_neo4j_logic.py +++ b/src/ubkg_api/common_routes/common_neo4j_logic.py @@ -1127,3 +1127,44 @@ def codes_code_id_terms_get_logic(neo4j_instance,code_id: str, term_type=None) - return term else: return terms + +def concepts_subgraph_sequential_get_logic(neo4j_instance, startCUI=None, reltypes=None, relsabs=None, skip=None, + limit=None) -> List[ConceptGraph]: + + """ + Obtains a subset of paths that originate from the concept with CUI=startCUI, in a sequence of relationships + specified by reltypes and relsab, limited by skip and limit parameters. + + :param neo4j_instance: UBKG connection + :param startCUI: CUI of concept from which to expand paths + :param reltypes: sequential list of relationship types + :param relsabs: sequential list of relationship SABs + :param skip: paths to skip + :param limit: maximum number of paths to return + + For example, reltypes=["isa","part_of"] and relsabs=["UBERON","PATO"] results in a query for paths that match + the pattern + + (startCUI: Concept)-[r1:isa]-(c1:Concept)-[r2:has_part]->(c2:Concept) + where r1.SAB = "UBERON" and r2.SAB="PATO" + """ + + conceptgraphs: [ConceptGraph] = [] + conceptgraph: ConceptGraph = {} + + # Load query string and associate parameter values to variables. + querytxt = loadquerystring(filename='concepts_subgraph_sequential.cypher') + querytxt = querytxt.replace('$startCUI', f'"{startCUI}"') + + sabjoin = format_list_for_query(listquery=reltypes, doublequote=True) + querytxt = querytxt.replace('$reltypes', sabjoin) + reljoin = format_list_for_query(listquery=relsabs, doublequote=True) + querytxt = querytxt.replace('$relsabs', reljoin) + querytxt = querytxt.replace('$skip', str(skip)) + querytxt = querytxt.replace('$limit', str(limit)) + + # Set timeout for query based on value in app.cfg. + query = neo4j.Query(text=querytxt, timeout=neo4j_instance.timeout) + + # MAY 2024 - bug fix - changed argument from querytxt to query + return get_graph(neo4j_instance, query=query) \ No newline at end of file diff --git a/src/ubkg_api/common_routes/concepts/concepts_controller.py b/src/ubkg_api/common_routes/concepts/concepts_controller.py index 83c87b5..a9674a9 100644 --- a/src/ubkg_api/common_routes/concepts/concepts_controller.py +++ b/src/ubkg_api/common_routes/concepts/concepts_controller.py @@ -4,7 +4,7 @@ from ..common_neo4j_logic import concepts_concept_id_codes_get_logic, concepts_concept_id_concepts_get_logic,\ concepts_concept_id_definitions_get_logic, concepts_expand_get_logic,\ concepts_shortestpath_get_logic, concepts_trees_get_logic, concepts_subgraph_get_logic, \ - concepts_identfier_node_get_logic + concepts_identfier_node_get_logic, concepts_subgraph_sequential_get_logic # Functions to validate query parameters from utils.http_error_string import get_404_error_string, validate_query_parameter_names, \ validate_parameter_value_in_enum, validate_required_parameters, validate_parameter_is_numeric, \ @@ -423,3 +423,82 @@ def concepts_concept_identifier_nodes_get(search): dict_result = {'nodeobjects': result} return jsonify(dict_result) + +@concepts_blueprint.route('/paths/subgraphs/sequential', methods=['GET']) +def concepts_paths_subraphs_sequential_expand_get(concept_id, relsequence=None): + + """ + Returns the set of paths that begins with the concept and has relationships in a specified + sequence. Response is in neo4j graph format ({nodes, paths, edges}). + + :param relsequence: an ordered list that specifies a sequence of relationships in a path. + The format of each element in relsequence is :. + For example, ['UBERON:isa','PATO:has_part'] specifies the set of paths that start from the concept with CUI + with relationships that match the pattern + + (concept_id: Concept)-[r1:isa]-(c1:Concept)-[r2:has_part]->(c2:Concept) + + in which r1.SAB = 'UBERON' and r2.SAB = 'PATO' + + """ + + neo4j_instance = current_app.neo4jConnectionHelper.instance() + + # Validate parameters. + # Check for invalid parameter names. + err = validate_query_parameter_names(parameter_name_list=['relsequence', 'skip', 'limit']) + if err != 'ok': + return make_response(err, 400) + + # Check for required parameters. + err = validate_required_parameters(required_parameter_list=['relsequence']) + if err != 'ok': + return make_response(err, 400) + + # Check that the non-default skip is non-negative. + skip = request.args.get('skip') + err = validate_parameter_is_nonnegative(param_name='skip', param_value=skip) + if err != 'ok': + return make_response(err, 400) + + # Set default mininum. + skip = set_default_minimum(param_value=skip, default=0) + + # Check that non-default limit is non-negative. + limit = request.args.get('limit') + err = validate_parameter_is_nonnegative(param_name='limit', param_value=limit) + if err != 'ok': + return make_response(err, 400) + # Set default row limit, based on the app configuration. + limit = set_default_maximum(param_value=limit, default=neo4j_instance.rowlimit) + + # Get remaining parameter values from the path or query string. + relsequence = parameter_as_list(param_name='relsequence') + reltypes = [] + relsabs = [] + for rs in relsequence: + if not ':' in rs: + err = f'Invalid parameter value: {rs}. Format relationships as :' + return make_response(err, 400) + + relsabs.append(rs.split(':')[0]) + reltypes.append(rs.split(':')[1]) + + + result = concepts_subgraph_sequential_get_logic(neo4j_instance, startCUI=concept_id, reltypes=reltypes, relsabs=relsabs, + skip=skip, limit=limit) + + iserr = result is None or result == {} + + if iserr: + err = get_404_error_string(prompt_string=f"No Concepts in paths with specified parameters", + custom_request_path=f"startCUI='{concept_id}'", + timeout=neo4j_instance.timeout) + return make_response(err, 404) + + # Limit the size of the payload, based on the app configuration. + err = check_payload_size(payload=result, max_payload_size=neo4j_instance.payloadlimit) + if err != "ok": + return make_response(err, 400) + + return jsonify(result) \ No newline at end of file diff --git a/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher b/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher new file mode 100644 index 0000000..f5c90bf --- /dev/null +++ b/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher @@ -0,0 +1,69 @@ +// Used by the concepts/paths/subgraph/sequential endpoint. +// Find all paths that start with Concept associated with the specified code and have relationships that are defined by specified SABs +// in the specified sequence. +// For example, the query will return all paths that start with the concept linked to UBERON:0004548 (left eye) and then have relationships: +// 1st level - "isa" relationship from UBERON +// 2nd level - "has_part" relationship from PATO + +// Two independent filters are required: +// 1. sequential relationship types (e.g., ["isa","has_part"]) +// 2. sequential SAB properties (e.g. ["UBERON","PATO"]) + + +// Selection parameters supplied by the calling function. +// 1. Code corresponding to the starting concept of the expansion. +//WITH 'PR:P11087 CUI' AS startCUI, +WITH $startCUI AS startCUI, +// 2. Sequence of relationship types +//['isa','only_in_taxon','has_part'] as reltypes, +[$reltypes] AS reltypes, +// 3. Sequence of relationship SABs +//['CL','MP','EFO'] as relsabs, +[$relsabs] AS relsabs + +// FIRST FILTER: sequence of relationship types. +// Expand from the starting concept, obtaining only those paths that exactly match the specified sequence of relationship types. +// Because the relationshipFilter specifies a sequence, relevant paths have length equal to the number of specified sequential relationships. +CALL +{ +WITH startCUI,reltypes +MATCH (cStart:Concept {CUI:startCUI}) +CALL apoc.path.expandConfig(cStart, + {relationshipFilter:apoc.text.join([x IN reltypes | x], ">,"), beginSequenceAtStart: true,minLevel: 1, maxLevel: size(reltypes)}) +YIELD path +return path +} +WITH path,reltypes,relsabs +WHERE length(path) = size(reltypes) + +// SECOND FILTER: sequence of relationship SABs. +// Filter paths to those in which the SAB properties of relationships occur in the same order as the the specified sequence. +CALL +{ +WITH path,relsabs +UNWIND(path) as path_check +UNWIND(relationships(path_check)) AS path_check_rels +RETURN COLLECT(path_check_rels.SAB) AS path_rel_sabs +} + +WITH path,path_rel_sabs +WHERE path_rel_sabs=relsabs +WITH path +SKIP 0 LIMIT 1 +//For the filtered set of paths, + +// 1. Obtain an "edges" object with information on all relationships in all paths. +// 2. Obtain a "paths" object with path information on all paths. + +UNWIND(relationships(path)) AS r +WITH path,collect({type:type(r),SAB:r.SAB,source:startNode(r).CUI,target:endNode(r).CUI}) AS path_r +WITH collect(path) as paths, apoc.coll.toSet(apoc.coll.flatten(COLLECT(path_r))) AS edges + +// 3. Obtain a "nodes" object for all Concept nodes in all paths +UNWIND(paths) AS path +UNWIND(nodes(path)) AS n +// Obtain preferred terms for Concept nodes. +OPTIONAL MATCH (n)-[:PREF_TERM]->(t:Term) + +WITH paths,edges,collect(DISTINCT{id:n.CUI,name:t.name}) AS nodes +RETURN {nodes:nodes, paths:paths, edges:edges} AS graph From a20decd98428721c7e1eae3f64d25d75f60283d8 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Thu, 25 Jul 2024 09:50:35 -0400 Subject: [PATCH 04/13] new /concepts//subgraphs/sequential endpoint --- .../concepts/concepts_controller.py | 2 +- tests/test_ubkg_api.sh | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/ubkg_api/common_routes/concepts/concepts_controller.py b/src/ubkg_api/common_routes/concepts/concepts_controller.py index a9674a9..e0f577d 100644 --- a/src/ubkg_api/common_routes/concepts/concepts_controller.py +++ b/src/ubkg_api/common_routes/concepts/concepts_controller.py @@ -481,7 +481,7 @@ def concepts_paths_subraphs_sequential_expand_get(concept_id, relsequence=None): err = f'Invalid parameter value: {rs}. Format relationships as :' return make_response(err, 400) - relsabs.append(rs.split(':')[0]) + relsabs.append(rs.split(':')[0].upper()) reltypes.append(rs.split(':')[1]) diff --git a/tests/test_ubkg_api.sh b/tests/test_ubkg_api.sh index e5cd0e1..f979cf4 100755 --- a/tests/test_ubkg_api.sh +++ b/tests/test_ubkg_api.sh @@ -254,6 +254,76 @@ curl --request GET \ echo | tee -a test.out echo | tee -a test.out +#-------------------------------------------- +echo "TESTS FOR: concepts//paths/subgraphs/sequential GET" | tee -a test.out +echo "SIGNATURE: /conepts//paths/subgraphs/sequential?relsequence=&limit=" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. concepts/C0006142/paths/subgraphs/sequential?test=x => invalid parameter; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?test=x" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "2. concepts/C0006142/paths/subgraphs/sequential?relsequence=x&skip=0&limit=5 => invalid relsequence format; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=x&skip=0&limit=5" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "3. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "4. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "5. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "6. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=-1" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "6. concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5 => invalid CUI; should return custom 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "7. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5 => valid, with list; should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "8. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + + #-------------------------------------------- echo "TESTS FOR: concepts//nodeobjects GET" | tee -a test.out echo "SIGNATURE: /conepts//nodeobjects" | tee -a test.out From e925de7fbdf9f55733ada16d4dc8274172604ea3 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Thu, 25 Jul 2024 10:28:59 -0400 Subject: [PATCH 05/13] new /concepts//subgraphs/sequential endpoint --- dd-api-spec.yaml | 46 +++++++++++++++++++++++++ tests/test_dd_api.sh | 78 ++++++++++++++++++++++++++++++++++++++++++ tests/test_ubkg_api.sh | 17 +++++---- ubkg-api-spec.yaml | 46 +++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 9 deletions(-) diff --git a/dd-api-spec.yaml b/dd-api-spec.yaml index 1bb2bae..18f6ebc 100644 --- a/dd-api-spec.yaml +++ b/dd-api-spec.yaml @@ -404,6 +404,52 @@ paths: description: No subgraph for specified relationship types. '5XX': description: Unknown error + /concepts/{concept_id}/paths/subgraphs/sequential: + get: + operationId: concepts_concept_id_paths_subgraphs_sequential_get + summary: Returns a set of paths that originate from the specified Concept with a specified sequence of relationships, subject to constraints specified in parameters. + parameters: + - name: concept_id + in: path + required: true + description: The identifier (also known as Concept Unique Identifier, or CUI) for the Concept from which paths originate. + schema: + type: string + example: C0006142 + - name: relsequence + in: query + required: true + description: A sequential set of relationships that specifies the pattern of relationships in the paths in the subgraph. Each element in the set should be in format SAB:relationship type. SAB corresponds to the source in which the relationship was asserted--e.g., "NCI:is_marked_by_gene_product" corresponds to the "is_marked_by_gene_product" relationship asserted in NCI. The set can be specified with a list of values delimited with commas (e.g., ?relsequence=SAB1:relationshiptype1,SAB2:relationshiptype2) or with individual key-value pairs (e.g., ?relsequence=SAB1:relationship_type1&relsequence=SAB2:relationshiptype2). + schema: + type: string + example: NCI:is_marked_by_gene_product,NCI:3Agene_product_encoded_by_gene + - name: skip + in: query + required: false + description: the number of paths to skip in the returned set. The value must be non-negative. The default value is 0. + schema: + type: string + example: 0 + - name: limit + in: query + required: false + description: the maximum number of paths to return. The value must be non-negative. The default value is the maximum number of rows specified by configuration. + schema: + type: string + example: 10 + responses: + '200': + description: Return the graph of all Concepts in paths that originate with the specified Concept node with specified sequential relationships, subject to constraints. The schema of the response corresponds to the Table result frame of the endpoint query in the neo4j browser. + content: + application/json: + schema: + $ref: '#/components/schemas/ConceptPaths' + '400': + description: invalid parameter name; missing required parameter name; non-numeric parameter value; negative value; mindepth > maxdepth + '404': + description: No Concepts with paths originating from the specified concept and set of parameters. Includes responses from queries with execution times that exceed the maximum. + '5XX': + description: Unknown error /concepts/{origin_concept_id}/paths/shortestpath/{terminus_concept_id}: get: operationId: concepts_shortestpath_get diff --git a/tests/test_dd_api.sh b/tests/test_dd_api.sh index 58beb09..7ede068 100755 --- a/tests/test_dd_api.sh +++ b/tests/test_dd_api.sh @@ -58,6 +58,7 @@ case "$env" in esac # UBKG_URL=$UBKG_URL_LOCAL +echo "DATA DISTILLERY API" | tee test.out echo "Using UBKG at: ${UBKG_URL}" | tee test.out echo "Only the first 60 characters of output from HTTP 200 returns displayed." @@ -283,6 +284,83 @@ curl --request GET \ echo | tee -a test.out echo | tee -a test.out +#-------------------------------------------- +echo "TESTS FOR: concepts//paths/subgraphs/sequential GET" | tee -a test.out +echo "SIGNATURE: /conepts//paths/subgraphs/sequential?relsequence=&limit=" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. concepts/C0006142/paths/subgraphs/sequential?test=x => invalid parameter; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?test=x" \ + --header "Accept: application/json" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "2. concepts/C0006142/paths/subgraphs/sequential?relsequence=x&skip=0&limit=5 => invalid relsequence format; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=x&skip=0&limit=5" \ + --header "Accept: application/json" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "3. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --header "Accept: application/json" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "4. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --header "Accept: application/json" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "5. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ + --header "Accept: application/json" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "6. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1" \ + --header "Accept: application/json" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "7. concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => invalid CUI; should return custom 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ + --header "Accept: application/json" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "8. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with list; should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ + --header "Accept: application/json" \ + --header "Authorization: UMLS-Key $umlskey" | cut -c1-60 | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "9. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ + --header "Authorization: UMLS-Key $umlskey" | cut -c1-60 | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + #-------------------------------------------- echo "TESTS FOR: concepts//nodeobjects GET" | tee -a test.out echo "SIGNATURE: /conepts//nodeobjects" | tee -a test.out diff --git a/tests/test_ubkg_api.sh b/tests/test_ubkg_api.sh index f979cf4..1e1b675 100755 --- a/tests/test_ubkg_api.sh +++ b/tests/test_ubkg_api.sh @@ -274,56 +274,55 @@ curl --request GET \ echo | tee -a test.out echo | tee -a test.out -echo "3. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out +echo "3. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out curl --request GET \ --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "4. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out +echo "4. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "5. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out +echo "5. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out curl --request GET \ --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "6. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out +echo "6. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out curl --request GET \ --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=-1" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "6. concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5 => invalid CUI; should return custom 404" | tee -a test.out +echo "7. concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => invalid CUI; should return custom 404" | tee -a test.out curl --request GET \ --url "${UBKG_URL}/concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "7. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5 => valid, with list; should return 200" | tee -a test.out +echo "8. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with list; should return 200" | tee -a test.out curl --request GET \ --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" | cut -c1-60 | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "8. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +echo "9. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out curl --request GET \ --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" | cut -c1-60 | tee -a test.out echo | tee -a test.out echo | tee -a test.out - #-------------------------------------------- echo "TESTS FOR: concepts//nodeobjects GET" | tee -a test.out echo "SIGNATURE: /conepts//nodeobjects" | tee -a test.out diff --git a/ubkg-api-spec.yaml b/ubkg-api-spec.yaml index 6b361ae..54de2d2 100644 --- a/ubkg-api-spec.yaml +++ b/ubkg-api-spec.yaml @@ -402,6 +402,52 @@ paths: description: No subgraph for specified relationship types. '5XX': description: Unknown error + /concepts/{concept_id}/paths/subgraphs/sequential: + get: + operationId: concepts_concept_id_paths_subgraphs_sequential_get + summary: Returns a set of paths that originate from the specified Concept with a specified sequence of relationships, subject to constraints specified in parameters. + parameters: + - name: concept_id + in: path + required: true + description: The identifier (also known as Concept Unique Identifier, or CUI) for the Concept from which paths originate. + schema: + type: string + example: C0006142 + - name: relsequence + in: query + required: true + description: A sequential set of relationships that specifies the pattern of relationships in the paths in the subgraph. Each element in the set should be in format SAB:relationship type. SAB corresponds to the source in which the relationship was asserted--e.g., "NCI:is_marked_by_gene_product" corresponds to the "is_marked_by_gene_product" relationship asserted in NCI. The set can be specified with a list of values delimited with commas (e.g., ?relsequence=SAB1:relationshiptype1,SAB2:relationshiptype2) or with individual key-value pairs (e.g., ?relsequence=SAB1:relationship_type1&relsequence=SAB2:relationshiptype2). + schema: + type: string + example: NCI:is_marked_by_gene_product,NCI:3Agene_product_encoded_by_gene + - name: skip + in: query + required: false + description: the number of paths to skip in the returned set. The value must be non-negative. The default value is 0. + schema: + type: string + example: 0 + - name: limit + in: query + required: false + description: the maximum number of paths to return. The value must be non-negative. The default value is the maximum number of rows specified by configuration. + schema: + type: string + example: 10 + responses: + '200': + description: Return the graph of all Concepts in paths that originate with the specified Concept node with specified sequential relationships, subject to constraints. The schema of the response corresponds to the Table result frame of the endpoint query in the neo4j browser. + content: + application/json: + schema: + $ref: '#/components/schemas/ConceptPaths' + '400': + description: invalid parameter name; missing required parameter name; non-numeric parameter value; negative value; mindepth > maxdepth + '404': + description: No Concepts with paths originating from the specified concept and set of parameters. Includes responses from queries with execution times that exceed the maximum. + '5XX': + description: Unknown error /concepts/{origin_concept_id}/paths/shortestpath/{terminus_concept_id}: get: operationId: concepts_shortestpath_get From 07a73e696e16a3f6102fe3bff69f2f0b8c6a22cc Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Fri, 26 Jul 2024 16:09:19 -0400 Subject: [PATCH 06/13] new /concepts/subgraphs/sequential endpoint --- .../common_routes/common_neo4j_logic.py | 1 + .../concepts/concepts_controller.py | 17 ++++++++-- .../concepts_subgraph_sequential.cypher | 31 +++++++++++++------ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/ubkg_api/common_routes/common_neo4j_logic.py b/src/ubkg_api/common_routes/common_neo4j_logic.py index 9a8f97b..130d480 100644 --- a/src/ubkg_api/common_routes/common_neo4j_logic.py +++ b/src/ubkg_api/common_routes/common_neo4j_logic.py @@ -1163,6 +1163,7 @@ def concepts_subgraph_sequential_get_logic(neo4j_instance, startCUI=None, reltyp querytxt = querytxt.replace('$skip', str(skip)) querytxt = querytxt.replace('$limit', str(limit)) + print(querytxt) # Set timeout for query based on value in app.cfg. query = neo4j.Query(text=querytxt, timeout=neo4j_instance.timeout) diff --git a/src/ubkg_api/common_routes/concepts/concepts_controller.py b/src/ubkg_api/common_routes/concepts/concepts_controller.py index e0f577d..c773892 100644 --- a/src/ubkg_api/common_routes/concepts/concepts_controller.py +++ b/src/ubkg_api/common_routes/concepts/concepts_controller.py @@ -424,14 +424,25 @@ def concepts_concept_identifier_nodes_get(search): dict_result = {'nodeobjects': result} return jsonify(dict_result) +@concepts_blueprint.route('/paths/subgraphs/sequential', methods=['GET']) +def concepts_paths_subgraphs_sequential_get_endpoint(): + return concepts_paths_subraphs_sequential_get(concept_id=None) + @concepts_blueprint.route('/paths/subgraphs/sequential', methods=['GET']) -def concepts_paths_subraphs_sequential_expand_get(concept_id, relsequence=None): +def concepts_paths_subgraphs_name_sequential_get_endpoint(concept_id): + return concepts_paths_subraphs_sequential_get(concept_id=concept_id) + +def concepts_paths_subraphs_sequential_get(concept_id=None): """ Returns the set of paths that begins with the concept and has relationships in a specified - sequence. Response is in neo4j graph format ({nodes, paths, edges}). + sequence. + + If no concept_id is specified, then return all paths that begin with the first specified relationship. + + Response is in neo4j graph format ({nodes, paths, edges}). - :param relsequence: an ordered list that specifies a sequence of relationships in a path. + The relsequence request parameter is an ordered list that specifies a sequence of relationships in a path. The format of each element in relsequence is :. For example, ['UBERON:isa','PATO:has_part'] specifies the set of paths that start from the concept with CUI with relationships that match the pattern diff --git a/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher b/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher index f5c90bf..d875178 100644 --- a/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher +++ b/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher @@ -5,15 +5,11 @@ // 1st level - "isa" relationship from UBERON // 2nd level - "has_part" relationship from PATO -// Two independent filters are required: -// 1. sequential relationship types (e.g., ["isa","has_part"]) -// 2. sequential SAB properties (e.g. ["UBERON","PATO"]) - - -// Selection parameters supplied by the calling function. -// 1. Code corresponding to the starting concept of the expansion. -//WITH 'PR:P11087 CUI' AS startCUI, -WITH $startCUI AS startCUI, +// SELECTION PARAMETERS +// Supplied by the calling function. +// 1. CUI corresponding to the starting concept of the expansion. +// If no CUI was supplied, query for all CUIs that have the first relationship. +WITH $startCUI AS initCUI, // 2. Sequence of relationship types //['isa','only_in_taxon','has_part'] as reltypes, [$reltypes] AS reltypes, @@ -21,6 +17,23 @@ WITH $startCUI AS startCUI, //['CL','MP','EFO'] as relsabs, [$relsabs] AS relsabs +// SELECTION OF STARTING CUIS +// Obtain either the CUI provided by the parameter OR the CUIs for all concepts that have the first relationship +// of the sequence. +CALL{ + WITH initCUI,reltypes, relsabs + MATCH (cStart:Concept)-[rStart]->(c1:Concept) + WHERE TYPE(rStart) = reltypes[0] + AND rStart.SAB = relsabs[0] + AND CASE WHEN initCUI IS NULL THEN cStart.CUI=initCUI ELSE 1=1 END + RETURN DISTINCT cStart.CUI AS startCUI +} + +// FILTERING +// Two independent filters are required: +// 1. sequential relationship types (e.g., ["isa","has_part"]) +// 2. sequential SAB properties (e.g. ["UBERON","PATO"]) + // FIRST FILTER: sequence of relationship types. // Expand from the starting concept, obtaining only those paths that exactly match the specified sequence of relationship types. // Because the relationshipFilter specifies a sequence, relevant paths have length equal to the number of specified sequential relationships. From 041a2a7b61b2786b29d8e3f6acb3a1ed9feffab0 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Fri, 26 Jul 2024 18:19:20 -0400 Subject: [PATCH 07/13] new /concepts/subgraphs/sequential endpoint --- dd-api-spec.yaml | 39 +++++++++++++++++++ .../concepts_subgraph_sequential.cypher | 3 +- ubkg-api-spec.yaml | 39 +++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/dd-api-spec.yaml b/dd-api-spec.yaml index 18f6ebc..6cb0ed3 100644 --- a/dd-api-spec.yaml +++ b/dd-api-spec.yaml @@ -404,6 +404,45 @@ paths: description: No subgraph for specified relationship types. '5XX': description: Unknown error + /concepts/paths/subgraphs/sequential: + get: + operationId: concepts_paths_subgraphs_sequential_get + summary: Returns a set of paths with a specified sequence of relationships, subject to constraints specified in parameters. + parameters: + - name: relsequence + in: query + required: true + description: A sequential set of relationships that specifies the pattern of relationships in the paths in the subgraph. Each element in the set should be in format SAB:relationship type. SAB corresponds to the source in which the relationship was asserted--e.g., "NCI:is_marked_by_gene_product" corresponds to the "is_marked_by_gene_product" relationship asserted in NCI. The set can be specified with a list of values delimited with commas (e.g., ?relsequence=SAB1:relationshiptype1,SAB2:relationshiptype2) or with individual key-value pairs (e.g., ?relsequence=SAB1:relationship_type1&relsequence=SAB2:relationshiptype2). + schema: + type: string + example: NCI:is_marked_by_gene_product,NCI:3Agene_product_encoded_by_gene + - name: skip + in: query + required: false + description: the number of paths to skip in the returned set. The value must be non-negative. The default value is 0. + schema: + type: string + example: 0 + - name: limit + in: query + required: false + description: the maximum number of paths to return. The value must be non-negative. The default value is the maximum number of rows specified by configuration. + schema: + type: string + example: 10 + responses: + '200': + description: Return the graph of all Concepts in paths with specified sequential relationships, subject to constraints. The schema of the response corresponds to the Table result frame of the endpoint query in the neo4j browser. + content: + application/json: + schema: + $ref: '#/components/schemas/ConceptPaths' + '400': + description: invalid parameter name; missing required parameter name; non-numeric parameter value; negative value; mindepth > maxdepth + '404': + description: No Concepts with paths originating from the specified concept and set of parameters. Includes responses from queries with execution times that exceed the maximum. + '5XX': + description: Unknown error /concepts/{concept_id}/paths/subgraphs/sequential: get: operationId: concepts_concept_id_paths_subgraphs_sequential_get diff --git a/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher b/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher index d875178..c962126 100644 --- a/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher +++ b/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher @@ -27,6 +27,7 @@ CALL{ AND rStart.SAB = relsabs[0] AND CASE WHEN initCUI IS NULL THEN cStart.CUI=initCUI ELSE 1=1 END RETURN DISTINCT cStart.CUI AS startCUI + ORDER BY cStart.CUI } // FILTERING @@ -62,7 +63,7 @@ RETURN COLLECT(path_check_rels.SAB) AS path_rel_sabs WITH path,path_rel_sabs WHERE path_rel_sabs=relsabs WITH path -SKIP 0 LIMIT 1 +SKIP $skip LIMIT $limit //For the filtered set of paths, // 1. Obtain an "edges" object with information on all relationships in all paths. diff --git a/ubkg-api-spec.yaml b/ubkg-api-spec.yaml index 54de2d2..4822a6e 100644 --- a/ubkg-api-spec.yaml +++ b/ubkg-api-spec.yaml @@ -402,6 +402,45 @@ paths: description: No subgraph for specified relationship types. '5XX': description: Unknown error + /concepts/paths/subgraphs/sequential: + get: + operationId: concepts_paths_subgraphs_sequential_get + summary: Returns a set of paths with a specified sequence of relationships, subject to constraints specified in parameters. + parameters: + - name: relsequence + in: query + required: true + description: A sequential set of relationships that specifies the pattern of relationships in the paths in the subgraph. Each element in the set should be in format SAB:relationship type. SAB corresponds to the source in which the relationship was asserted--e.g., "NCI:is_marked_by_gene_product" corresponds to the "is_marked_by_gene_product" relationship asserted in NCI. The set can be specified with a list of values delimited with commas (e.g., ?relsequence=SAB1:relationshiptype1,SAB2:relationshiptype2) or with individual key-value pairs (e.g., ?relsequence=SAB1:relationship_type1&relsequence=SAB2:relationshiptype2). + schema: + type: string + example: NCI:is_marked_by_gene_product,NCI:3Agene_product_encoded_by_gene + - name: skip + in: query + required: false + description: the number of paths to skip in the returned set. The value must be non-negative. The default value is 0. + schema: + type: string + example: 0 + - name: limit + in: query + required: false + description: the maximum number of paths to return. The value must be non-negative. The default value is the maximum number of rows specified by configuration. + schema: + type: string + example: 10 + responses: + '200': + description: Return the graph of all Concepts in paths with specified sequential relationships, subject to constraints. The schema of the response corresponds to the Table result frame of the endpoint query in the neo4j browser. + content: + application/json: + schema: + $ref: '#/components/schemas/ConceptPaths' + '400': + description: invalid parameter name; missing required parameter name; non-numeric parameter value; negative value; mindepth > maxdepth + '404': + description: No Concepts with paths originating from the specified concept and set of parameters. Includes responses from queries with execution times that exceed the maximum. + '5XX': + description: Unknown error /concepts/{concept_id}/paths/subgraphs/sequential: get: operationId: concepts_concept_id_paths_subgraphs_sequential_get From ca7ee832e6c089d238e976af79df08e29ba57dc7 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Fri, 26 Jul 2024 18:24:55 -0400 Subject: [PATCH 08/13] new /concepts/subgraphs/sequential endpoint --- tests/test_dd_api.sh | 16 ++++++++++++++++ tests/test_ubkg_api.sh | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tests/test_dd_api.sh b/tests/test_dd_api.sh index 7ede068..171f440 100755 --- a/tests/test_dd_api.sh +++ b/tests/test_dd_api.sh @@ -361,6 +361,22 @@ curl --request GET \ echo | tee -a test.out echo | tee -a test.out +#-------------------------------------------- +# The /concepts/paths/subgraphs/sequential endpoint uses the same code as the +# /concepts//paths/subgraphs/sequential endpoint. + +echo "TEST FOR: concepts/paths/subgraphs/sequential GET" | tee -a test.out +echo "SIGNATURE: /conepts/paths/subgraphs/sequential?relsequence=&limit=" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "concepts/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ + --header "Authorization: UMLS-Key $umlskey" | cut -c1-60 | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + #-------------------------------------------- echo "TESTS FOR: concepts//nodeobjects GET" | tee -a test.out echo "SIGNATURE: /conepts//nodeobjects" | tee -a test.out diff --git a/tests/test_ubkg_api.sh b/tests/test_ubkg_api.sh index 1e1b675..f2fc285 100755 --- a/tests/test_ubkg_api.sh +++ b/tests/test_ubkg_api.sh @@ -323,6 +323,21 @@ curl --request GET \ echo | tee -a test.out echo | tee -a test.out +#-------------------------------------------- +# The /concepts/paths/subgraphs/sequential endpoint uses the same code as the +# /concepts//paths/subgraphs/sequential endpoint. +#-------------------------------------------- +echo "TESTS FOR: concepts//paths/subgraphs/sequential GET" | tee -a test.out +echo "SIGNATURE: /conepts//paths/subgraphs/sequential?relsequence=&limit=" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out +echo "1. concepts/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/concepts/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ + --header "Accept: application/json" | cut -c1-60 | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + #-------------------------------------------- echo "TESTS FOR: concepts//nodeobjects GET" | tee -a test.out echo "SIGNATURE: /conepts//nodeobjects" | tee -a test.out From df8b55716fef805790ce06a146f6f7e5cb9c68bf Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Fri, 26 Jul 2024 19:07:39 -0400 Subject: [PATCH 09/13] new /concepts/subgraphs/sequential endpoint --- src/ubkg_api/common_routes/common_neo4j_logic.py | 1 - src/ubkg_api/cypher/concepts_subgraph_sequential.cypher | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ubkg_api/common_routes/common_neo4j_logic.py b/src/ubkg_api/common_routes/common_neo4j_logic.py index 130d480..9a8f97b 100644 --- a/src/ubkg_api/common_routes/common_neo4j_logic.py +++ b/src/ubkg_api/common_routes/common_neo4j_logic.py @@ -1163,7 +1163,6 @@ def concepts_subgraph_sequential_get_logic(neo4j_instance, startCUI=None, reltyp querytxt = querytxt.replace('$skip', str(skip)) querytxt = querytxt.replace('$limit', str(limit)) - print(querytxt) # Set timeout for query based on value in app.cfg. query = neo4j.Query(text=querytxt, timeout=neo4j_instance.timeout) diff --git a/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher b/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher index c962126..419d4a2 100644 --- a/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher +++ b/src/ubkg_api/cypher/concepts_subgraph_sequential.cypher @@ -25,7 +25,7 @@ CALL{ MATCH (cStart:Concept)-[rStart]->(c1:Concept) WHERE TYPE(rStart) = reltypes[0] AND rStart.SAB = relsabs[0] - AND CASE WHEN initCUI IS NULL THEN cStart.CUI=initCUI ELSE 1=1 END + AND CASE WHEN initCUI <> "None" THEN cStart.CUI=initCUI ELSE 1=1 END RETURN DISTINCT cStart.CUI AS startCUI ORDER BY cStart.CUI } From a0bb0216a8e2fd27f9a773df4f41635893973083 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Sun, 28 Jul 2024 15:12:24 -0400 Subject: [PATCH 10/13] new /concepts/subgraph/sequential endpoints --- dd-api-spec.yaml | 8 ++++---- .../common_routes/concepts/concepts_controller.py | 4 ++-- ubkg-api-spec.yaml | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dd-api-spec.yaml b/dd-api-spec.yaml index 6cb0ed3..31069b4 100644 --- a/dd-api-spec.yaml +++ b/dd-api-spec.yaml @@ -404,9 +404,9 @@ paths: description: No subgraph for specified relationship types. '5XX': description: Unknown error - /concepts/paths/subgraphs/sequential: + /concepts/paths/subgraph/sequential: get: - operationId: concepts_paths_subgraphs_sequential_get + operationId: concepts_paths_subgraph_sequential_get summary: Returns a set of paths with a specified sequence of relationships, subject to constraints specified in parameters. parameters: - name: relsequence @@ -443,9 +443,9 @@ paths: description: No Concepts with paths originating from the specified concept and set of parameters. Includes responses from queries with execution times that exceed the maximum. '5XX': description: Unknown error - /concepts/{concept_id}/paths/subgraphs/sequential: + /concepts/{concept_id}/paths/subgraph/sequential: get: - operationId: concepts_concept_id_paths_subgraphs_sequential_get + operationId: concepts_concept_id_paths_subgraph_sequential_get summary: Returns a set of paths that originate from the specified Concept with a specified sequence of relationships, subject to constraints specified in parameters. parameters: - name: concept_id diff --git a/src/ubkg_api/common_routes/concepts/concepts_controller.py b/src/ubkg_api/common_routes/concepts/concepts_controller.py index c773892..fe3efda 100644 --- a/src/ubkg_api/common_routes/concepts/concepts_controller.py +++ b/src/ubkg_api/common_routes/concepts/concepts_controller.py @@ -424,11 +424,11 @@ def concepts_concept_identifier_nodes_get(search): dict_result = {'nodeobjects': result} return jsonify(dict_result) -@concepts_blueprint.route('/paths/subgraphs/sequential', methods=['GET']) +@concepts_blueprint.route('/paths/subgraph/sequential', methods=['GET']) def concepts_paths_subgraphs_sequential_get_endpoint(): return concepts_paths_subraphs_sequential_get(concept_id=None) -@concepts_blueprint.route('/paths/subgraphs/sequential', methods=['GET']) +@concepts_blueprint.route('/paths/subgraph/sequential', methods=['GET']) def concepts_paths_subgraphs_name_sequential_get_endpoint(concept_id): return concepts_paths_subraphs_sequential_get(concept_id=concept_id) diff --git a/ubkg-api-spec.yaml b/ubkg-api-spec.yaml index 4822a6e..699411f 100644 --- a/ubkg-api-spec.yaml +++ b/ubkg-api-spec.yaml @@ -402,9 +402,9 @@ paths: description: No subgraph for specified relationship types. '5XX': description: Unknown error - /concepts/paths/subgraphs/sequential: + /concepts/paths/subgraph/sequential: get: - operationId: concepts_paths_subgraphs_sequential_get + operationId: concepts_paths_subgraph_sequential_get summary: Returns a set of paths with a specified sequence of relationships, subject to constraints specified in parameters. parameters: - name: relsequence @@ -441,9 +441,9 @@ paths: description: No Concepts with paths originating from the specified concept and set of parameters. Includes responses from queries with execution times that exceed the maximum. '5XX': description: Unknown error - /concepts/{concept_id}/paths/subgraphs/sequential: + /concepts/{concept_id}/paths/subgraph/sequential: get: - operationId: concepts_concept_id_paths_subgraphs_sequential_get + operationId: concepts_concept_id_paths_subgraph_sequential_get summary: Returns a set of paths that originate from the specified Concept with a specified sequence of relationships, subject to constraints specified in parameters. parameters: - name: concept_id From e0f75d251acb6825243eac7580fbbd194ea1a6b6 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Sun, 28 Jul 2024 15:15:27 -0400 Subject: [PATCH 11/13] new /concepts/subgraph/sequential endpoints --- tests/test_dd_api.sh | 52 +++++++++++++++++++++--------------------- tests/test_ubkg_api.sh | 52 +++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/tests/test_dd_api.sh b/tests/test_dd_api.sh index 171f440..b66379d 100755 --- a/tests/test_dd_api.sh +++ b/tests/test_dd_api.sh @@ -285,94 +285,94 @@ echo | tee -a test.out echo | tee -a test.out #-------------------------------------------- -echo "TESTS FOR: concepts//paths/subgraphs/sequential GET" | tee -a test.out -echo "SIGNATURE: /conepts//paths/subgraphs/sequential?relsequence=&limit=" | tee -a test.out +echo "TESTS FOR: concepts//paths/subgraph/sequential GET" | tee -a test.out +echo "SIGNATURE: /conepts//paths/subgraph/sequential?relsequence=&limit=" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "1. concepts/C0006142/paths/subgraphs/sequential?test=x => invalid parameter; should return custom 400" | tee -a test.out +echo "1. concepts/C0006142/paths/subgraph/sequential?test=x => invalid parameter; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?test=x" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?test=x" \ --header "Accept: application/json" \ --header "Authorization: UMLS-Key $umlskey" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "2. concepts/C0006142/paths/subgraphs/sequential?relsequence=x&skip=0&limit=5 => invalid relsequence format; should return custom 400" | tee -a test.out +echo "2. concepts/C0006142/paths/subgraph/sequential?relsequence=x&skip=0&limit=5 => invalid relsequence format; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=x&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=x&skip=0&limit=5" \ --header "Accept: application/json" \ --header "Authorization: UMLS-Key $umlskey" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "3. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out +echo "3. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ --header "Accept: application/json" \ --header "Authorization: UMLS-Key $umlskey" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "4. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out +echo "4. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ --header "Accept: application/json" \ --header "Authorization: UMLS-Key $umlskey" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "5. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out +echo "5. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ --header "Accept: application/json" \ --header "Authorization: UMLS-Key $umlskey" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "6. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out +echo "6. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1" \ --header "Accept: application/json" \ --header "Authorization: UMLS-Key $umlskey" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "7. concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => invalid CUI; should return custom 404" | tee -a test.out +echo "7. concepts/C0006142X/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => invalid CUI; should return custom 404" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142X/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" \ --header "Authorization: UMLS-Key $umlskey" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "8. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with list; should return 200" | tee -a test.out +echo "8. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with list; should return 200" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" \ --header "Authorization: UMLS-Key $umlskey" | cut -c1-60 | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "9. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +echo "9. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ --header "Authorization: UMLS-Key $umlskey" | cut -c1-60 | tee -a test.out echo | tee -a test.out echo | tee -a test.out #-------------------------------------------- -# The /concepts/paths/subgraphs/sequential endpoint uses the same code as the -# /concepts//paths/subgraphs/sequential endpoint. +# The /concepts/paths/subgraph/sequential endpoint uses the same code as the +# /concepts//paths/subgraph/sequential endpoint. -echo "TEST FOR: concepts/paths/subgraphs/sequential GET" | tee -a test.out -echo "SIGNATURE: /conepts/paths/subgraphs/sequential?relsequence=&limit=" | tee -a test.out +echo "TEST FOR: concepts/paths/subgraph/sequential GET" | tee -a test.out +echo "SIGNATURE: /conepts/paths/subgraph/sequential?relsequence=&limit=" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "concepts/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +echo "concepts/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5" \ --header "Authorization: UMLS-Key $umlskey" | cut -c1-60 | tee -a test.out echo | tee -a test.out echo | tee -a test.out diff --git a/tests/test_ubkg_api.sh b/tests/test_ubkg_api.sh index f2fc285..ae6c23f 100755 --- a/tests/test_ubkg_api.sh +++ b/tests/test_ubkg_api.sh @@ -255,85 +255,85 @@ echo | tee -a test.out echo | tee -a test.out #-------------------------------------------- -echo "TESTS FOR: concepts//paths/subgraphs/sequential GET" | tee -a test.out -echo "SIGNATURE: /conepts//paths/subgraphs/sequential?relsequence=&limit=" | tee -a test.out +echo "TESTS FOR: concepts//paths/subgraph/sequential GET" | tee -a test.out +echo "SIGNATURE: /conepts//paths/subgraph/sequential?relsequence=&limit=" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "1. concepts/C0006142/paths/subgraphs/sequential?test=x => invalid parameter; should return custom 400" | tee -a test.out +echo "1. concepts/C0006142/paths/subgraph/sequential?test=x => invalid parameter; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?test=x" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?test=x" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "2. concepts/C0006142/paths/subgraphs/sequential?relsequence=x&skip=0&limit=5 => invalid relsequence format; should return custom 400" | tee -a test.out +echo "2. concepts/C0006142/paths/subgraph/sequential?relsequence=x&skip=0&limit=5 => invalid relsequence format; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=x&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=x&skip=0&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "3. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out +echo "3. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "4. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out +echo "4. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequentialrelsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "5. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out +echo "5. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "6. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out +echo "6. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=-1" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=-1" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "7. concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => invalid CUI; should return custom 404" | tee -a test.out +echo "7. concepts/C0006142X/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => invalid CUI; should return custom 404" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142X/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142X/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "8. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with list; should return 200" | tee -a test.out +echo "8. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product,NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with list; should return 200" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product,NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" | cut -c1-60 | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "9. concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +echo "9. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" | cut -c1-60 | tee -a test.out echo | tee -a test.out echo | tee -a test.out #-------------------------------------------- -# The /concepts/paths/subgraphs/sequential endpoint uses the same code as the -# /concepts//paths/subgraphs/sequential endpoint. +# The /concepts/paths/subgraph/sequential endpoint uses the same code as the +# /concepts//paths/subgraph/sequential endpoint. #-------------------------------------------- -echo "TESTS FOR: concepts//paths/subgraphs/sequential GET" | tee -a test.out -echo "SIGNATURE: /conepts//paths/subgraphs/sequential?relsequence=&limit=" | tee -a test.out +echo "TESTS FOR: concepts//paths/subgraph/sequential GET" | tee -a test.out +echo "SIGNATURE: /conepts//paths/subgraph/sequential?relsequence=&limit=" | tee -a test.out echo | tee -a test.out echo | tee -a test.out -echo "1. concepts/paths/subgraphs/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out +echo "1. concepts/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&relsequence=NCI%3Agene_product_encoded_by_gene&skip=0&limit=5 => valid, with individual; should return 200" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/paths/subgraphs/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ + --url "${UBKG_URL}/concepts/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product&relsequence=NCI:gene_product_encoded_by_gene&skip=0&limit=5" \ --header "Accept: application/json" | cut -c1-60 | tee -a test.out echo | tee -a test.out echo | tee -a test.out From f6a698f4750413c3618b2e0ca38ff03f1d7a7070 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Sun, 28 Jul 2024 15:46:36 -0400 Subject: [PATCH 12/13] tests for codes/{code_id}/terms --- .../common_routes/common_neo4j_logic.py | 1 - tests/test_dd_api.sh | 27 ++++++++++++++++++ tests/test_ubkg_api.sh | 28 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/ubkg_api/common_routes/common_neo4j_logic.py b/src/ubkg_api/common_routes/common_neo4j_logic.py index 9a8f97b..52b90c6 100644 --- a/src/ubkg_api/common_routes/common_neo4j_logic.py +++ b/src/ubkg_api/common_routes/common_neo4j_logic.py @@ -1111,7 +1111,6 @@ def codes_code_id_terms_get_logic(neo4j_instance,code_id: str, term_type=None) - # Set timeout for query based on value in app.cfg. query = neo4j.Query(text=querytxt, timeout=neo4j_instance.timeout) - with neo4j_instance.driver.session() as session: recds: neo4j.Result = session.run(query) for record in recds: diff --git a/tests/test_dd_api.sh b/tests/test_dd_api.sh index b66379d..b7196ab 100755 --- a/tests/test_dd_api.sh +++ b/tests/test_dd_api.sh @@ -131,6 +131,33 @@ curl --request GET \ echo | tee -a test.out echo | tee -a test.out +#-------------------------------------------- +echo "TESTS FOR: codes//terms" | tee -a test.out +echo "SIGNATURE: /codes//terms" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. codes/SNOMEDCT_US%3A254837009X/terms => no match; should return custom 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/codes/SNOMEDCT_US%3A254837009X/terms" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "2. codes/SNOMEDCT_US%3A254837009/terms?term_typex=PT => invalid parameter; should return custom 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/codes/SNOMEDCT_US%3A254837009/terms?term_typex=PT" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "3. codes/SNOMEDCT_US%3A254837009/terms?term_type=PT => valid; should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/codes/SNOMEDCT_US%3A254837009/terms?term_type=PT" \ + --header "Authorization: UMLS-Key $umlskey" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + #-------------------------------------------- echo "TESTS FOR: concepts//codes GET" | tee -a test.out echo "SIGNATURE: /concepts//codes"| tee -a test.out diff --git a/tests/test_ubkg_api.sh b/tests/test_ubkg_api.sh index ae6c23f..9fe05ec 100755 --- a/tests/test_ubkg_api.sh +++ b/tests/test_ubkg_api.sh @@ -117,6 +117,34 @@ curl --request GET \ echo | tee -a test.out echo | tee -a test.out +#-------------------------------------------- +echo "TESTS FOR: codes//terms" | tee -a test.out +echo "SIGNATURE: /codes//terms" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "1. codes/SNOMEDCT_US%3A254837009X/terms => no match; should return custom 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/codes/SNOMEDCT_US%3A254837009X/terms" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "2. codes/SNOMEDCT_US%3A254837009/terms?term_typex=PT => invalid parameter; should return custom 404" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/codes/SNOMEDCT_US%3A254837009/terms?term_typex=PT" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + +echo "3. codes/SNOMEDCT_US%3A254837009/terms?term_type=PT => valid; should return 200" | tee -a test.out +curl --request GET \ + --url "${UBKG_URL}/codes/SNOMEDCT_US%3A254837009/terms?term_type=PT" \ + --header "Accept: application/json" | tee -a test.out +echo | tee -a test.out +echo | tee -a test.out + + #-------------------------------------------- echo "TESTS FOR: concepts//codes GET" | tee -a test.out echo "SIGNATURE: /concepts//codes"| tee -a test.out From 3064188f6ed6e0bd5f457f89c885837c692c8db7 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Fri, 2 Aug 2024 16:19:46 -0400 Subject: [PATCH 13/13] fixes for test_ubkg_api.sh for concepts//paths/subgraph/sequential --- tests/test_ubkg_api.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_ubkg_api.sh b/tests/test_ubkg_api.sh index 9fe05ec..44a58bb 100755 --- a/tests/test_ubkg_api.sh +++ b/tests/test_ubkg_api.sh @@ -304,28 +304,28 @@ echo | tee -a test.out echo "3. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5 => skip non-numeric; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product&skip=X&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out echo "4. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=-1&limit=5 => skip negative; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequentialrelsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=X&limit=5" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out echo "5. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=x => limit non-numeric; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=x" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out echo "6. concepts/C0006142/paths/subgraph/sequential?relsequence=NCI%3Ais_marked_by_gene_product&skip=0&limit=-1 => limit negative; should return custom 400" | tee -a test.out curl --request GET \ - --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequentialrelsequence=NCI:is_marked_by_gene_product&skip=0&limit=-1" \ + --url "${UBKG_URL}/concepts/C0006142/paths/subgraph/sequential?relsequence=NCI:is_marked_by_gene_product&skip=0&limit=-1" \ --header "Accept: application/json" | tee -a test.out echo | tee -a test.out echo | tee -a test.out