From 730d83af354207dd62cae774396015a58704a983 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Tue, 31 Oct 2023 07:59:08 -0500 Subject: [PATCH 01/11] Removed prototype calls to Cells API. --- src/hs_ontology_api/routes/neo4j_logic.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/hs_ontology_api/routes/neo4j_logic.py b/src/hs_ontology_api/routes/neo4j_logic.py index 3eba1d4..f4ca2a8 100644 --- a/src/hs_ontology_api/routes/neo4j_logic.py +++ b/src/hs_ontology_api/routes/neo4j_logic.py @@ -628,16 +628,10 @@ def query_cypher_dataset_info(sab: str) -> str: def genedetail_get_logic(neo4j_instance, gene_id: str) -> List[GeneDetail]: """ Returns detailed information on a gene, based on an input list of HGNC identifiers in the request body of a POST. + :param neo4j_instance: instance of neo4j connection + :param gene_id: HGNC identifier for a gene """ # response list - - # Read indexed cell-type data from Cells API. - # The cells_client was instantiated at startup. - oc = current_app.cells_client - - # The current prototype call reads a CSV of static information obtained from - # prior calls to the Cells API. - cellsapi_celltypes = oc.celltypes_for_gene_csv(gene_id) genedetails: [GeneDetail] = [] # Load annotated Cypher query from the cypher directory. From d33daaabcbca4af8909f9ddf4380b698371afce8 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Tue, 31 Oct 2023 10:04:17 -0500 Subject: [PATCH 02/11] Proposed endpoints for proteins; protein; celltypes; and celltype --- hs-ontology-api-spec.yaml | 355 +++++++++++++++++++++++++++++++++++++- 1 file changed, 349 insertions(+), 6 deletions(-) diff --git a/hs-ontology-api-spec.yaml b/hs-ontology-api-spec.yaml index e103e36..e390636 100644 --- a/hs-ontology-api-spec.yaml +++ b/hs-ontology-api-spec.yaml @@ -392,7 +392,6 @@ paths: description: No genes that satisfied the criteria were found. This usually means that there are no genes with HGNC approved symbols that start with the string specified in the *starts_with* parameter. '5XX': description: Unexpected error - /gene/{id}: get: operationId: gene_detail_get @@ -418,6 +417,140 @@ paths: description: No information for gene with HGNC identifier *id*. '5XX': description: Unexpected error + /proteins: + get: + operationId: protein_list_get + summary: Return high-level information on a set of proteins specified by pagination parameters. + parameters: + - name: page + in: query + required: false + description: Offset set of proteins of count=proteinsperpage. Can be a non-negative number or the words "first" or "last". + schema: + type: string + default: 1 + example: 1 + - name: proteinsperpage + in: query + required: false + description: Number of proteins per "page" to return. Can be a nonzero number. + schema: + type: string + default: 10 + - name: starts_with + in: query + required: false + description: Optional *case-insensitive* string by which to filter by UniProtKB protein name. + schema: + type: string + example: M + responses: + '200': + description: high-level information on proteins specified by UniProtKB identifiers. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ProteinListResponse' + '400': + description: Invalid parameter--e.g., *proteinsperpage* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' + '404': + description: No proteins that satisfied the criteria were found. This usually means that there are no proteins with UniprotKB names that start with the string specified in the *starts_with* parameter. + '5XX': + description: Unexpected error + /protein/{id}: + get: + operationId: protein_detail_get + summary: Return detailed information on a protein specified by a UniProtKB identifier. + parameters: + - name: id + in: path + required: true + description: Case-insensitive UniProtKB identifier + schema: + type: string + example: Q13201 + responses: + '200': + description: detailed information on the protein specified by a UniProtKB identifier. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ProteinDetailResponse' + '404': + description: No information for protein with UniProtKB identifier *id*. + '5XX': + description: Unexpected error + /celltypes: + get: + operationId: celltype_list_get + summary: Return high-level information on a set of cell types specified by pagination parameters. + parameters: + - name: page + in: query + required: false + description: Offset set of genes of count=celltypesperpage. Can be a non-negative number or the words "first" or "last". + schema: + type: string + default: 1 + example: 1 + - name: celltypesperpage + in: query + required: false + description: Number of cell types per "page" to return. Can be a nonzero number. + schema: + type: string + default: 10 + - name: starts_with + in: query + required: false + description: Optional *case-insensitive* string by which to filter by cell type name. + schema: + type: string + example: E + responses: + '200': + description: high-level information on cell types specified by Cell Ontology. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CellTypeListResponse' + '400': + description: Invalid parameter--e.g., *celltypesperpage* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' + '404': + description: No cell types that satisfied the criteria were found. This usually means that there are no cell types with names that start with the string specified in the *starts_with* parameter. + '5XX': + description: Unexpected error + /celltype/{id}: + get: + operationId: celltype_detail_get + summary: Return detailed information on a cell type specified by a name. + parameters: + - name: id + in: path + required: true + description: case-insensitive Cell Ontology identifier in format CL{id} + schema: + type: string + example: CL0002138 + responses: + '200': + description: detailed information on the cell type specified by a Cell Ontology identifier. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CellTypeDetailResponse' + '404': + description: No information for cell type with identifier *id*. + '5XX': + description: Unexpected error components: schemas: AssayTypePropertyInfo: @@ -591,7 +724,7 @@ components: - SenNet GeneListResponse: type: object - description: High-level information on a set of genes requested by the gene_list endpoint. + description: High-level information on a set of genes requested by the genes endpoint. properties: genes: type: array @@ -613,11 +746,11 @@ components: example: Multimerin is a massive, soluble protein found in.... genes_per_page: type: number - description: value of genesperpage paramater from the gene_list endpoint + description: value of genesperpage parameter from the gene_list endpoint example: 3 page: type: number - description: value of page paramater from the gene_list endpoint + description: value of page parameter from the gene_list endpoint example: 1 total_pages: type: number @@ -633,7 +766,7 @@ components: example: 500 GeneDetailResponse: # JAS Sept 2023 type: object - description: Detailed information on a gene. + description: Detailed information on a gene requested by the gene endpoint properties: alias_names: type: array @@ -734,4 +867,214 @@ components: summary: type: string description: RefSeq summary of the gene - example: Multimerin is a massive, soluble protein found in.... \ No newline at end of file + example: Multimerin is a massive, soluble protein found in.... + ProteinListResponse: + type: object + description: High-level list of proteins with pagination details, requested by the proteins endpoint. + properties: + proteins: + type: array + description: protein information + items: + type: object + properties: + id: + type: string + description: UniProtKB identifier + example: Q13201 + entry_name: + type: string + description: UniProtKB Entry Name + example: MMRN1_HUMAN + recommended_name: + type: string + description: UniProtKB Recommended Name + example: multimerin-1 + proteins_per_page: + type: number + description: value of proteinsperpage parameter from the proteins endpoint + example: 3 + page: + type: number + description: value of page parameter from the proteins endpoint + example: 1 + total_pages: + type: number + description: total number of "pages" based on the values of proteinsperpage and the total number of proteins + example: 14381 + starts_with: + type: string + description: value of starts_with from the proteins endpoint + example: M + protein_count: + type: number + description: Count of proteins. If starts_with is non-null, the count is that of the proteins with names that start with the value of starts_with. + example: 500 + ProteinDetailResponse: + type: object + description: Detailed information on a protein requested by the protein endpoint + properties: + id: + type: string + description: UniProtKB identifier + example: Q13201 + entry_name: + type: string + description: UniProtKB Entry Name + example: MMRN1_HUMAN + recommended_name: + type: string + description: UniProtKB Recommended Name + example: multimerin-1 + synonyms: + type: array + description: alternative names + items: + type: string + example: [EMILIN-4,Elastin microfibril interface located protein 4 (Elastin microfibril interfacer 4),Endothelial cell multimerin] + references: + type: array + description: Array of links to protein databases + items: + type: object + properties: + source: + type: string + description: protein database name + example: uniprotkb + entry: + type: string + description: description of protein in source--e.g., the "function" in UniProtKB + example: Carrier protein for platelet (but not plasma) factor V/Va. Plays a role… + url: + type: string + description: URL to reference site + example: https://www.ncbi.nlm.nih.gov/gene/22915 + curation: + type: string + description: method of curation + enum: + - swissprot + - trembl + organisms: + type: array + description: array of relevant organism, named in binomial nomenclature + items: + type: string + example: [Homo sapiens] + CellTypeListResponse: + type: object + description: High-level list of cell types with pagination details, requested by the celltypes endpoint. + properties: + proteins: + type: array + description: cell type information + items: + type: object + properties: + id: + type: string + description: Cell Ontology code + example: CL:0002138 + name: + type: string + description: Preferred term for the cell type from Cell Ontology + example: endothelial cell of lymphatic vessel + definition: + type: string + description: Definition of the cell type from Cell Ontology + example: A endothelial cell of a lymphatic vessel. The border of… + celltypes_per_page: + type: number + description: value of celltypesperpage parameter from the celltypes endpoint + example: 3 + page: + type: number + description: value of page parameter from the celltypes endpoint + example: 1 + total_pages: + type: number + description: total number of "pages" based on the values of celltypesperpage and the total number of celltypes + example: 14381 + starts_with: + type: string + description: value of starts_with from the celltypes endpoint + example: E + cell_type_count: + type: number + description: Count of cell types. If starts_with is non-null, the count is that of the cell types with names that start with the value of starts_with. + example: 500 + CellTypeDetailResponse: + type: object + description: Detailed information on a cell type requested by the celltype endpoint + properties: + id: + type: string + description: Cell Ontology code + example: CL:0002138 + name: + type: string + description: Preferred term for the cell type from Cell Ontology + example: endothelial cell of lymphatic vessel + definition: + type: string + description: Definition of the cell type from Cell Ontology + example: A endothelial cell of a lymphatic vessel. The border of… + biomarkers: + type: array + description: Array of biomarkers associated with the cell type + items: + type: object + properties: + reference: + type: string + description: source for association between cell type and biomarker + enum: + - Human Reference Atlas + - Cells + biomarker_type: + type: string + description: type of biomarker + enum: + - gene + - protein + - lipid + - metabolite + entry: + type: object + description: Encoded entry in reference + properties: + vocabulary: + type: string + description: vocabulary for entry + example: hgnc + id: + type: string + description: identifier for entry in vocabulary + example: 7178 + symbol: + type: string + description: symbol for entry in vocabulary + example: MMRN1 + name: + type: string + description: preferred term for entry in vocabulary + example: multimerin-1 + organs: + type: array + description: Organs associated with the cell type, based on relationships in the Human Resource Atlas and Azimuth + items: + type: object + properties: + id: + type: string + description: Identifier (code) for the organ in source + example: 0000948 + source: + type: string + description: Source vocabulary for id for organ + example: UBERON + name: + type: string + description: Name (preferred term) for organ in source + example: heart \ No newline at end of file From b138cb072cd38c1bf1afa3854895af2ae0824351 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Tue, 31 Oct 2023 10:24:05 -0500 Subject: [PATCH 03/11] Proposed endpoints for proteins; protein; celltypes; and celltype --- src/hs_ontology_api/routes/neo4j_logic.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hs_ontology_api/routes/neo4j_logic.py b/src/hs_ontology_api/routes/neo4j_logic.py index f4ca2a8..c3a93ba 100644 --- a/src/hs_ontology_api/routes/neo4j_logic.py +++ b/src/hs_ontology_api/routes/neo4j_logic.py @@ -657,9 +657,6 @@ def genedetail_get_logic(neo4j_instance, gene_id: str) -> List[GeneDetail]: record.get('cell_types_code_definition'), record.get('cell_types_codes_organ'),record.get('cell_types_codes_source')).serialize() - # Append cell type information from Cells API. - for cell_type in cellsapi_celltypes: - genedetail['cell_types'].append(cell_type) genedetails.append(genedetail) except KeyError: From 909521d4230e24c1c8e44ff3276ccf19b62e6594 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Wed, 1 Nov 2023 11:07:34 -0500 Subject: [PATCH 04/11] Proposed endpoints to replace use of field_descriptions.yaml, field_types.yaml, field_entities.yaml, field_assays.yaml, and field_schemas.yaml in ingest-validation-tools. --- hs-ontology-api-spec.yaml | 289 +++++++++++++++++++++++++++++++++++++- 1 file changed, 288 insertions(+), 1 deletion(-) diff --git a/hs-ontology-api-spec.yaml b/hs-ontology-api-spec.yaml index e390636..562c2e7 100644 --- a/hs-ontology-api-spec.yaml +++ b/hs-ontology-api-spec.yaml @@ -551,6 +551,132 @@ paths: description: No information for cell type with identifier *id*. '5XX': description: Unexpected error + /field_descriptions: + get: + operationId: field_descriptions_get + summary: Return descriptions for ingest metadata fields. Replacement for field_descriptions.yaml. + responses: + '200': + description: Ingest metadata fields and descriptions + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/FieldDescriptionsResponse' + '5XX': + description: Unexpected error + /field_types: + get: + operationId: field_types_get + summary: Return data types for ingest metadata fields. Replacement for field_types.yaml. + responses: + '200': + description: Ingest metadata fields mapped to XSD data types. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/FieldTypesResponse' + '5XX': + description: Unexpected error + /field_entities: + get: + operationId: field_entities_get + summary: Return associations between ingest metadata fields and the provenance entities of an application. Replacement for field_types.yaml. + parameters: + - name: application_context + in: query + required: true + description: Filter to indicate application context + schema: + type: string + default: HUBMAP + enum: + - HUBMAP + - SENNET + - hubmap + - sennet + - HuBMAP + - SenNet + responses: + '200': + description: Associations between ingest metadata fields and application-specific provenance entities. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/FieldEntitiesResponse' + '400': + description: missing application context + '5XX': + description: Unexpected error + /field_assays: + get: + operationId: field_assays_get + summary: Return associations between ingest metadata fields and the "assays" (dataset data types) for an application. Replacement for field_assays.yaml. + parameters: + - name: application_context + in: query + required: true + description: Filter to indicate application context + schema: + type: string + default: HUBMAP + enum: + - HUBMAP + - SENNET + - hubmap + - sennet + - HuBMAP + - SenNet + responses: + '200': + description: Associations between ingest metadata fields and application-specific assays/dataset types. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/FieldAssaysResponse' + '400': + description: missing application context + '5XX': + description: Unexpected error + /field_schemas: + get: + operationId: field_schemas_get + summary: Return associations between ingest metadata fields and metadata schemas for an application. Replacement for field_schemas.yaml. + parameters: + - name: application_context + in: query + required: true + description: Filter to indicate application context + schema: + type: string + default: HUBMAP + enum: + - HUBMAP + - SENNET + - hubmap + - sennet + - HuBMAP + - SenNet + responses: + '200': + description: Associations between ingest metadata fields and application-specific metadata schemas. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/FieldSchemasResponse' + '400': + description: missing application context + '5XX': + description: Unexpected error components: schemas: AssayTypePropertyInfo: @@ -1077,4 +1203,165 @@ components: name: type: string description: Name (preferred term) for organ in source - example: heart \ No newline at end of file + example: heart + FieldDescriptionsResponse: + type: object + description: Descriptions ingest metadata fields from legacy field_descriptions.yaml + properties: + fields: + type: array + description: array of fields and their descriptions + items: + type: object + properties: + codeID: + type: string + description: Code in the HMFIELD ontology + example: HMFIELD:1001 + identifier: + type: string + description: identifier for metadata field + example: ablation_distance_between_shots_x_units + description: + type: string + description: description of field + example: Units of x resolution distance between laser ablation shots. + FieldTypesResponse: + type: object + description: Associations between metadata field and data type ingest metadata fields from legacy field_types.yaml + properties: + fields: + type: array + description: array of fields and their data types + items: + type: object + properties: + codeID: + type: string + description: Code in the HMFIELD ontology + example: HMFIELD:1001 + identifier: + type: string + description: identifier for metadata field + example: ablation_distance_between_shots_x_units + type: + type: object + properties: + XSDCode: + type: string + description: Code in XSD + example: xsd:string + name: + type: string + description: name for type + example: string + FieldEntitiesResponse: + type: object + description: Associations between fields and provenance entities for ingest metadata fields from legacy field_types.yaml + properties: + fields: + type: array + description: array of fields, each with its associated provenance entities + items: + type: object + properties: + codeID: + type: string + description: Code in the HMFIELD ontology + example: HMFIELD:1001 + identifier: + type: string + description: identifier for metadata field + example: ablation_distance_between_shots_x_units + entities: + type: array + description: array of provenance entities associated with the field + items: + type: object + properties: + code: + type: string + description: Code in application ontology + example: HUBMAP:C040001 + name: + type: string + description: name + example: dataset + FieldAssaysResponse: + type: object + description: Associations between fields and "assays" (dataset types) for ingest metadata fields from legacy field_assays.yaml + properties: + fields: + type: array + description: array of fields, each with its associated assays/dataset types + items: + type: object + properties: + codeID: + type: string + description: Code in the HMFIELD ontology + example: HMFIELD:1001 + identifier: + type: string + description: identifier for metadata field + example: ablation_distance_between_shots_x_units + assays: + type: array + description: array of "assays" (dataset types) associated with the field + items: + type: object + properties: + data_type: + type: object + description: The data_type for the assay/dataset type--i.e., the key for ingest workflows + properties: + code: + type: string + description: Code in application ontology + example: HUBMAP:C006904 + name: + type: string + description: the data_type string used in ingest workflows + example: IMC + display_name: + type: string + description: The display name for the assay/dataset type in the Portal UI + example: Imaging Mass Cytometry (2D) + alt-names: + type: array + description: The set of alt-names (synonyms for data_type) for the assay/dataset type + items: + type: string + example: [Imaging Mass Cytometry,2D-IMC,IMC] + FieldSchemasResponse: + type: object + description: Associations between fields and metadata schemas for ingest metadata fields from legacy field_schemas.yaml + properties: + fields: + type: array + description: array of fields, each with its associated schemas + items: + type: object + properties: + codeID: + type: string + description: Code in the HMFIELD ontology + example: HMFIELD:1001 + identifier: + type: string + description: identifier for metadata field + example: ablation_distance_between_shots_x_units + schemas: + type: array + description: array of metadata schemas associated with the field + items: + type: object + properties: + code: + type: string + description: Code in HMFIELD ontology + example: HMFIELD:1001 + name: + type: string + description: name + example: imc3d \ No newline at end of file From 78b86e4c21598300e3581e9615d19721e39458d7 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Tue, 7 Nov 2023 17:27:37 -0600 Subject: [PATCH 05/11] Refactored controllers and Blueprints to reflect requested change in names of routes--i.e., genes and genes-info. --- .../routes/{genedetail => genes}/__init__.py | 0 .../genes_controller.py} | 13 ++++--------- .../routes/{geneslist => genesinfo}/__init__.py | 0 .../genesinfo_controller.py} | 4 ++-- src/main.py | 8 ++++---- 5 files changed, 10 insertions(+), 15 deletions(-) rename src/hs_ontology_api/routes/{genedetail => genes}/__init__.py (100%) rename src/hs_ontology_api/routes/{genedetail/genedetail_controller.py => genes/genes_controller.py} (67%) rename src/hs_ontology_api/routes/{geneslist => genesinfo}/__init__.py (100%) rename src/hs_ontology_api/routes/{geneslist/geneslist_controller.py => genesinfo/genesinfo_controller.py} (95%) diff --git a/src/hs_ontology_api/routes/genedetail/__init__.py b/src/hs_ontology_api/routes/genes/__init__.py similarity index 100% rename from src/hs_ontology_api/routes/genedetail/__init__.py rename to src/hs_ontology_api/routes/genes/__init__.py diff --git a/src/hs_ontology_api/routes/genedetail/genedetail_controller.py b/src/hs_ontology_api/routes/genes/genes_controller.py similarity index 67% rename from src/hs_ontology_api/routes/genedetail/genedetail_controller.py rename to src/hs_ontology_api/routes/genes/genes_controller.py index d96947f..857028c 100644 --- a/src/hs_ontology_api/routes/genedetail/genedetail_controller.py +++ b/src/hs_ontology_api/routes/genes/genes_controller.py @@ -4,15 +4,10 @@ from ..neo4j_logic import genedetail_get_logic -genedetail_blueprint = Blueprint('genedetail', __name__, url_prefix='/gene') +genes_blueprint = Blueprint('genes', __name__, url_prefix='/genes') -@genedetail_blueprint.route('', methods=['GET']) -def genedetail_expand_get(): - return make_response('The /gene endpoint should specify a single HGNC gene identifier--e.g., /gene/MMRN1.', 400) - - -@genedetail_blueprint.route('/', methods=['GET']) -def genedetail_id_expand_get(id=None): +@genes_blueprint.route('/', methods=['GET']) +def genes_id_expand_get(id=None): """Returns detailed information on a single gene. :rtype: Union[List[GeneDetail]] @@ -30,7 +25,7 @@ def genedetail_id_expand_get(id=None): if id =='' or id is None: # Missing ID parameter in URL - return make_response('The /gene endpoint must specify a HGNC gene identifier--e.g., /gene/MMRN1.',400) + return make_response('The /genes endpoint must specify a HGNC gene identifier--e.g., /genes/MMRN1.',400) neo4j_instance = current_app.neo4jConnectionHelper.instance() result = genedetail_get_logic(neo4j_instance, id) diff --git a/src/hs_ontology_api/routes/geneslist/__init__.py b/src/hs_ontology_api/routes/genesinfo/__init__.py similarity index 100% rename from src/hs_ontology_api/routes/geneslist/__init__.py rename to src/hs_ontology_api/routes/genesinfo/__init__.py diff --git a/src/hs_ontology_api/routes/geneslist/geneslist_controller.py b/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py similarity index 95% rename from src/hs_ontology_api/routes/geneslist/geneslist_controller.py rename to src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py index f1a40e8..93da0d5 100644 --- a/src/hs_ontology_api/routes/geneslist/geneslist_controller.py +++ b/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py @@ -4,9 +4,9 @@ from ..neo4j_logic import genelist_get_logic,genelist_count_get_logic import math -geneslist_blueprint = Blueprint('geneslist', __name__, url_prefix='/genes') +genesinfo_blueprint = Blueprint('genes-info', __name__, url_prefix='/genes-info') -@geneslist_blueprint.route('', methods=['GET']) +@genesinfo_blueprint.route('', methods=['GET']) def geneslist() -> list[str]: neo4j_instance = current_app.neo4jConnectionHelper.instance() diff --git a/src/main.py b/src/main.py index d48a00a..cf6deec 100755 --- a/src/main.py +++ b/src/main.py @@ -10,8 +10,8 @@ from hs_ontology_api.routes.relationships.relationships_controller import relationships_blueprint from hs_ontology_api.routes.valueset.valueset_controller import valueset_blueprint # JAS September 2023 -from hs_ontology_api.routes.genedetail.genedetail_controller import genedetail_blueprint -from hs_ontology_api.routes.geneslist.geneslist_controller import geneslist_blueprint +from hs_ontology_api.routes.genes.genes_controller import genes_blueprint +from hs_ontology_api.routes.genesinfo.genesinfo_controller import genesinfo_blueprint # Cells API client from hs_ontology_api.routes.cellsclient import OntologyCellsClient @@ -32,8 +32,8 @@ def make_flask_config(): app.register_blueprint(relationships_blueprint) app.register_blueprint(valueset_blueprint) # JAS Sept 2023 -app.register_blueprint(genedetail_blueprint) -app.register_blueprint(geneslist_blueprint) +app.register_blueprint(genes_blueprint) +app.register_blueprint(genesinfo_blueprint) # Instantiate a Cells API client. cellsurl = make_flask_config().get('CELLSURL') From a891665d48f14026fe49914fc44e6db1f6c7b4f1 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Tue, 7 Nov 2023 20:30:58 -0600 Subject: [PATCH 06/11] Refactored to move neo4j_logic.py, cellsclient.py to utils directory. Moved loadquerystring function into neo4j_logic.py. --- src/hs_ontology_api/cypher/util_query.py | 22 -- src/hs_ontology_api/models/genelist.py | 164 +++------------ src/hs_ontology_api/models/pagination.py | 188 ++++++++++++++++++ .../routes/assayname/assayname_controller.py | 2 +- .../routes/assaytype/assaytype_controller.py | 2 +- .../routes/datasets/datasets_controller.py | 2 +- .../routes/genes/genes_controller.py | 4 +- .../routes/genesinfo/genesinfo_controller.py | 26 +-- .../routes/organs/organs_controller.py | 2 +- .../relationships/relationships_controller.py | 2 +- .../routes/valueset/valueset_controller.py | 2 +- src/hs_ontology_api/utils/README.md | 9 + src/hs_ontology_api/utils/__init__.py | 0 .../{routes => utils}/cellsclient.py | 2 +- .../{routes => utils}/neo4j_logic.py | 47 +++-- src/main.py | 2 +- 16 files changed, 283 insertions(+), 193 deletions(-) delete mode 100644 src/hs_ontology_api/cypher/util_query.py create mode 100644 src/hs_ontology_api/models/pagination.py create mode 100644 src/hs_ontology_api/utils/README.md create mode 100644 src/hs_ontology_api/utils/__init__.py rename src/hs_ontology_api/{routes => utils}/cellsclient.py (97%) rename src/hs_ontology_api/{routes => utils}/neo4j_logic.py (96%) diff --git a/src/hs_ontology_api/cypher/util_query.py b/src/hs_ontology_api/cypher/util_query.py deleted file mode 100644 index 402c47e..0000000 --- a/src/hs_ontology_api/cypher/util_query.py +++ /dev/null @@ -1,22 +0,0 @@ -# coding: utf-8 - -# JAS September 2023 - -# Utilities to load large Cypher query strings. - -import os - -def loadquerystring(filename: str) ->str: - - # Load a query string from a file. - # filename: filename, without path. - - # Assumes that the file is in the cypher directory. - - fpath = os.path.dirname(os.getcwd()) - fpath = os.path.join(fpath,'src/hs_ontology_api/cypher',filename) - - f = open(fpath, "r") - query = f.read() - f.close() - return query diff --git a/src/hs_ontology_api/models/genelist.py b/src/hs_ontology_api/models/genelist.py index 3811c27..4dccf5f 100644 --- a/src/hs_ontology_api/models/genelist.py +++ b/src/hs_ontology_api/models/genelist.py @@ -2,7 +2,7 @@ # JAS October 2023 # GeneList model class -# Used by the geneslist endpoint. +# Used by the genes-info endpoints. # Provides information on genes identified by either the UBKG or the Cells API--i.e., that have relevance to HuBMAP/SenNet. from __future__ import absolute_import @@ -13,64 +13,50 @@ # Sub-object models # Array of gene detail objects from hs_ontology_api.models.genelist_detail import GeneListDetail +from hs_ontology_api.models.pagination import Pagination class GeneList(): - def __init__(self, page=None, total_pages=None, genesperpage=None, genes=None, starts_with=None, gene_count=None): + def __init__(self, page=None, totalpages=None, genesperpage=None, genes=None, startswith=None, genecount=None): """GeneList - a model defined in OpenAPI - :param page: Relative "page" (block of genes) + :param page: Requested relative "page" (block of genes) :type page: str - :param total_pages: Total number of "pages" (blocks of genes) - :type total_pages: str - :param genes: List of genes in page + :param totalpages: Calculated total number of "pages" (blocks of genes) + :type totalpages: str + :param genes: List of gene objects for an array :type genes: List[GeneListDetail] - :param genesperpage: Number of genes in each "page" (block) - :type genes: str - :starts_with: Search string for type ahead - :type starts_with: str - :gene_count: Count of (filtered) genes - :type gene_count: str - + :param genesperpage: Requested number of genes in each "page" (block) + :type genesperpage: str + :startswith: Optional search string for type ahead + :type startswith: str + :genecount: Calculated count of genes that satisfied the search criteria + :type genecount: str """ - # Parameters other than page will be used to build nested GeneListDetail objects. + # The page, totalpages, genesperpage, startswith, and genecount parameters will be used to build a + # Pagination object. + # The genes parameter will be used to build an array of GeneListDetail objects. # Types for JSON objects self.openapi_types = { - 'page': int, - 'total_pages': int, - 'genes_per_page': int, + 'pagination': Pagination, 'genes': List[GeneListDetail], - 'starts_with': str, - 'gene_count': int } # Attribute mappings used by the base Model class to assert key/value pairs. self.attribute_map = { - 'page': 'page', - 'total_pages': 'total_pages', - 'genes_per_page': 'genes_per_page', + 'pagination': 'pagination', 'genes': 'genes', - 'starts_with': 'starts_with', - 'gene_count': 'gene_count' } # Property assignments - self._page = int(page) - self._total_pages = int(total_pages) - self._genes_per_page = int(genesperpage) self._genes = genes - self._starts_with = starts_with - self._gene_count = gene_count + self._pagination = Pagination(page, totalpages, genesperpage, startswith, genecount).serialize() def serialize(self): # Key/value format of response. return { - "page": self._page, - "total_pages": self._total_pages, - "genes_per_page": self._genes_per_page, + "pagination": self._pagination, "genes": self._genes, - "starts_with": self._starts_with, - "gene_count": self._gene_count } @classmethod @@ -85,26 +71,25 @@ def from_dict(cls, dikt) -> 'GeneList': return util.deserialize_model(dikt, cls) @property - def page(self): - """Gets the page of this GeneList. + def pagination(self): + """Gets the pagination of this GeneList. - 'Page' or relative block of genes. - :return: The page of this GeneList. + Pagination statistics + :return: The pagination of this GeneList. :rtype: str """ - return self._page - - @page.setter - def page(self, page): - """Sets the page of this GeneList. + return self._pagination - 'Page' or relative block of genes. + @pagination.setter + def pagination(self, pagination): + """Sets the pagination of this GeneList. - :param page: The page of this GeneList - :type page: str + Pagination statistics + :param pagination: The pagination of this GeneList + :type pagination: str """ - self._page = page + self._pagination = pagination @property def genes(self): @@ -128,88 +113,3 @@ def genes(self, genes): self._genes = genes - @property - def total_pages(self): - """Gets the total_pages of this GeneList. - - Total number of "pages" (blocks of genes) - :return: The total_pages of this GeneList. - :rtype: int - """ - return self._total_pages - - @total_pages.setter - def total_pages(self, total_pages): - """Sets the total_pages of this GeneList. - - Total number of "pages" (blocks of genes) - - :param total_pages: The genes of this GeneList - :type genes: int - """ - - self._total_pages = total_pages - - @property - def genes_per_page(self): - """Gets the genes_per_page of this GeneList. - - Number of genes per "page" or block of returns - :return: The total_pages of this GeneList. - :rtype: int - """ - return self._total_pages - - @genes_per_page.setter - def genes_per_page(self, genes_per_page): - """Sets the genes_per_page of this GeneList. - - Number of genes per "page" or block of returns - - :param genes_per_page: The genes_per_page of this GeneList - :type genes_per_page: int - """ - - self._genes_per_page = genes_per_page - @property - def starts_with(self): - """Gets the starts_with of this GeneList. - - Optional type-ahead search string - :return: The starts_with of this GeneList. - :rtype: str - """ - return self._starts_with - - @starts_with.setter - def starts_with(self, starts_with): - """Sets the starts_with of this GeneList. - - Optional type-ahead search string - - :param starts_with: The genes_per_page of this GeneList - :type starts_with: int - """ - - self._starts_with = starts_with - @property - def gene_count(self): - """Gets the gene_count of this GeneList. - - Count of genes - :return: The gene_count of this GeneList. - :rtype: str - """ - return self._gene_count - - @gene_count.setter - def gene_count(self, gene_count): - """Sets the gene_count of this GeneList. - - Count of genes - - :param gene_count: The gene_count of this GeneList - :type gene_count: int - """ - - self._gene_count = gene_count diff --git a/src/hs_ontology_api/models/pagination.py b/src/hs_ontology_api/models/pagination.py new file mode 100644 index 0000000..289b742 --- /dev/null +++ b/src/hs_ontology_api/models/pagination.py @@ -0,0 +1,188 @@ +# coding: utf-8 + +# JAS November 2023 +# Pagination model class +# Used by the *s-info endpoints--e.g., genes-info, proteins-info + +from __future__ import absolute_import +from typing import List +from ubkg_api.models.base_model_ import Model +from ubkg_api.models import util + +class Pagination(): + + def __init__(self, page=None, totalpages=None, itemsperpage=None, startswith=None, itemcount=None): + """Pagination - a model defined in OpenAPI + + :param page: Relative "page" (block of items) + :param totalpages: Total number of "pages" (blocks of items) + :param itemsperpage: Number of items in each "page" (block) + :param startswith: Optional search string for type ahead + :param itemcount: Count of items, optionally filtered by startswith + + """ + + # Types for JSON objects + self.openapi_types = { + 'page': int, + 'totalpages': int, + 'itemsperpage': int, + 'startswith': str, + 'itemcount': int + } + + # Attribute mappings used by the base Model class to assert key/value pairs. + self.attribute_map = { + 'page': 'page', + 'totalpages': 'totalpages', + 'itemsperpage': 'itemsperpage', + 'startswith': 'startswith', + 'itemcount': 'itemcount' + } + # Property assignments + self._page = int(page) + self._totalpages = int(totalpages) + self._itemsperpage = int(itemsperpage) + self._startswith = startswith + self._itemcount = itemcount + + def serialize(self): + # Key/value format of response. + return { + "page": self._page, + "totalpages": self._totalpages, + "genes_per_page": self._itemsperpage, + "startswith": self._startswith, + "itemcount": self._itemcount + } + + @classmethod + def from_dict(cls, dikt) -> 'Pagination': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The class + :rtype: GeneDetail + """ + return util.deserialize_model(dikt, cls) + + def serialize(self): + # Key/value format of response. + return { + "page": self._page, + "totalpages": self._totalpages, + "itemsperpage": self._itemsperpage, + "startswith": self._startswith, + "itemcount": self._itemcount + } + + @property + def page(self): + """Gets the page of this Pagination. + + 'Page' or relative block of items. + :return: The page of this Pagination. + :rtype: str + """ + return self._page + + @page.setter + def page(self, page): + """Sets the page of this Pagination. + + 'Page' or relative block of items. + + :param page: The page of this Pagination + :type page: str + """ + + self._page = page + + @property + def totalpages(self): + """Gets the total_pages of this Pagination. + + Total number of "pages" (blocks of items) + :return: The total_pages of this Pagination. + :rtype: int + """ + return self._totalpages + + @totalpages.setter + def total_pages(self, totalpages): + """Sets the total_pages of this Pagination. + + Total number of "pages" (blocks of items) + + :param totalpages: The number of itmes in this Pagination + :type genes: int + """ + + self._totalpages = totalpages + + @property + def itemsperpage(self): + """Gets the itemsperpage of this Pagination. + + Number of items per "page" or block of returns + :return: The itemsperpage of this Pagination. + :rtype: int + """ + return self._itemsperpage + + @itemsperpage.setter + def genes_per_page(self, itemsperpage): + """Sets the itemsperpage of this Pagination. + + Number of items per "page" or block of returns + + :param itemsperpage: The itemsperpage of this Pagination + :type itemsperpage: int + """ + + self._itemsperpage = itemsperpage + + @property + def startswith(self): + """Gets the startswith of this Pagination. + + Optional type-ahead search string + :return: The startswith of this Pagination. + :rtype: str + """ + return self._startswith + + @startswith.setter + def startswith(self, startswith): + """Sets the startswith of this Pagination. + + Optional type-ahead search string + + :param starts_with: The startswith of this Pagination + :type startswith: int + """ + + self._startswith = startswith + + @property + def itemcount(self): + """Gets the itemcount of this Pagination. + + Count of items, optionally filtered by startswith. + :return: The itemcount of this Pagination. + :rtype: str + """ + return self._itemcount + + @itemcount.setter + def itemcount(self, itemcount): + """Sets the itemcount of this Pagination. + + Count of items, optionally filtered by startswith + + :param itemcount: The itemcount of this Pagination + :type itemcount: int + """ + + self._itemcount = itemcount \ No newline at end of file diff --git a/src/hs_ontology_api/routes/assayname/assayname_controller.py b/src/hs_ontology_api/routes/assayname/assayname_controller.py index 92ae027..65b3199 100644 --- a/src/hs_ontology_api/routes/assayname/assayname_controller.py +++ b/src/hs_ontology_api/routes/assayname/assayname_controller.py @@ -1,6 +1,6 @@ from flask import Blueprint, jsonify, current_app, request, make_response -from hs_ontology_api.routes.neo4j_logic import assaytype_name_get_logic +from hs_ontology_api.utils.neo4j_logic import assaytype_name_get_logic assayname_blueprint = Blueprint('assayname', __name__, url_prefix='/assayname') diff --git a/src/hs_ontology_api/routes/assaytype/assaytype_controller.py b/src/hs_ontology_api/routes/assaytype/assaytype_controller.py index c99de88..4ca052f 100644 --- a/src/hs_ontology_api/routes/assaytype/assaytype_controller.py +++ b/src/hs_ontology_api/routes/assaytype/assaytype_controller.py @@ -2,7 +2,7 @@ from ubkg_api.common_routes.validate import validate_application_context -from hs_ontology_api.routes.neo4j_logic import assaytype_get_logic, assaytype_name_get_logic +from hs_ontology_api.utils.neo4j_logic import assaytype_get_logic, assaytype_name_get_logic assaytype_blueprint = Blueprint('assaytype', __name__, url_prefix='/assaytype') diff --git a/src/hs_ontology_api/routes/datasets/datasets_controller.py b/src/hs_ontology_api/routes/datasets/datasets_controller.py index 6185f74..a227a04 100644 --- a/src/hs_ontology_api/routes/datasets/datasets_controller.py +++ b/src/hs_ontology_api/routes/datasets/datasets_controller.py @@ -2,7 +2,7 @@ from ubkg_api.common_routes.validate import validate_application_context -from ..neo4j_logic import dataset_get_logic +from hs_ontology_api.utils.neo4j_logic import dataset_get_logic datasets_blueprint = Blueprint('datasets_hs', __name__, url_prefix='/datasets') diff --git a/src/hs_ontology_api/routes/genes/genes_controller.py b/src/hs_ontology_api/routes/genes/genes_controller.py index 857028c..2955f6a 100644 --- a/src/hs_ontology_api/routes/genes/genes_controller.py +++ b/src/hs_ontology_api/routes/genes/genes_controller.py @@ -1,7 +1,7 @@ # coding: utf-8 # JAS September 2023 -from flask import Blueprint, jsonify, current_app, request, make_response -from ..neo4j_logic import genedetail_get_logic +from flask import Blueprint, jsonify, current_app, make_response +from hs_ontology_api.utils.neo4j_logic import genedetail_get_logic genes_blueprint = Blueprint('genes', __name__, url_prefix='/genes') diff --git a/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py b/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py index 93da0d5..a4c8623 100644 --- a/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py +++ b/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py @@ -1,7 +1,7 @@ # coding: utf-8 # JAS October 2023 from flask import Blueprint, jsonify, current_app, request, make_response -from ..neo4j_logic import genelist_get_logic,genelist_count_get_logic +from hs_ontology_api.utils.neo4j_logic import genelist_get_logic,genelist_count_get_logic import math genesinfo_blueprint = Blueprint('genes-info', __name__, url_prefix='/genes-info') @@ -18,9 +18,9 @@ def geneslist() -> list[str]: # Obtain parameters. page = request.args.get('page') genesperpage = request.args.get('genesperpage') - starts_with = request.args.get('starts_with') - if starts_with is None: - starts_with = '' + startswith = request.args.get('startswith') + if startswith is None: + startswith = '' # Validate and set defaults for genesperpage. if genesperpage is None: @@ -31,9 +31,9 @@ def geneslist() -> list[str]: return (make_response(f'The value for parameter genesperpage ({genesperpage}) must be greater than zero.', 400)) # Obtain the total count of genes, considering the filter starts_with. - gene_count = genelist_count_get_logic(neo4j_instance, starts_with) - if gene_count == 0: - return make_response(f'There are no genes with HGNC symbols that start with \'{starts_with}\'.', 404) + genecount = genelist_count_get_logic(neo4j_instance, startswith) + if genecount == 0: + return make_response(f'There are no genes with HGNC symbols that start with \'{startswith}\'.', 404) # Default values for page. # Case: No parameter specified. @@ -45,12 +45,12 @@ def geneslist() -> list[str]: page = '1' # Calculate the total number of (filtered) pages. - total_pages = str(math.ceil(int(gene_count) / int(genesperpage))) + totalpages = str(math.ceil(int(genecount) / int(genesperpage))) # Translation for cases "last" or "first" - print(f'total_pages={total_pages}') + print(f'total_pages={totalpages}') if page == 'last': - page = str(int(total_pages)) + page = str(int(totalpages)) if page == 'first': page = '1' @@ -60,9 +60,9 @@ def geneslist() -> list[str]: if int(page) < 0: return make_response(f'The value for parameter page ({page}) must be >= 0', 400) - if int(page) > int(total_pages): - page = str(int(total_pages)) + if int(page) > int(totalpages): + page = str(int(totalpages)) # Obtain results. neo4j_instance = current_app.neo4jConnectionHelper.instance() - return jsonify(genelist_get_logic(neo4j_instance, page=page, total_pages=total_pages, genesperpage=genesperpage, starts_with=starts_with, gene_count=gene_count)) \ No newline at end of file + return jsonify(genelist_get_logic(neo4j_instance, page=page, totalpages=totalpages, genesperpage=genesperpage, startswith=startswith, genecount=genecount)) \ No newline at end of file diff --git a/src/hs_ontology_api/routes/organs/organs_controller.py b/src/hs_ontology_api/routes/organs/organs_controller.py index 9e23825..affd8ad 100644 --- a/src/hs_ontology_api/routes/organs/organs_controller.py +++ b/src/hs_ontology_api/routes/organs/organs_controller.py @@ -2,7 +2,7 @@ from ubkg_api.common_routes.validate import validate_application_context -from ..neo4j_logic import get_organ_types_logic +from hs_ontology_api.utils.neo4j_logic import get_organ_types_logic organs_blueprint = Blueprint('organs_hs', __name__, url_prefix='/organs') diff --git a/src/hs_ontology_api/routes/relationships/relationships_controller.py b/src/hs_ontology_api/routes/relationships/relationships_controller.py index 05e0d29..540f96b 100644 --- a/src/hs_ontology_api/routes/relationships/relationships_controller.py +++ b/src/hs_ontology_api/routes/relationships/relationships_controller.py @@ -1,6 +1,6 @@ from flask import Blueprint, jsonify, current_app, make_response -from ..neo4j_logic import relationships_for_gene_target_symbol_get_logic +from hs_ontology_api.utils.neo4j_logic import relationships_for_gene_target_symbol_get_logic relationships_blueprint = Blueprint('relationships', __name__, url_prefix='/relationships') diff --git a/src/hs_ontology_api/routes/valueset/valueset_controller.py b/src/hs_ontology_api/routes/valueset/valueset_controller.py index 12ed3b8..6c8c9db 100644 --- a/src/hs_ontology_api/routes/valueset/valueset_controller.py +++ b/src/hs_ontology_api/routes/valueset/valueset_controller.py @@ -1,6 +1,6 @@ from flask import Blueprint, jsonify, current_app, request -from ..neo4j_logic import valueset_get_logic +from hs_ontology_api.utils.neo4j_logic import valueset_get_logic valueset_blueprint = Blueprint('valueset_hs', __name__, url_prefix='/valueset') diff --git a/src/hs_ontology_api/utils/README.md b/src/hs_ontology_api/utils/README.md new file mode 100644 index 0000000..8b4dd21 --- /dev/null +++ b/src/hs_ontology_api/utils/README.md @@ -0,0 +1,9 @@ +# hs-ontology-api +# Utility scripts + +## neo4j_logic.py +Scripts that execute queries against the UBKG instance and return JSON to endpoints. + +## cellsclient.py +A wrapper class around the Cells API python client. + diff --git a/src/hs_ontology_api/utils/__init__.py b/src/hs_ontology_api/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/hs_ontology_api/routes/cellsclient.py b/src/hs_ontology_api/utils/cellsclient.py similarity index 97% rename from src/hs_ontology_api/routes/cellsclient.py rename to src/hs_ontology_api/utils/cellsclient.py index 1cf53d2..4b3f531 100644 --- a/src/hs_ontology_api/routes/cellsclient.py +++ b/src/hs_ontology_api/utils/cellsclient.py @@ -8,7 +8,7 @@ from hubmap_api_py_client.errors import ClientError # Array of cell type objects -from ..models.genedetail_celltype import GeneDetailCellType +from hs_ontology_api.models.genedetail_celltype import GeneDetailCellType import pandas as pd import os diff --git a/src/hs_ontology_api/routes/neo4j_logic.py b/src/hs_ontology_api/utils/neo4j_logic.py similarity index 96% rename from src/hs_ontology_api/routes/neo4j_logic.py rename to src/hs_ontology_api/utils/neo4j_logic.py index c3a93ba..296c7b5 100644 --- a/src/hs_ontology_api/routes/neo4j_logic.py +++ b/src/hs_ontology_api/utils/neo4j_logic.py @@ -2,6 +2,7 @@ import neo4j from typing import List import pandas as pd +import os from flask import current_app @@ -17,13 +18,28 @@ from hs_ontology_api.models.genelist_detail import GeneListDetail # Query utilities -from hs_ontology_api.cypher.util_query import loadquerystring +# from hs_ontology_api.cypher.util_query import loadquerystring logging.basicConfig(format='[%(asctime)s] %(levelname)s in %(module)s:%(lineno)d: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=logging.INFO) logger = logging.getLogger(__name__) +def loadquerystring(filename: str) ->str: + + # Load a query string from a file. + # filename: filename, without path. + + # Assumes that the file is in the cypher directory. + + fpath = os.path.dirname(os.getcwd()) + fpath = os.path.join(fpath,'src/hs_ontology_api/cypher',filename) + + f = open(fpath, "r") + query = f.read() + f.close() + return query + def make_assaytype_property_info(record): return AssayTypePropertyInfo( record['data_type'], @@ -664,7 +680,7 @@ def genedetail_get_logic(neo4j_instance, gene_id: str) -> List[GeneDetail]: return genedetails -def genelist_get_logic(neo4j_instance, page:str, total_pages:str, genesperpage:str, starts_with:str, gene_count:str) -> List[GeneList]: +def genelist_get_logic(neo4j_instance, page:str, totalpages:str, genesperpage:str, startswith:str, genecount:str) -> List[GeneList]: """ Returns information on HGNC genes. @@ -672,13 +688,12 @@ def genelist_get_logic(neo4j_instance, page:str, total_pages:str, genesperpage:s list with pagination features. :param neo4j_instance: neo4j client - :page: Zero-based number of pages with rows=pagesize to skip in neo4j query - :genesperpage: number of rows to limit in neo4j query + :param page: Zero-based number of pages with rows=pagesize to skip in neo4j query + :param totalpages: Calculated number of pages of genes + :param genesperpage: number of rows to limit in neo4j query + :param startswith: string for type-ahead (starts with) searches + :param genecount: Calculated total count of genes, optionally filtered with startswith :return: List[GeneList] - :starts_with: string for type-ahead (starts with) searches - :return: str - :gene_count: filtered count of genes - :return: str """ @@ -702,8 +717,8 @@ def genelist_get_logic(neo4j_instance, page:str, total_pages:str, genesperpage:s skiprows = intpage * int(genesperpage) starts_with_clause = '' - if starts_with != '': - starts_with_clause = f'AND map[\'approved_symbol\'][0] STARTS WITH \'{starts_with}\'' + if startswith != '': + starts_with_clause = f'AND map[\'approved_symbol\'][0] STARTS WITH \'{startswith}\'' query = query.replace('$starts_with_clause',starts_with_clause) query = query.replace('$skiprows', str(skiprows)) query = query.replace('$limitrows', str(genesperpage)) @@ -722,16 +737,16 @@ def genelist_get_logic(neo4j_instance, page:str, total_pages:str, genesperpage:s except KeyError: pass # Use the list of gene details with the page to build a genelist object. - genelist: GeneList = GeneList(page, total_pages, genesperpage, genes, starts_with, gene_count).serialize() + genelist: GeneList = GeneList(page, totalpages, genesperpage, genes, startswith, genecount).serialize() return genelist -def genelist_count_get_logic(neo4j_instance, starts_with: str) -> int: +def genelist_count_get_logic(neo4j_instance, startswith: str) -> int: """ Returns the count of HGNC genes in the UBKG. - If starts_with is non-null, returns the count of HGNC genes with approved symbol + If startswith is non-null, returns the count of HGNC genes with approved symbol that starts with the parameter value. :param neo4j_instance: neo4j client - :param starts_with: filtering string for STARTS WITH queries + :param startswith: filtering string for STARTS WITH queries :return: integer count """ # @@ -740,8 +755,8 @@ def genelist_count_get_logic(neo4j_instance, starts_with: str) -> int: queryfile = 'geneslist_count.cypher' query = loadquerystring(queryfile) starts_with_clause = '' - if starts_with != '': - starts_with_clause = f'AND tGene.name STARTS WITH \'{starts_with}\'' + if startswith != '': + starts_with_clause = f'AND tGene.name STARTS WITH \'{startswith}\'' query = query.replace('$starts_with_clause', starts_with_clause) with neo4j_instance.driver.session() as session: diff --git a/src/main.py b/src/main.py index cf6deec..100d470 100755 --- a/src/main.py +++ b/src/main.py @@ -14,7 +14,7 @@ from hs_ontology_api.routes.genesinfo.genesinfo_controller import genesinfo_blueprint # Cells API client -from hs_ontology_api.routes.cellsclient import OntologyCellsClient +from hs_ontology_api.utils.cellsclient import OntologyCellsClient def make_flask_config(): temp_flask_app = Flask(__name__, From 05c6aa1f389921f6d0bdac10e7e4f2548cb52ace Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Tue, 7 Nov 2023 20:34:01 -0600 Subject: [PATCH 07/11] Updated test script for renamed genes endpoints. --- test_api.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test_api.sh b/test_api.sh index e7e51b0..e7ab62c 100755 --- a/test_api.sh +++ b/test_api.sh @@ -116,27 +116,27 @@ curl --request GET \ echo # Test for gene_list endpoint -echo "genes GET" +echo "genes-info GET" curl --request GET \ - --url "${UBKG_URL}/genes?page=1&genesperpage=3" \ + --url "${UBKG_URL}/genes-info?page=1&genesperpage=3" \ --header "Content-Type: application/json" echo -echo "genes GET last page" +echo "genes-info GET last page" curl --request GET \ - --url "${UBKG_URL}/genes?page=last&genesperpage=3" \ + --url "${UBKG_URL}/genes-info?page=last&genesperpage=3" \ --header "Content-Type: application/json" echo -echo "genes GET starts_with B" +echo "genes-info GET starts_with B" curl --request GET \ - --url "${UBKG_URL}/genes?genesperpage=3&starts_with=B" \ + --url "${UBKG_URL}/genes-info?genesperpage=3&starts_with=B" \ --header "Content-Type: application/json" echo # Test for gene endpoint. -echo "gene GET for MMRN1" +echo "genes GET for MMRN1" curl --request GET \ - --url "${UBKG_URL}/gene/MMRN1" \ + --url "${UBKG_URL}/genes/MMRN1" \ --header "Content-Type: application/json" echo From 80126f87603e0b08188df4aec2fcb4357c902344 Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Tue, 7 Nov 2023 21:00:20 -0600 Subject: [PATCH 08/11] Updated SmartAPI YAML for genes-info/genes, proteins-info/proteins, and celltypes-info/celltypes endpoints. Updated YAML for new pagination object in *-info endpoints. --- hs-ontology-api-spec.yaml | 176 ++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 82 deletions(-) diff --git a/hs-ontology-api-spec.yaml b/hs-ontology-api-spec.yaml index 562c2e7..b062b06 100644 --- a/hs-ontology-api-spec.yaml +++ b/hs-ontology-api-spec.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: HubMAP/SenNet Ontology API (hs-ontology-api) - description: The HuBMAP/SenNet Ontology API contains endpoints for querying a UBKG(https://ubkg.docs.xconsortia.org/) instance with content from the HuBMAP/SenNet context (https://ubkg.docs.xconsortia.org/contexts/#hubmapsennet-context). The hs-ontology-api imports the [ubkg-api](https://github.com/x-atlas-consortia/ubkg-api), which encapsulates both basic connectivity to a UBKG instance and generic endpoint code. This specification describes endpoints of both hs-ontology-api and ubkg-api. + description: The HuBMAP/SenNet Ontology API contains endpoints for querying a [UBKG](https://ubkg.docs.xconsortia.org/) instance with content from the [HuBMAP/SenNet context](https://ubkg.docs.xconsortia.org/contexts/#hubmapsennet-context). The hs-ontology-api imports the [ubkg-api](https://smart-api.info/ui/96e5b5c0b0efeef5b93ea98ac2794837), which encapsulates both basic connectivity to a UBKG instance and generic endpoint code. version: 1.3.1 contact: name: GitHub repository @@ -350,10 +350,10 @@ paths: type: array items: $ref: '#/components/schemas/SabCodeTerm' - /genes: + /genes-info: get: - operationId: gene_list_get - summary: Return high-level information on a set of genes specified by pagination parameters. + operationId: genes_info_get + summary: Return high-level information on a set of genes specified by pagination request parameters. parameters: - name: page in: query @@ -370,7 +370,7 @@ paths: schema: type: string default: 10 - - name: starts_with + - name: startswith in: query required: false description: Optional string by which to filter by gene symbol. @@ -389,12 +389,12 @@ paths: '400': description: Invalid parameter--e.g., *genesperpage* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' '404': - description: No genes that satisfied the criteria were found. This usually means that there are no genes with HGNC approved symbols that start with the string specified in the *starts_with* parameter. + description: No genes that satisfied the criteria were found. This usually means that there are no genes with HGNC approved symbols that start with the string specified in the *startswith* parameter. '5XX': description: Unexpected error - /gene/{id}: + /genes/{id}: get: - operationId: gene_detail_get + operationId: genes_get summary: Return detailed information on a gene specified by a HGNC identifier. parameters: - name: id @@ -417,10 +417,10 @@ paths: description: No information for gene with HGNC identifier *id*. '5XX': description: Unexpected error - /proteins: + /proteins-info: get: - operationId: protein_list_get - summary: Return high-level information on a set of proteins specified by pagination parameters. + operationId: protein_info_get + summary: Return high-level information on a set of proteins specified by pagination request parameters. parameters: - name: page in: query @@ -437,7 +437,7 @@ paths: schema: type: string default: 10 - - name: starts_with + - name: startswith in: query required: false description: Optional *case-insensitive* string by which to filter by UniProtKB protein name. @@ -456,12 +456,12 @@ paths: '400': description: Invalid parameter--e.g., *proteinsperpage* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' '404': - description: No proteins that satisfied the criteria were found. This usually means that there are no proteins with UniprotKB names that start with the string specified in the *starts_with* parameter. + description: No proteins that satisfied the criteria were found. This usually means that there are no proteins with UniprotKB names that start with the string specified in the *startswith* parameter. '5XX': description: Unexpected error - /protein/{id}: + /proteins/{id}: get: - operationId: protein_detail_get + operationId: proteins_get summary: Return detailed information on a protein specified by a UniProtKB identifier. parameters: - name: id @@ -484,10 +484,10 @@ paths: description: No information for protein with UniProtKB identifier *id*. '5XX': description: Unexpected error - /celltypes: + /celltypes-info: get: - operationId: celltype_list_get - summary: Return high-level information on a set of cell types specified by pagination parameters. + operationId: celltypes_info_get + summary: Return high-level information on a set of cell types specified by pagination request parameters. parameters: - name: page in: query @@ -504,7 +504,7 @@ paths: schema: type: string default: 10 - - name: starts_with + - name: startswith in: query required: false description: Optional *case-insensitive* string by which to filter by cell type name. @@ -523,12 +523,12 @@ paths: '400': description: Invalid parameter--e.g., *celltypesperpage* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' '404': - description: No cell types that satisfied the criteria were found. This usually means that there are no cell types with names that start with the string specified in the *starts_with* parameter. + description: No cell types that satisfied the criteria were found. This usually means that there are no cell types with names that start with the string specified in the *startswith* parameter. '5XX': description: Unexpected error - /celltype/{id}: + /celltypes/{id}: get: - operationId: celltype_detail_get + operationId: celltypes_get summary: Return detailed information on a cell type specified by a name. parameters: - name: id @@ -870,26 +870,30 @@ components: type: string description: RefSeq summary for the gene example: Multimerin is a massive, soluble protein found in.... - genes_per_page: - type: number - description: value of genesperpage parameter from the gene_list endpoint - example: 3 - page: - type: number - description: value of page parameter from the gene_list endpoint - example: 1 - total_pages: - type: number - description: total number of "pages" based on the values of genesperpage and the total number of genes - example: 14381 - starts_with: - type: string - description: value of starts_with from the gene_list endpoint - example: M - gene_count: - type: number - description: Count of genes. If starts_with is non-null, the count is that of the genes with approved HGNC symbols that start with the value of starts_with. - example: 500 + pagination: + type: object + description: pagination statistics + properties: + itemsperpage: + type: number + description: original value of genesperpage request parameter + example: 3 + page: + type: number + description: original value of page request parameter + example: 1 + totalpages: + type: number + description: calculated total number of "pages" (blocks) based on the values of itemsperpage and the total number of items + example: 14381 + startswith: + type: string + description: original value of startswith request parameter + example: M + itemcount: + type: number + description: Calculated count of items. If startswith is non-null, the count is that of the genes with approved HGNC symbols that start with the value of starts_with. + example: 500 GeneDetailResponse: # JAS Sept 2023 type: object description: Detailed information on a gene requested by the gene endpoint @@ -1016,26 +1020,30 @@ components: type: string description: UniProtKB Recommended Name example: multimerin-1 - proteins_per_page: - type: number - description: value of proteinsperpage parameter from the proteins endpoint - example: 3 - page: - type: number - description: value of page parameter from the proteins endpoint - example: 1 - total_pages: - type: number - description: total number of "pages" based on the values of proteinsperpage and the total number of proteins - example: 14381 - starts_with: - type: string - description: value of starts_with from the proteins endpoint - example: M - protein_count: - type: number - description: Count of proteins. If starts_with is non-null, the count is that of the proteins with names that start with the value of starts_with. - example: 500 + pagination: + type: object + description: pagination statistics + properties: + itemsperpage: + type: number + description: original value of proteinsperpage request parameter + example: 3 + page: + type: number + description: original value of page request parameter + example: 1 + totalpages: + type: number + description: calculated total number of "pages" (blocks) based on the values of proteinsperpage and the total number of proteins + example: 14381 + startswith: + type: string + description: original value of startswith request parameter + example: M + itemcount: + type: number + description: Count of proteins. If startswith is non-null, the count is that of the proteins with names that start with the value of startswith. + example: 500 ProteinDetailResponse: type: object description: Detailed information on a protein requested by the protein endpoint @@ -1110,26 +1118,30 @@ components: type: string description: Definition of the cell type from Cell Ontology example: A endothelial cell of a lymphatic vessel. The border of… - celltypes_per_page: - type: number - description: value of celltypesperpage parameter from the celltypes endpoint - example: 3 - page: - type: number - description: value of page parameter from the celltypes endpoint - example: 1 - total_pages: - type: number - description: total number of "pages" based on the values of celltypesperpage and the total number of celltypes - example: 14381 - starts_with: - type: string - description: value of starts_with from the celltypes endpoint - example: E - cell_type_count: - type: number - description: Count of cell types. If starts_with is non-null, the count is that of the cell types with names that start with the value of starts_with. - example: 500 + pagination: + type: object + description: pagination statistics + properties: + itemsperpage: + type: number + description: original value of celltypesperpage request parameter + example: 3 + page: + type: number + description: original value of page request parameter + example: 1 + totalpages: + type: number + description: calculated total number of "pages" (blocks) based on the values of celltypesperpage and the total number of celltypes + example: 14381 + startswith: + type: string + description: original value of startswith request parameter + example: E + itemcount: + type: number + description: calculated count of cell types. If startswith is non-null, the count is that of the cell types with names that start with the value of startswith. + example: 500 CellTypeDetailResponse: type: object description: Detailed information on a cell type requested by the celltype endpoint From b1faaee66555c1d690e70cadba0a10b2b61a43db Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Wed, 8 Nov 2023 11:18:55 -0600 Subject: [PATCH 09/11] Updated to change pagination-related parameters to use underscores (e.g. starts_with instead of startswith). --- hs-ontology-api-spec.yaml | 78 +++++------ src/hs_ontology_api/models/genelist.py | 22 +-- src/hs_ontology_api/models/pagination.py | 126 +++++++++--------- .../routes/genesinfo/genesinfo_controller.py | 41 +++--- src/hs_ontology_api/utils/neo4j_logic.py | 34 ++--- test_api.sh | 6 +- 6 files changed, 154 insertions(+), 153 deletions(-) diff --git a/hs-ontology-api-spec.yaml b/hs-ontology-api-spec.yaml index b062b06..30f7b93 100644 --- a/hs-ontology-api-spec.yaml +++ b/hs-ontology-api-spec.yaml @@ -358,19 +358,19 @@ paths: - name: page in: query required: false - description: Offset set of genes of count=genesperpage. Can be a non-negative number or the words "first" or "last". + description: Offset set of genes of count=genes_per_page. Can be a non-negative number or the words "first" or "last". schema: type: string default: 1 example: 1 - - name: genesperpage + - name: genes_per_page in: query required: false description: Number of genes per "page" to return. Can be a nonzero number. schema: type: string default: 10 - - name: startswith + - name: starts_with in: query required: false description: Optional string by which to filter by gene symbol. @@ -387,9 +387,9 @@ paths: items: $ref: '#/components/schemas/GeneListResponse' '400': - description: Invalid parameter--e.g., *genesperpage* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' + description: Invalid parameter--e.g., *genes_per_page* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' '404': - description: No genes that satisfied the criteria were found. This usually means that there are no genes with HGNC approved symbols that start with the string specified in the *startswith* parameter. + description: No genes that satisfied the criteria were found. This usually means that there are no genes with HGNC approved symbols that start with the string specified in the *starts_with* parameter. '5XX': description: Unexpected error /genes/{id}: @@ -425,19 +425,19 @@ paths: - name: page in: query required: false - description: Offset set of proteins of count=proteinsperpage. Can be a non-negative number or the words "first" or "last". + description: Offset set of proteins of count=proteins_per_page. Can be a non-negative number or the words "first" or "last". schema: type: string default: 1 example: 1 - - name: proteinsperpage + - name: proteins_per_page in: query required: false description: Number of proteins per "page" to return. Can be a nonzero number. schema: type: string default: 10 - - name: startswith + - name: starts_with in: query required: false description: Optional *case-insensitive* string by which to filter by UniProtKB protein name. @@ -454,9 +454,9 @@ paths: items: $ref: '#/components/schemas/ProteinListResponse' '400': - description: Invalid parameter--e.g., *proteinsperpage* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' + description: Invalid parameter--e.g., *proteins_per_page* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' '404': - description: No proteins that satisfied the criteria were found. This usually means that there are no proteins with UniprotKB names that start with the string specified in the *startswith* parameter. + description: No proteins that satisfied the criteria were found. This usually means that there are no proteins with UniprotKB names that start with the string specified in the *starts_with* parameter. '5XX': description: Unexpected error /proteins/{id}: @@ -492,19 +492,19 @@ paths: - name: page in: query required: false - description: Offset set of genes of count=celltypesperpage. Can be a non-negative number or the words "first" or "last". + description: Offset set of genes of count=celltypes_per_page. Can be a non-negative number or the words "first" or "last". schema: type: string default: 1 example: 1 - - name: celltypesperpage + - name: celltypes_per_page in: query required: false description: Number of cell types per "page" to return. Can be a nonzero number. schema: type: string default: 10 - - name: startswith + - name: starts_with in: query required: false description: Optional *case-insensitive* string by which to filter by cell type name. @@ -521,9 +521,9 @@ paths: items: $ref: '#/components/schemas/CellTypeListResponse' '400': - description: Invalid parameter--e.g., *celltypesperpage* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' + description: Invalid parameter--e.g., *celltypes_per_page* is either non-numeric or a negative number; *page* is something other than a non-negative number or the words 'first' or 'last' '404': - description: No cell types that satisfied the criteria were found. This usually means that there are no cell types with names that start with the string specified in the *startswith* parameter. + description: No cell types that satisfied the criteria were found. This usually means that there are no cell types with names that start with the string specified in the *starts_with* parameter. '5XX': description: Unexpected error /celltypes/{id}: @@ -874,25 +874,25 @@ components: type: object description: pagination statistics properties: - itemsperpage: + items_per_page: type: number - description: original value of genesperpage request parameter + description: original value of genes_per_page request parameter example: 3 page: type: number description: original value of page request parameter example: 1 - totalpages: + total_pages: type: number - description: calculated total number of "pages" (blocks) based on the values of itemsperpage and the total number of items + description: calculated total number of "pages" (blocks) based on the values of items_per_page and the total number of items example: 14381 - startswith: + starts_with: type: string - description: original value of startswith request parameter + description: original value of starts_with request parameter example: M - itemcount: + item_count: type: number - description: Calculated count of items. If startswith is non-null, the count is that of the genes with approved HGNC symbols that start with the value of starts_with. + description: Calculated count of items. If starts_with is non-null, the count is that of the genes with approved HGNC symbols that start with the value of starts_with. example: 500 GeneDetailResponse: # JAS Sept 2023 type: object @@ -1024,25 +1024,25 @@ components: type: object description: pagination statistics properties: - itemsperpage: + items_per_page: type: number - description: original value of proteinsperpage request parameter + description: original value of proteins_per_page request parameter example: 3 page: type: number description: original value of page request parameter example: 1 - totalpages: + total_pages: type: number - description: calculated total number of "pages" (blocks) based on the values of proteinsperpage and the total number of proteins + description: calculated total number of "pages" (blocks) based on the values of proteins_per_page and the total number of proteins example: 14381 - startswith: + starts_with: type: string - description: original value of startswith request parameter + description: original value of starts_with request parameter example: M - itemcount: + item_count: type: number - description: Count of proteins. If startswith is non-null, the count is that of the proteins with names that start with the value of startswith. + description: Count of proteins. If starts_with is non-null, the count is that of the proteins with names that start with the value of starts_with. example: 500 ProteinDetailResponse: type: object @@ -1122,25 +1122,25 @@ components: type: object description: pagination statistics properties: - itemsperpage: + items_per_page: type: number - description: original value of celltypesperpage request parameter + description: original value of celltypes_per_page request parameter example: 3 page: type: number description: original value of page request parameter example: 1 - totalpages: + total_pages: type: number - description: calculated total number of "pages" (blocks) based on the values of celltypesperpage and the total number of celltypes + description: calculated total number of "pages" (blocks) based on the values of celltypes_per_page and the total number of celltypes example: 14381 - startswith: + starts_with: type: string - description: original value of startswith request parameter + description: original value of starts_with request parameter example: E - itemcount: + item_count: type: number - description: calculated count of cell types. If startswith is non-null, the count is that of the cell types with names that start with the value of startswith. + description: calculated count of cell types. If starts_with is non-null, the count is that of the cell types with names that start with the value of starts_with. example: 500 CellTypeDetailResponse: type: object diff --git a/src/hs_ontology_api/models/genelist.py b/src/hs_ontology_api/models/genelist.py index 4dccf5f..62399ec 100644 --- a/src/hs_ontology_api/models/genelist.py +++ b/src/hs_ontology_api/models/genelist.py @@ -16,25 +16,25 @@ from hs_ontology_api.models.pagination import Pagination class GeneList(): - def __init__(self, page=None, totalpages=None, genesperpage=None, genes=None, startswith=None, genecount=None): + def __init__(self, page=None, total_pages=None, genes_per_page=None, genes=None, starts_with=None, gene_count=None): """GeneList - a model defined in OpenAPI :param page: Requested relative "page" (block of genes) :type page: str - :param totalpages: Calculated total number of "pages" (blocks of genes) - :type totalpages: str + :param total_pages: Calculated total number of "pages" (blocks of genes) + :type total_pages: str :param genes: List of gene objects for an array :type genes: List[GeneListDetail] - :param genesperpage: Requested number of genes in each "page" (block) - :type genesperpage: str - :startswith: Optional search string for type ahead - :type startswith: str - :genecount: Calculated count of genes that satisfied the search criteria - :type genecount: str + :param genes_per_page: Requested number of genes in each "page" (block) + :type genes_per_page: str + :starts_with: Optional search string for type ahead + :type starts_with: str + :gene_count: Calculated count of genes that satisfied the search criteria + :type gene_count: str """ - # The page, totalpages, genesperpage, startswith, and genecount parameters will be used to build a + # The page, total_pages, genes_per_page, starts_with, and gene_count parameters will be used to build a # Pagination object. # The genes parameter will be used to build an array of GeneListDetail objects. @@ -50,7 +50,7 @@ def __init__(self, page=None, totalpages=None, genesperpage=None, genes=None, st } # Property assignments self._genes = genes - self._pagination = Pagination(page, totalpages, genesperpage, startswith, genecount).serialize() + self._pagination = Pagination(page, total_pages, genes_per_page, starts_with, gene_count).serialize() def serialize(self): # Key/value format of response. diff --git a/src/hs_ontology_api/models/pagination.py b/src/hs_ontology_api/models/pagination.py index 289b742..9bee031 100644 --- a/src/hs_ontology_api/models/pagination.py +++ b/src/hs_ontology_api/models/pagination.py @@ -11,49 +11,49 @@ class Pagination(): - def __init__(self, page=None, totalpages=None, itemsperpage=None, startswith=None, itemcount=None): + def __init__(self, page=None, total_pages=None, items_per_page=None, starts_with=None, item_count=None): """Pagination - a model defined in OpenAPI :param page: Relative "page" (block of items) - :param totalpages: Total number of "pages" (blocks of items) - :param itemsperpage: Number of items in each "page" (block) - :param startswith: Optional search string for type ahead - :param itemcount: Count of items, optionally filtered by startswith + :param total_pages: Total number of "pages" (blocks of items) + :param items_per_page: Number of items in each "page" (block) + :param starts_with: Optional search string for type ahead + :param item_count: Count of items, optionally filtered by starts_with """ # Types for JSON objects self.openapi_types = { 'page': int, - 'totalpages': int, - 'itemsperpage': int, - 'startswith': str, - 'itemcount': int + 'total_pages': int, + 'items_per_page': int, + 'starts_with': str, + 'item_count': int } # Attribute mappings used by the base Model class to assert key/value pairs. self.attribute_map = { 'page': 'page', - 'totalpages': 'totalpages', - 'itemsperpage': 'itemsperpage', - 'startswith': 'startswith', - 'itemcount': 'itemcount' + 'total_pages': 'total_pages', + 'items_per_page': 'items_per_page', + 'starts_with': 'starts_with', + 'item_count': 'item_count' } # Property assignments self._page = int(page) - self._totalpages = int(totalpages) - self._itemsperpage = int(itemsperpage) - self._startswith = startswith - self._itemcount = itemcount + self._total_pages = int(total_pages) + self._items_per_page = int(items_per_page) + self._starts_with = starts_with + self._item_count = item_count def serialize(self): # Key/value format of response. return { "page": self._page, - "totalpages": self._totalpages, - "genes_per_page": self._itemsperpage, - "startswith": self._startswith, - "itemcount": self._itemcount + "total_pages": self._total_pages, + "genes_per_page": self._items_per_page, + "starts_with": self._starts_with, + "item_count": self._item_count } @classmethod @@ -71,10 +71,10 @@ def serialize(self): # Key/value format of response. return { "page": self._page, - "totalpages": self._totalpages, - "itemsperpage": self._itemsperpage, - "startswith": self._startswith, - "itemcount": self._itemcount + "total_pages": self._total_pages, + "items_per_page": self._items_per_page, + "starts_with": self._starts_with, + "item_count": self._item_count } @property @@ -100,89 +100,89 @@ def page(self, page): self._page = page @property - def totalpages(self): + def total_pages(self): """Gets the total_pages of this Pagination. Total number of "pages" (blocks of items) :return: The total_pages of this Pagination. :rtype: int """ - return self._totalpages + return self._total_pages - @totalpages.setter - def total_pages(self, totalpages): + @total_pages.setter + def total_pages(self, total_pages): """Sets the total_pages of this Pagination. Total number of "pages" (blocks of items) - :param totalpages: The number of itmes in this Pagination + :param total_pages: The number of itmes in this Pagination :type genes: int """ - self._totalpages = totalpages + self._total_pages = total_pages @property - def itemsperpage(self): - """Gets the itemsperpage of this Pagination. + def items_per_page(self): + """Gets the items_per_page of this Pagination. Number of items per "page" or block of returns - :return: The itemsperpage of this Pagination. + :return: The items_per_page of this Pagination. :rtype: int """ - return self._itemsperpage + return self._items_per_page - @itemsperpage.setter - def genes_per_page(self, itemsperpage): - """Sets the itemsperpage of this Pagination. + @items_per_page.setter + def genes_per_page(self, items_per_page): + """Sets the items_per_page of this Pagination. Number of items per "page" or block of returns - :param itemsperpage: The itemsperpage of this Pagination - :type itemsperpage: int + :param items_per_page: The items_per_page of this Pagination + :type items_per_page: int """ - self._itemsperpage = itemsperpage + self._items_per_page = items_per_page @property - def startswith(self): - """Gets the startswith of this Pagination. + def starts_with(self): + """Gets the starts_with of this Pagination. Optional type-ahead search string - :return: The startswith of this Pagination. + :return: The starts_with of this Pagination. :rtype: str """ - return self._startswith + return self._starts_with - @startswith.setter - def startswith(self, startswith): - """Sets the startswith of this Pagination. + @starts_with.setter + def starts_with(self, starts_with): + """Sets the starts_with of this Pagination. Optional type-ahead search string - :param starts_with: The startswith of this Pagination - :type startswith: int + :param starts_with: The starts_with of this Pagination + :type starts_with: int """ - self._startswith = startswith + self._starts_with = starts_with @property - def itemcount(self): - """Gets the itemcount of this Pagination. + def item_count(self): + """Gets the item_count of this Pagination. - Count of items, optionally filtered by startswith. - :return: The itemcount of this Pagination. + Count of items, optionally filtered by starts_with. + :return: The item_count of this Pagination. :rtype: str """ - return self._itemcount + return self._item_count - @itemcount.setter - def itemcount(self, itemcount): - """Sets the itemcount of this Pagination. + @item_count.setter + def item_count(self, item_count): + """Sets the item_count of this Pagination. - Count of items, optionally filtered by startswith + Count of items, optionally filtered by starts_with - :param itemcount: The itemcount of this Pagination - :type itemcount: int + :param item_count: The item_count of this Pagination + :type item_count: int """ - self._itemcount = itemcount \ No newline at end of file + self._item_count = item_count \ No newline at end of file diff --git a/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py b/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py index a4c8623..78d7d43 100644 --- a/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py +++ b/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py @@ -17,23 +17,23 @@ def geneslist() -> list[str]: # Obtain parameters. page = request.args.get('page') - genesperpage = request.args.get('genesperpage') - startswith = request.args.get('startswith') - if startswith is None: - startswith = '' + genes_per_page = request.args.get('genes_per_page') + starts_with = request.args.get('starts_with') + if starts_with is None: + starts_with = '' - # Validate and set defaults for genesperpage. - if genesperpage is None: - genesperpage = '10' - if not genesperpage.isnumeric(): - return make_response(f'The value for parameter genesperpage ({genesperpage}) must be numeric.', 400) - if int(genesperpage) <= 0: - return (make_response(f'The value for parameter genesperpage ({genesperpage}) must be greater than zero.', 400)) + # Validate and set defaults for genes_per_page. + if genes_per_page is None: + genes_per_page = '10' + if not genes_per_page.isnumeric(): + return make_response(f'The value for parameter genes_per_page ({genes_per_page}) must be numeric.', 400) + if int(genes_per_page) <= 0: + return (make_response(f'The value for parameter genes_per_page ({genes_per_page}) must be greater than zero.', 400)) # Obtain the total count of genes, considering the filter starts_with. - genecount = genelist_count_get_logic(neo4j_instance, startswith) - if genecount == 0: - return make_response(f'There are no genes with HGNC symbols that start with \'{startswith}\'.', 404) + gene_count = genelist_count_get_logic(neo4j_instance, starts_with) + if gene_count == 0: + return make_response(f'There are no genes with HGNC symbols that start with \'{starts_with}\'.', 404) # Default values for page. # Case: No parameter specified. @@ -45,12 +45,12 @@ def geneslist() -> list[str]: page = '1' # Calculate the total number of (filtered) pages. - totalpages = str(math.ceil(int(genecount) / int(genesperpage))) + total_pages = str(math.ceil(int(gene_count) / int(genes_per_page))) # Translation for cases "last" or "first" - print(f'total_pages={totalpages}') + print(f'total_pages={total_pages}') if page == 'last': - page = str(int(totalpages)) + page = str(int(total_pages)) if page == 'first': page = '1' @@ -60,9 +60,10 @@ def geneslist() -> list[str]: if int(page) < 0: return make_response(f'The value for parameter page ({page}) must be >= 0', 400) - if int(page) > int(totalpages): - page = str(int(totalpages)) + if int(page) > int(total_pages): + page = str(int(total_pages)) # Obtain results. neo4j_instance = current_app.neo4jConnectionHelper.instance() - return jsonify(genelist_get_logic(neo4j_instance, page=page, totalpages=totalpages, genesperpage=genesperpage, startswith=startswith, genecount=genecount)) \ No newline at end of file + return jsonify(genelist_get_logic(neo4j_instance, page=page, total_pages=total_pages, genes_per_page=genes_per_page, + starts_with=starts_with, gene_count=gene_count)) \ No newline at end of file diff --git a/src/hs_ontology_api/utils/neo4j_logic.py b/src/hs_ontology_api/utils/neo4j_logic.py index 296c7b5..9fc7794 100644 --- a/src/hs_ontology_api/utils/neo4j_logic.py +++ b/src/hs_ontology_api/utils/neo4j_logic.py @@ -680,7 +680,7 @@ def genedetail_get_logic(neo4j_instance, gene_id: str) -> List[GeneDetail]: return genedetails -def genelist_get_logic(neo4j_instance, page:str, totalpages:str, genesperpage:str, startswith:str, genecount:str) -> List[GeneList]: +def genelist_get_logic(neo4j_instance, page:str, total_pages:str, genes_per_page:str, starts_with:str, gene_count:str) -> List[GeneList]: """ Returns information on HGNC genes. @@ -689,10 +689,10 @@ def genelist_get_logic(neo4j_instance, page:str, totalpages:str, genesperpage:st :param neo4j_instance: neo4j client :param page: Zero-based number of pages with rows=pagesize to skip in neo4j query - :param totalpages: Calculated number of pages of genes - :param genesperpage: number of rows to limit in neo4j query - :param startswith: string for type-ahead (starts with) searches - :param genecount: Calculated total count of genes, optionally filtered with startswith + :param total_pages: Calculated number of pages of genes + :param genes_per_page: number of rows to limit in neo4j query + :param starts_with: string for type-ahead (starts with) searches + :param gene_count: Calculated total count of genes, optionally filtered with starts_with :return: List[GeneList] """ @@ -714,14 +714,14 @@ def genelist_get_logic(neo4j_instance, page:str, totalpages:str, genesperpage:st # Convert to 1-based. intpage = int(page)-1 - skiprows = intpage * int(genesperpage) + skiprows = intpage * int(genes_per_page) starts_with_clause = '' - if startswith != '': - starts_with_clause = f'AND map[\'approved_symbol\'][0] STARTS WITH \'{startswith}\'' + if starts_with != '': + starts_with_clause = f'AND map[\'approved_symbol\'][0] STARTS WITH \'{starts_with}\'' query = query.replace('$starts_with_clause',starts_with_clause) query = query.replace('$skiprows', str(skiprows)) - query = query.replace('$limitrows', str(genesperpage)) + query = query.replace('$limitrows', str(genes_per_page)) with neo4j_instance.driver.session() as session: # Execute Cypher query. @@ -737,16 +737,16 @@ def genelist_get_logic(neo4j_instance, page:str, totalpages:str, genesperpage:st except KeyError: pass # Use the list of gene details with the page to build a genelist object. - genelist: GeneList = GeneList(page, totalpages, genesperpage, genes, startswith, genecount).serialize() + genelist: GeneList = GeneList(page, total_pages, genes_per_page, genes, starts_with, gene_count).serialize() return genelist -def genelist_count_get_logic(neo4j_instance, startswith: str) -> int: +def genelist_count_get_logic(neo4j_instance, starts_with: str) -> int: """ Returns the count of HGNC genes in the UBKG. - If startswith is non-null, returns the count of HGNC genes with approved symbol + If starts_with is non-null, returns the count of HGNC genes with approved symbol that starts with the parameter value. :param neo4j_instance: neo4j client - :param startswith: filtering string for STARTS WITH queries + :param starts_with: filtering string for STARTS WITH queries :return: integer count """ # @@ -755,8 +755,8 @@ def genelist_count_get_logic(neo4j_instance, startswith: str) -> int: queryfile = 'geneslist_count.cypher' query = loadquerystring(queryfile) starts_with_clause = '' - if startswith != '': - starts_with_clause = f'AND tGene.name STARTS WITH \'{startswith}\'' + if starts_with != '': + starts_with_clause = f'AND tGene.name STARTS WITH \'{starts_with}\'' query = query.replace('$starts_with_clause', starts_with_clause) with neo4j_instance.driver.session() as session: @@ -765,8 +765,8 @@ def genelist_count_get_logic(neo4j_instance, startswith: str) -> int: for record in recds: try: - genecount = record.get('genelistcount') + gene_count = record.get('genelistcount') except KeyError: pass - return genecount + return gene_count diff --git a/test_api.sh b/test_api.sh index e7ab62c..e24c28c 100755 --- a/test_api.sh +++ b/test_api.sh @@ -118,18 +118,18 @@ echo # Test for gene_list endpoint echo "genes-info GET" curl --request GET \ - --url "${UBKG_URL}/genes-info?page=1&genesperpage=3" \ + --url "${UBKG_URL}/genes-info?page=1&genes_per_page=3" \ --header "Content-Type: application/json" echo echo "genes-info GET last page" curl --request GET \ - --url "${UBKG_URL}/genes-info?page=last&genesperpage=3" \ + --url "${UBKG_URL}/genes-info?page=last&genes_per_page=3" \ --header "Content-Type: application/json" echo echo "genes-info GET starts_with B" curl --request GET \ - --url "${UBKG_URL}/genes-info?genesperpage=3&starts_with=B" \ + --url "${UBKG_URL}/genes-info?genes_per_page=3&starts_with=B" \ --header "Content-Type: application/json" echo From 7618426ffe540ea0d35c6e2f44eef6f84817813d Mon Sep 17 00:00:00 2001 From: yuanzhou Date: Wed, 8 Nov 2023 15:48:51 -0500 Subject: [PATCH 10/11] Correct version of yaml --- hs-ontology-api-spec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hs-ontology-api-spec.yaml b/hs-ontology-api-spec.yaml index 30f7b93..1c22a0b 100644 --- a/hs-ontology-api-spec.yaml +++ b/hs-ontology-api-spec.yaml @@ -2,7 +2,7 @@ openapi: 3.0.3 info: title: HubMAP/SenNet Ontology API (hs-ontology-api) description: The HuBMAP/SenNet Ontology API contains endpoints for querying a [UBKG](https://ubkg.docs.xconsortia.org/) instance with content from the [HuBMAP/SenNet context](https://ubkg.docs.xconsortia.org/contexts/#hubmapsennet-context). The hs-ontology-api imports the [ubkg-api](https://smart-api.info/ui/96e5b5c0b0efeef5b93ea98ac2794837), which encapsulates both basic connectivity to a UBKG instance and generic endpoint code. - version: 1.3.1 + version: 1.4.4 contact: name: GitHub repository url: https://github.com/x-atlas-consortia/hs-ontology-api From ad1d466640a15c16b35c15bdf95f588b82a5170c Mon Sep 17 00:00:00 2001 From: AlanSimmons Date: Wed, 8 Nov 2023 15:25:11 -0600 Subject: [PATCH 11/11] Changed underscores to dashes in proposed endpoints: field-descriptions field-types field-entities field-assays field-schemas Also changed underscores to dashes in route summary fields to reflect the names of the legacy YAML files. --- hs-ontology-api-spec.yaml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/hs-ontology-api-spec.yaml b/hs-ontology-api-spec.yaml index 1c22a0b..808b002 100644 --- a/hs-ontology-api-spec.yaml +++ b/hs-ontology-api-spec.yaml @@ -551,10 +551,10 @@ paths: description: No information for cell type with identifier *id*. '5XX': description: Unexpected error - /field_descriptions: + /field-descriptions: get: operationId: field_descriptions_get - summary: Return descriptions for ingest metadata fields. Replacement for field_descriptions.yaml. + summary: Return descriptions for ingest metadata fields. Replacement for field-descriptions.yaml. responses: '200': description: Ingest metadata fields and descriptions @@ -566,10 +566,10 @@ paths: $ref: '#/components/schemas/FieldDescriptionsResponse' '5XX': description: Unexpected error - /field_types: + /field-types: get: operationId: field_types_get - summary: Return data types for ingest metadata fields. Replacement for field_types.yaml. + summary: Return data types for ingest metadata fields. Replacement for field-types.yaml. responses: '200': description: Ingest metadata fields mapped to XSD data types. @@ -581,10 +581,10 @@ paths: $ref: '#/components/schemas/FieldTypesResponse' '5XX': description: Unexpected error - /field_entities: + /field-entities: get: operationId: field_entities_get - summary: Return associations between ingest metadata fields and the provenance entities of an application. Replacement for field_types.yaml. + summary: Return associations between ingest metadata fields and the provenance entities of an application. Replacement for field-types.yaml. parameters: - name: application_context in: query @@ -613,10 +613,10 @@ paths: description: missing application context '5XX': description: Unexpected error - /field_assays: + /field-assays: get: operationId: field_assays_get - summary: Return associations between ingest metadata fields and the "assays" (dataset data types) for an application. Replacement for field_assays.yaml. + summary: Return associations between ingest metadata fields and the "assays" (dataset data types) for an application. Replacement for field-assays.yaml. parameters: - name: application_context in: query @@ -645,10 +645,10 @@ paths: description: missing application context '5XX': description: Unexpected error - /field_schemas: + /field-schemas: get: operationId: field_schemas_get - summary: Return associations between ingest metadata fields and metadata schemas for an application. Replacement for field_schemas.yaml. + summary: Return associations between ingest metadata fields and metadata schemas for an application. Replacement for field-schemas.yaml. parameters: - name: application_context in: query @@ -1218,7 +1218,7 @@ components: example: heart FieldDescriptionsResponse: type: object - description: Descriptions ingest metadata fields from legacy field_descriptions.yaml + description: Descriptions ingest metadata fields from legacy field-descriptions.yaml properties: fields: type: array @@ -1240,7 +1240,7 @@ components: example: Units of x resolution distance between laser ablation shots. FieldTypesResponse: type: object - description: Associations between metadata field and data type ingest metadata fields from legacy field_types.yaml + description: Associations between metadata field and data type ingest metadata fields from legacy field-types.yaml properties: fields: type: array @@ -1269,7 +1269,7 @@ components: example: string FieldEntitiesResponse: type: object - description: Associations between fields and provenance entities for ingest metadata fields from legacy field_types.yaml + description: Associations between fields and provenance entities for ingest metadata fields from legacy field-types.yaml properties: fields: type: array @@ -1301,7 +1301,7 @@ components: example: dataset FieldAssaysResponse: type: object - description: Associations between fields and "assays" (dataset types) for ingest metadata fields from legacy field_assays.yaml + description: Associations between fields and "assays" (dataset types) for ingest metadata fields from legacy field-assays.yaml properties: fields: type: array @@ -1347,7 +1347,7 @@ components: example: [Imaging Mass Cytometry,2D-IMC,IMC] FieldSchemasResponse: type: object - description: Associations between fields and metadata schemas for ingest metadata fields from legacy field_schemas.yaml + description: Associations between fields and metadata schemas for ingest metadata fields from legacy field-schemas.yaml properties: fields: type: array