From 966a05851e434b30fc090bd8e2ab0763fe08634a Mon Sep 17 00:00:00 2001 From: caseyta Date: Tue, 11 Jul 2023 16:08:10 -0700 Subject: [PATCH 1/2] Deprecate OxO endpoints in OpenAPI spec and remove from unit tests and continuous monitoring --- cohd/cohd_oas3.yaml | 12 ++- cohd/test_unit_tests.py | 156 ++++++++++++++-------------- test_cohd_io.py | 220 ++++++++++++++++++++-------------------- 3 files changed, 199 insertions(+), 189 deletions(-) diff --git a/cohd/cohd_oas3.yaml b/cohd/cohd_oas3.yaml index 2c854be..a57f391 100644 --- a/cohd/cohd_oas3.yaml +++ b/cohd/cohd_oas3.yaml @@ -41,7 +41,7 @@ info: [OMOP Common Data Model](https://github.com/OHDSI/CommonDataModel/wiki) [Athena](http://athena.ohdsi.org) (OMOP vocabularies, search, concept relationships, concept hierarchy) [Atlas](http://www.ohdsi.org/web/atlas/) (OMOP vocabularies, search, concept relationships, concept hierarchy, concept sets) - version: 5.0.0 + version: 5.1.0 title: Columbia Open Health Data (COHD) API contact: name: Casey Ta @@ -669,10 +669,15 @@ paths: example: "SNOMED" /omop/xrefToOMOP: get: + deprecated: true tags: - OMOP summary: Cross-reference from an ontology to OMOP standard concepts using the Ontology Xref Service description: >- + This endpoint has been deprecated. COHD is now using Translator SRI services to map from OMOP to Biolink model. + Also, the OxO API has been producing internal server errors in some circumstances for several months. This + endpoint is currently still available but may not function correctly. + Attempts to map a concept from an external ontology to an OMOP standard concept ID using the EMBL-EBI Ontology Xref Service (OxO): https://www.ebi.ac.uk/spot/oxo/index. This method attempts to use OxO to map from the original ontology to an intermediate ontology that is included in OMOP (ICD9CM, ICD10CM, SNOMEDCT, and MeSH), then uses the OMOP mappings to the standard concepts. Multiple mappings may be returned. Results are sorted by total_distance (OxO distance + OMOP distance) in ascending order. parameters: - name: curie @@ -754,10 +759,15 @@ paths: example: 2 /omop/xrefFromOMOP: get: + deprecated: true tags: - OMOP summary: Cross-reference from an ontology to OMOP standard concepts using the Ontology Xref Service description: >- + This endpoint has been deprecated. COHD is now using Translator SRI services to map from OMOP to Biolink model. + Also, the OxO API has been producing internal server errors in some circumstances for several months. This + endpoint is currently still available but may not function correctly. + Attempts to map a concept from an external ontology to an OMOP standard concept ID using the EMBL-EBI Ontology Xref Service (OxO): https://www.ebi.ac.uk/spot/oxo/index. This method maps from the OMOP standard concept to an intermediate vocabulary included is OxO (ICD9CM, ICD10CM, SNOMEDCT, and MeSH), then uses the OxO API to map to other ontologies. Multiple mappings may be returned. Results are sorted by total_distance (OxO distance + OMOP distance) in ascending order. parameters: - name: concept_id diff --git a/cohd/test_unit_tests.py b/cohd/test_unit_tests.py index 2190ec8..a121ad1 100644 --- a/cohd/test_unit_tests.py +++ b/cohd/test_unit_tests.py @@ -272,84 +272,84 @@ def test_omop_vocab_to_oxo_prefix(): omop_xref.omop_vocab_to_oxo_prefix('MeSH') == 'MeSH' -def test_oxo_search(): - """ Tests omop_xref.oxo_search - Uses oxo_search to make multiple requests to OxO and checks that the results have the expected formats, expected - number of search results (i.e., one search result for each CURIE queried), and checks how the number of mappings - compare across different calls with different parameter settings (i.e., more mappings when larger distances are - used). - - Returns - ------- - No return value. Asserts will be triggered upon failure. - """ - oxo_response_keys = ['_links', '_embedded', 'page'] - - # Check oxo_search with a known CURIE that should produce matches - json = omop_xref.oxo_search(['DOID:8398'], distance=2) - # Check the general response format - assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) - # Searched for 1 CURIE, expect searchResults with length 1 - assert len(json['_embedded']['searchResults']) == 1 - # Check that the query produced a response, but don't verify the mappings themselves - assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= 0 - # Keep track of the number of results for comparison against following queries - comparison_length = len(json['_embedded']['searchResults'][0]['mappingResponseList']) - - # Sleep for 2 seconds so we don't overload OxO - sleep(2) - - # Check oxo_search with a known CURIE with a shorter distance and check that there are fewer results - json = omop_xref.oxo_search(['DOID:8398'], distance=1) - # Check the general response format - assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) - # Searched for 1 CURIE, expect searchResults with length 1 - assert len(json['_embedded']['searchResults']) == 1 - # Check that the query produced a response, but don't verify the mappings themselves - assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= 0 - # Keep track of the number of results for comparison against following queries - assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) <= comparison_length - - # Sleep for 2 seconds so we don't overload OxO - sleep(2) - - # Check oxo_search with a known CURIE with a longer distance and check that there are fewer results - json = omop_xref.oxo_search(['DOID:8398'], distance=3) - # Check the general response format - assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) - # Searched for 1 CURIE, expect searchResults with length 1 - assert len(json['_embedded']['searchResults']) == 1 - # Check that the query produced a response, but don't verify the mappings themselves - assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= 0 - # Keep track of the number of results for comparison against following queries - assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= comparison_length - - # Sleep for 2 seconds so we don't overload OxO - sleep(2) - - # Check oxo_search with a known CURIE with a restricted mapping targets and check that there are fewer results - json = omop_xref.oxo_search(['DOID:8398'], mapping_targets=['ICD10CM'], distance=2) - # Check the general response format - assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) - # Searched for 1 CURIE, expect searchResults with length 1 - assert len(json['_embedded']['searchResults']) == 1 - # Check that the query produced a response, but don't verify the mappings themselves - assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= 0 - # Keep track of the number of results for comparison against following queries - assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) <= comparison_length - - # Sleep for 2 seconds so we don't overload OxO - sleep(2) - - # Check oxo_search with one valid CURIE and one fake CURIE - json = omop_xref.oxo_search(['DOID:8398', 'DOID:83980000'], distance=1) - # Check the general response format - assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) - # Searched for 2 CURIEs, expect searchResults with length 2 - assert len(json['_embedded']['searchResults']) == 2 - # Check that the first query produced mappings and the second query produced no mappings - assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) > 0 and \ - len(json['_embedded']['searchResults'][1]['mappingResponseList']) == 0 +# def test_oxo_search(): +# """ Tests omop_xref.oxo_search +# Uses oxo_search to make multiple requests to OxO and checks that the results have the expected formats, expected +# number of search results (i.e., one search result for each CURIE queried), and checks how the number of mappings +# compare across different calls with different parameter settings (i.e., more mappings when larger distances are +# used). +# +# Returns +# ------- +# No return value. Asserts will be triggered upon failure. +# """ +# oxo_response_keys = ['_links', '_embedded', 'page'] +# +# # Check oxo_search with a known CURIE that should produce matches +# json = omop_xref.oxo_search(['DOID:8398'], distance=2) +# # Check the general response format +# assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) +# # Searched for 1 CURIE, expect searchResults with length 1 +# assert len(json['_embedded']['searchResults']) == 1 +# # Check that the query produced a response, but don't verify the mappings themselves +# assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= 0 +# # Keep track of the number of results for comparison against following queries +# comparison_length = len(json['_embedded']['searchResults'][0]['mappingResponseList']) +# +# # Sleep for 2 seconds so we don't overload OxO +# sleep(2) +# +# # Check oxo_search with a known CURIE with a shorter distance and check that there are fewer results +# json = omop_xref.oxo_search(['DOID:8398'], distance=1) +# # Check the general response format +# assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) +# # Searched for 1 CURIE, expect searchResults with length 1 +# assert len(json['_embedded']['searchResults']) == 1 +# # Check that the query produced a response, but don't verify the mappings themselves +# assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= 0 +# # Keep track of the number of results for comparison against following queries +# assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) <= comparison_length +# +# # Sleep for 2 seconds so we don't overload OxO +# sleep(2) +# +# # Check oxo_search with a known CURIE with a longer distance and check that there are fewer results +# json = omop_xref.oxo_search(['DOID:8398'], distance=3) +# # Check the general response format +# assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) +# # Searched for 1 CURIE, expect searchResults with length 1 +# assert len(json['_embedded']['searchResults']) == 1 +# # Check that the query produced a response, but don't verify the mappings themselves +# assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= 0 +# # Keep track of the number of results for comparison against following queries +# assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= comparison_length +# +# # Sleep for 2 seconds so we don't overload OxO +# sleep(2) +# +# # Check oxo_search with a known CURIE with a restricted mapping targets and check that there are fewer results +# json = omop_xref.oxo_search(['DOID:8398'], mapping_targets=['ICD10CM'], distance=2) +# # Check the general response format +# assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) +# # Searched for 1 CURIE, expect searchResults with length 1 +# assert len(json['_embedded']['searchResults']) == 1 +# # Check that the query produced a response, but don't verify the mappings themselves +# assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) >= 0 +# # Keep track of the number of results for comparison against following queries +# assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) <= comparison_length +# +# # Sleep for 2 seconds so we don't overload OxO +# sleep(2) +# +# # Check oxo_search with one valid CURIE and one fake CURIE +# json = omop_xref.oxo_search(['DOID:8398', 'DOID:83980000'], distance=1) +# # Check the general response format +# assert json is not None and isinstance(json, dict) and all(k in json for k in oxo_response_keys) +# # Searched for 2 CURIEs, expect searchResults with length 2 +# assert len(json['_embedded']['searchResults']) == 2 +# # Check that the first query produced mappings and the second query produced no mappings +# assert len(json['_embedded']['searchResults'][0]['mappingResponseList']) > 0 and \ +# len(json['_embedded']['searchResults'][1]['mappingResponseList']) == 0 def test_xref_best_from(): diff --git a/test_cohd_io.py b/test_cohd_io.py index f14186d..0c34abb 100644 --- a/test_cohd_io.py +++ b/test_cohd_io.py @@ -500,116 +500,116 @@ def test_vocabularies(): check_result_values(json, expected_results) -def test_xrefFromOMOP(): - """ Check the /omop/xrefFromOMOP endpoint. Try mapping from OMOP:192855 (Cancer in situ of urinary bladder) to UMLS - with max distance 2. - Checks the response json conforms to the expected schema and includes the expected results (see expected_results). - """ - print(f'\ntest_cohd_io: testing /omop/xrefFromOMOP on {cr.server}..... ') - json, df = cr.xref_from_omop(concept_id=192855, mapping_targets=['UMLS'], distance=2, local=True, recommend=False) - - # Check that the results adhere to the expected schema - schema = [_s('intermediate_omop_concept_code', str), - _s('intermediate_omop_concept_id', object), # Sometimes int, sometimes string when OMOP-UMLS mapping used - _s('intermediate_omop_concept_name', str), - _s('intermediate_omop_vocabulary_id', str), - _s('intermediate_oxo_curie', str), - _s('intermediate_oxo_label', str), - _s('omop_distance', int), - _s('oxo_distance', int), - _s('source_omop_concept_code', str), - _s('source_omop_concept_id', int), - _s('source_omop_concept_name', str), - _s('source_omop_vocabulary_id', str), - _s('target_curie', str), - _s('target_label', str), - _s('total_distance', int)] - check_results_schema(json, schema) - - # There should be at least one result - assert len(json['results']) >= 1 - - # Spot check a few of the entries against expected values: OMOP:192855 (Cancer in situ of urinary bladder) should - # map to UMLS:C0154091 (Carcinoma in situ of bladder) through a couple different paths - expected_results = [ - { - "intermediate_omop_concept_code": "N/A (OMOP-UMLS mapping)", - "intermediate_omop_concept_id": "N/A (OMOP-UMLS mapping)", - "intermediate_omop_concept_name": "N/A (OMOP-UMLS mapping)", - "intermediate_omop_vocabulary_id": "N/A (OMOP-UMLS mapping)", - "intermediate_oxo_curie": "N/A (OMOP-UMLS mapping)", - "intermediate_oxo_label": "N/A (OMOP-UMLS mapping)", - "omop_distance": 1, - "oxo_distance": 0, - "source_omop_concept_code": "92546004", - "source_omop_concept_id": 192855, - "source_omop_concept_name": "Cancer in situ of urinary bladder", - "source_omop_vocabulary_id": "SNOMED", - "target_curie": "UMLS:C0154091", - "target_label": "Carcinoma in situ of bladder", - "total_distance": 1 - }, - { - "intermediate_omop_concept_code": "233.7", - "intermediate_omop_concept_id": 44824068, - "intermediate_omop_concept_name": "Carcinoma in situ of bladder", - "intermediate_omop_vocabulary_id": "ICD9CM", - "intermediate_oxo_curie": "ICD9CM:233.7", - "intermediate_oxo_label": "Carcinoma in situ of bladder", - "omop_distance": 1, - "oxo_distance": 1, - "source_omop_concept_code": "92546004", - "source_omop_concept_id": 192855, - "source_omop_concept_name": "Cancer in situ of urinary bladder", - "source_omop_vocabulary_id": "SNOMED", - "target_curie": "UMLS:C0154091", - "target_label": "Carcinoma in situ of bladder", - "total_distance": 2 - } - ] - check_result_values(json, expected_results) - - -def test_xrefToOMOP(): - """ Check the /omop/xrefToOMOP endpoint. Try to map DOID:8398 (osteoarthritis) to OMOP using the local OxO - implementation, recommended mapping, and max distance 2 - Checks the response json conforms to the expected schema and includes the expected results (see expected_results). - """ - print(f'\ntest_cohd_io: testing /omop/xrefToOMOP on {cr.server}..... ') - json, df = cr.xref_to_omop(curie='DOID:8398', distance=2, local=True, recommend=True) - - # Check that the results adhere to the expected schema - schema = [_s('intermediate_oxo_id', str), - _s('intermediate_oxo_label', str), - _s('omop_concept_name', str), - _s('omop_distance', int), - _s('omop_domain_id', str), - _s('omop_standard_concept_id', int), - _s('oxo_distance', int), - _s('source_oxo_id', str), - _s('source_oxo_label', str), - _s('total_distance', int)] - check_results_schema(json, schema) - - # There should be one result - assert len(json['results']) == 1 - - # With recommend=True, there should be exactly one mapping from DOID:8398 (osteoarthritis) to OMOP:80180 - expected_results = [ - { - "intermediate_oxo_id": "SNOMEDCT:396275006", - "intermediate_oxo_label": "Osteoarthritis (disorder)", - "omop_concept_name": "Osteoarthritis", - "omop_distance": 0, - "omop_domain_id": "Condition", - "omop_standard_concept_id": 80180, - "oxo_distance": 2, - "source_oxo_id": "DOID:8398", - "source_oxo_label": "osteoarthritis", - "total_distance": 2 - } - ] - check_result_values(json, expected_results) +# def test_xrefFromOMOP(): +# """ Check the /omop/xrefFromOMOP endpoint. Try mapping from OMOP:192855 (Cancer in situ of urinary bladder) to UMLS +# with max distance 2. +# Checks the response json conforms to the expected schema and includes the expected results (see expected_results). +# """ +# print(f'\ntest_cohd_io: testing /omop/xrefFromOMOP on {cr.server}..... ') +# json, df = cr.xref_from_omop(concept_id=192855, mapping_targets=['UMLS'], distance=2, local=True, recommend=False) +# +# # Check that the results adhere to the expected schema +# schema = [_s('intermediate_omop_concept_code', str), +# _s('intermediate_omop_concept_id', object), # Sometimes int, sometimes string when OMOP-UMLS mapping used +# _s('intermediate_omop_concept_name', str), +# _s('intermediate_omop_vocabulary_id', str), +# _s('intermediate_oxo_curie', str), +# _s('intermediate_oxo_label', str), +# _s('omop_distance', int), +# _s('oxo_distance', int), +# _s('source_omop_concept_code', str), +# _s('source_omop_concept_id', int), +# _s('source_omop_concept_name', str), +# _s('source_omop_vocabulary_id', str), +# _s('target_curie', str), +# _s('target_label', str), +# _s('total_distance', int)] +# check_results_schema(json, schema) +# +# # There should be at least one result +# assert len(json['results']) >= 1 +# +# # Spot check a few of the entries against expected values: OMOP:192855 (Cancer in situ of urinary bladder) should +# # map to UMLS:C0154091 (Carcinoma in situ of bladder) through a couple different paths +# expected_results = [ +# { +# "intermediate_omop_concept_code": "N/A (OMOP-UMLS mapping)", +# "intermediate_omop_concept_id": "N/A (OMOP-UMLS mapping)", +# "intermediate_omop_concept_name": "N/A (OMOP-UMLS mapping)", +# "intermediate_omop_vocabulary_id": "N/A (OMOP-UMLS mapping)", +# "intermediate_oxo_curie": "N/A (OMOP-UMLS mapping)", +# "intermediate_oxo_label": "N/A (OMOP-UMLS mapping)", +# "omop_distance": 1, +# "oxo_distance": 0, +# "source_omop_concept_code": "92546004", +# "source_omop_concept_id": 192855, +# "source_omop_concept_name": "Cancer in situ of urinary bladder", +# "source_omop_vocabulary_id": "SNOMED", +# "target_curie": "UMLS:C0154091", +# "target_label": "Carcinoma in situ of bladder", +# "total_distance": 1 +# }, +# { +# "intermediate_omop_concept_code": "233.7", +# "intermediate_omop_concept_id": 44824068, +# "intermediate_omop_concept_name": "Carcinoma in situ of bladder", +# "intermediate_omop_vocabulary_id": "ICD9CM", +# "intermediate_oxo_curie": "ICD9CM:233.7", +# "intermediate_oxo_label": "Carcinoma in situ of bladder", +# "omop_distance": 1, +# "oxo_distance": 1, +# "source_omop_concept_code": "92546004", +# "source_omop_concept_id": 192855, +# "source_omop_concept_name": "Cancer in situ of urinary bladder", +# "source_omop_vocabulary_id": "SNOMED", +# "target_curie": "UMLS:C0154091", +# "target_label": "Carcinoma in situ of bladder", +# "total_distance": 2 +# } +# ] +# check_result_values(json, expected_results) +# +# +# def test_xrefToOMOP(): +# """ Check the /omop/xrefToOMOP endpoint. Try to map DOID:8398 (osteoarthritis) to OMOP using the local OxO +# implementation, recommended mapping, and max distance 2 +# Checks the response json conforms to the expected schema and includes the expected results (see expected_results). +# """ +# print(f'\ntest_cohd_io: testing /omop/xrefToOMOP on {cr.server}..... ') +# json, df = cr.xref_to_omop(curie='DOID:8398', distance=2, local=True, recommend=True) +# +# # Check that the results adhere to the expected schema +# schema = [_s('intermediate_oxo_id', str), +# _s('intermediate_oxo_label', str), +# _s('omop_concept_name', str), +# _s('omop_distance', int), +# _s('omop_domain_id', str), +# _s('omop_standard_concept_id', int), +# _s('oxo_distance', int), +# _s('source_oxo_id', str), +# _s('source_oxo_label', str), +# _s('total_distance', int)] +# check_results_schema(json, schema) +# +# # There should be one result +# assert len(json['results']) == 1 +# +# # With recommend=True, there should be exactly one mapping from DOID:8398 (osteoarthritis) to OMOP:80180 +# expected_results = [ +# { +# "intermediate_oxo_id": "SNOMEDCT:396275006", +# "intermediate_oxo_label": "Osteoarthritis (disorder)", +# "omop_concept_name": "Osteoarthritis", +# "omop_distance": 0, +# "omop_domain_id": "Condition", +# "omop_standard_concept_id": 80180, +# "oxo_distance": 2, +# "source_oxo_id": "DOID:8398", +# "source_oxo_label": "osteoarthritis", +# "total_distance": 2 +# } +# ] +# check_result_values(json, expected_results) def test_singleConceptFreq(): From cc87b03fd7b2d528533909954447a8759ad8c7b5 Mon Sep 17 00:00:00 2001 From: caseyta Date: Tue, 11 Jul 2023 16:08:50 -0700 Subject: [PATCH 2/2] Update Flask cache specification style --- cohd/cohd_flask.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cohd/cohd_flask.conf b/cohd/cohd_flask.conf index b7f0e4e..42cb06f 100644 --- a/cohd/cohd_flask.conf +++ b/cohd/cohd_flask.conf @@ -1,6 +1,6 @@ DEPLOYMENT_ENV = 'ITRB-CI' # Expected values: ITRB-CI, ITRB-TEST, or ITRB-PROD DEBUG = False -CACHE_TYPE = 'filesystem' +CACHE_TYPE = 'FileSystemCache' CACHE_DEFAULT_TIMEOUT = 3600 CACHE_DIR = 'flask_cache' CACHE_THRESHOLD = 100000