diff --git a/hs-ontology-api-spec.yaml b/hs-ontology-api-spec.yaml index b3e6b86..808b002 100644 --- a/hs-ontology-api-spec.yaml +++ b/hs-ontology-api-spec.yaml @@ -1,8 +1,8 @@ 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. - version: 1.3.1 + 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.4.4 contact: name: GitHub repository url: https://github.com/x-atlas-consortia/hs-ontology-api @@ -352,18 +352,18 @@ paths: $ref: '#/components/schemas/SabCodeTerm' /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 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. @@ -387,14 +387,14 @@ 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 *starts_with* parameter. '5XX': description: Unexpected error /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 @@ -419,18 +419,18 @@ paths: description: Unexpected error /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 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. @@ -454,14 +454,14 @@ 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 *starts_with* parameter. '5XX': description: Unexpected error /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 @@ -486,18 +486,18 @@ paths: description: Unexpected error /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 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. @@ -521,14 +521,14 @@ 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 *starts_with* parameter. '5XX': description: Unexpected error /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 @@ -554,7 +554,7 @@ paths: /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 @@ -569,7 +569,7 @@ paths: /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. @@ -584,7 +584,7 @@ paths: /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 @@ -616,7 +616,7 @@ paths: /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 @@ -648,7 +648,7 @@ paths: /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 @@ -850,32 +850,8 @@ components: - SenNet GeneListResponse: type: object - description: High-level information on a set of genes requested by the genes-info endpoint. + description: High-level information on a set of genes requested by the genes endpoint. properties: - pagination: - type: object - description: pagination information related to the request - properties: - items_per_page: - type: number - description: value of genesperpage parameter from the endpoint - example: 3 - page: - type: number - description: value of page parameter from 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 - item_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 - starts_with: - type: string - description: value of starts_with from the endpoint - example: M genes: type: array description: gene information @@ -894,9 +870,33 @@ components: type: string description: RefSeq summary for the gene example: Multimerin is a massive, soluble protein found in.... + pagination: + type: object + description: pagination statistics + properties: + items_per_page: + type: number + description: original value of genes_per_page request parameter + example: 3 + page: + type: number + description: original value of page request parameter + example: 1 + total_pages: + type: number + description: calculated total number of "pages" (blocks) based on the values of items_per_page and the total number of items + example: 14381 + starts_with: + type: string + description: original value of starts_with request parameter + example: M + item_count: + type: number + 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 - description: Detailed information on a gene requested by the genes endpoint + description: Detailed information on a gene requested by the gene endpoint properties: alias_names: type: array @@ -1000,32 +1000,8 @@ components: 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-info endpoint. + description: High-level list of proteins with pagination details, requested by the proteins endpoint. properties: - pagination: - type: object - description: pagination information related to the request - properties: - items_per_page: - type: number - description: value of proteinsperpage parameter from the endpoint - example: 3 - page: - type: number - description: value of page parameter from the 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 - item_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 - starts_with: - type: string - description: value of starts_with from the endpoint - example: M proteins: type: array description: protein information @@ -1044,6 +1020,30 @@ components: type: string description: UniProtKB Recommended Name example: multimerin-1 + pagination: + type: object + description: pagination statistics + properties: + items_per_page: + type: number + description: original value of proteins_per_page request parameter + example: 3 + page: + type: number + description: original value of page request parameter + example: 1 + total_pages: + type: number + description: calculated total number of "pages" (blocks) based on the values of proteins_per_page and the total number of proteins + example: 14381 + starts_with: + type: string + description: original value of starts_with request parameter + example: M + item_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 @@ -1098,50 +1098,50 @@ components: example: [Homo sapiens] CellTypeListResponse: type: object - description: High-level list of cell types with pagination details, requested by the celltypes-info endpoint. + 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… pagination: type: object - description: pagination information related to the request + description: pagination statistics properties: items_per_page: type: number - description: value of celltypesperpage parameter from the endpoint + description: original value of celltypes_per_page request parameter example: 3 page: type: number - description: value of page parameter from the endpoint + description: original value of page request parameter example: 1 total_pages: type: number - description: total number of "pages" 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 starts_with: type: string - description: value of starts_with from the endpoint + description: original value of starts_with request parameter example: E item_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. + 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 - celltypes: - 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… CellTypeDetailResponse: type: object description: Detailed information on a cell type requested by the celltype endpoint @@ -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 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..62399ec 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, total_pages=None, genes_per_page=None, genes=None, starts_with=None, gene_count=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) + :param total_pages: Calculated total number of "pages" (blocks of genes) :type total_pages: str - :param genes: List of genes in page + :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 + :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: Count of (filtered) genes + :gene_count: Calculated count of genes that satisfied the search criteria :type gene_count: str - """ - # Parameters other than page will be used to build nested GeneListDetail objects. + # 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. # 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, total_pages, genes_per_page, starts_with, gene_count).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..9bee031 --- /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, 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 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, + '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', + '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._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, + "total_pages": self._total_pages, + "genes_per_page": self._items_per_page, + "starts_with": self._starts_with, + "item_count": self._item_count + } + + @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, + "total_pages": self._total_pages, + "items_per_page": self._items_per_page, + "starts_with": self._starts_with, + "item_count": self._item_count + } + + @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 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._total_pages + + @total_pages.setter + def total_pages(self, total_pages): + """Sets the total_pages of this Pagination. + + Total number of "pages" (blocks of items) + + :param total_pages: The number of itmes in this Pagination + :type genes: int + """ + + self._total_pages = total_pages + + @property + def items_per_page(self): + """Gets the items_per_page of this Pagination. + + Number of items per "page" or block of returns + :return: The items_per_page of this Pagination. + :rtype: int + """ + return self._items_per_page + + @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 items_per_page: The items_per_page of this Pagination + :type items_per_page: int + """ + + self._items_per_page = items_per_page + + @property + def starts_with(self): + """Gets the starts_with of this Pagination. + + Optional type-ahead search string + :return: The starts_with of this Pagination. + :rtype: str + """ + return self._starts_with + + @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 starts_with of this Pagination + :type starts_with: int + """ + + self._starts_with = starts_with + + @property + def item_count(self): + """Gets the item_count of this Pagination. + + Count of items, optionally filtered by starts_with. + :return: The item_count of this Pagination. + :rtype: str + """ + return self._item_count + + @item_count.setter + def item_count(self, item_count): + """Sets the item_count of this Pagination. + + Count of items, optionally filtered by starts_with + + :param item_count: The item_count of this Pagination + :type item_count: int + """ + + self._item_count = item_count \ 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/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 59% rename from src/hs_ontology_api/routes/genedetail/genedetail_controller.py rename to src/hs_ontology_api/routes/genes/genes_controller.py index d96947f..2955f6a 100644 --- a/src/hs_ontology_api/routes/genedetail/genedetail_controller.py +++ b/src/hs_ontology_api/routes/genes/genes_controller.py @@ -1,18 +1,13 @@ # 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 -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 67% rename from src/hs_ontology_api/routes/geneslist/geneslist_controller.py rename to src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py index f1a40e8..78d7d43 100644 --- a/src/hs_ontology_api/routes/geneslist/geneslist_controller.py +++ b/src/hs_ontology_api/routes/genesinfo/genesinfo_controller.py @@ -1,12 +1,12 @@ # 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 -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() @@ -17,18 +17,18 @@ def geneslist() -> list[str]: # Obtain parameters. page = request.args.get('page') - genesperpage = request.args.get('genesperpage') + 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. gene_count = genelist_count_get_logic(neo4j_instance, starts_with) @@ -45,7 +45,7 @@ def geneslist() -> list[str]: page = '1' # Calculate the total number of (filtered) pages. - total_pages = str(math.ceil(int(gene_count) / int(genesperpage))) + total_pages = str(math.ceil(int(gene_count) / int(genes_per_page))) # Translation for cases "last" or "first" print(f'total_pages={total_pages}') @@ -65,4 +65,5 @@ def geneslist() -> list[str]: # 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, 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/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 3eba1d4..9fc7794 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'], @@ -628,16 +644,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. @@ -663,9 +673,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: @@ -673,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, total_pages:str, genes_per_page:str, starts_with:str, gene_count:str) -> List[GeneList]: """ Returns information on HGNC genes. @@ -681,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 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] - :starts_with: string for type-ahead (starts with) searches - :return: str - :gene_count: filtered count of genes - :return: str """ @@ -708,14 +714,14 @@ def genelist_get_logic(neo4j_instance, page:str, total_pages:str, genesperpage:s # Convert to 1-based. intpage = int(page)-1 - skiprows = intpage * int(genesperpage) + skiprows = intpage * int(genes_per_page) starts_with_clause = '' 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. @@ -731,7 +737,7 @@ 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, total_pages, genes_per_page, genes, starts_with, gene_count).serialize() return genelist def genelist_count_get_logic(neo4j_instance, starts_with: str) -> int: @@ -759,8 +765,8 @@ def genelist_count_get_logic(neo4j_instance, starts_with: 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/src/main.py b/src/main.py index d48a00a..100d470 100755 --- a/src/main.py +++ b/src/main.py @@ -10,11 +10,11 @@ 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 +from hs_ontology_api.utils.cellsclient import OntologyCellsClient def make_flask_config(): temp_flask_app = Flask(__name__, @@ -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') diff --git a/test_api.sh b/test_api.sh index e7e51b0..e24c28c 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&genes_per_page=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&genes_per_page=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?genes_per_page=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