Skip to content

Commit 1dcdb89

Browse files
authored
Merge pull request #14 from omics-datascience/swagger
Swagger
2 parents 5fca6dc + 264d0d3 commit 1dcdb89

17 files changed

+444
-79
lines changed

DEPLOYING.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ Below are the steps to perform a production deployment of BioAPI.
3232
3333
BioAPI uses three genomic databases for its operation. These databases must be loaded in MongoDB. You can import all the databases in two ways:
3434
35-
3635
### Import using public DB backup (recommended)
3736
3837
To import all databases in MongoDB:
@@ -67,7 +66,6 @@ To import all databases in MongoDB:
6766
4. Stop services with the command `docker compose -f docker-compose.dev.yml down`
6867
5. Roll up the changes in the `docker-compose.dev.yml` file to remove the backup file from the `volumes` section. Restart all the services again.
6968
70-
7169
### Manually import the different databases
7270
7371
Alternatively (but **not recommended** due to high computational demands) you can run a separate ETL process to download from source, process and import the databases into MongoDB.
@@ -101,6 +99,7 @@ docker-compose up -d
10199
```
102100
103101
By default, BioAPI runs on `localhost:8000`.
102+
Test BioAPI with Swagger on `localhost:8000/apidocs`
104103
105104
If you want to stop all services, you can execute:
106105

bio-api/bioapi.py

+50-29
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
import json
44
import gzip
55
import logging
6+
7+
from flask import Flask, jsonify, make_response, abort, render_template, request
8+
from flasgger import Swagger, swag_from
69
from db import get_mongo_connection
710
from concurrent.futures import ThreadPoolExecutor
811
import configparser
9-
import urllib.parse
1012
from typing import List, Dict, Optional, Any
11-
from flask import Flask, jsonify, make_response, abort, render_template, request
1213
from utils import map_gene
1314
from gprofiler import GProfiler
1415

16+
1517
# Gets production flag
1618
IS_DEBUG: bool = os.environ.get('DEBUG', 'true') == 'true'
1719

@@ -515,9 +517,6 @@ def bfs_on_terms(term_id, relations: Optional[List[str]] = None, general_depth=0
515517
return list(graph.values())
516518

517519

518-
# PharmGKB
519-
520-
521520
def cancer_drugs_related_to_gene(gene: str) -> List:
522521
"""
523522
Gets all cancer related drugs associated with a gene .
@@ -527,6 +526,7 @@ def cancer_drugs_related_to_gene(gene: str) -> List:
527526
collection_pharm = mydb["pharmgkb"]
528527
return list(collection_pharm.find({"genes": gene}, {"_id": 0}))
529528

529+
530530
def get_data_from_oncokb(genes: List[str], query: str) -> Dict[str, Dict[str, Any]]:
531531
"""
532532
Gets all data from OncoKB database associated with a gene list.
@@ -643,33 +643,41 @@ def associated_string_genes(gene_symbol: str, min_combined_score: int = 400) ->
643643
return res
644644

645645

646-
# Documentation of included services
647-
services = [
648-
{"name": "Genes symbols validator", "url": "[POST] /gene-symbols"},
649-
{"name": "Genes symbols finder", "url": "[GET] /gene-symbols-finder"},
650-
{"name": "Genes information", "url": "[POST] /information-of-genes"},
651-
{"name": "Gene Groups", "url": "[GET] /genes-of-its-group/<gene_id>"},
652-
{"name": "Genes of a metabolic pathway", "url": "[GET] /pathway-genes/<source>/<external_id>"},
653-
{"name": "Metabolic pathways from different genes", "url": "[POST] /pathways-in-common"},
654-
{"name": "Gene expression", "url": "[POST] /expression-of-genes"},
655-
{"name": "Therapies and actionable genes in cancer", "url": "[POST] /information-of-oncokb"},
656-
{"name": "Gene Ontology terms related to a list of genes", "url": "[POST] /genes-to-terms"},
657-
{"name": "Gene Ontology terms related to another specific term", "url": "[POST] /related-terms"},
658-
{"name": "Cancer related drugs", "url": "[POST] /drugs-pharm-gkb"},
659-
{"name": "Predicted functional associations network", "url": "[POST] /string-relations"},
660-
{"name": "Drugs that regulate a gene", "url": "[GET] /drugs-regulating-gene/<gene_id>"}
661-
]
662-
663-
664646
def create_app():
665647
# Creates and configures the app
666648
flask_app = Flask(__name__, instance_relative_config=True)
667649

650+
swagger_config = {
651+
"headers": [
652+
],
653+
"openapi": "3.0.0",
654+
"specs": [
655+
{
656+
"endpoint": "swagger",
657+
"route": "/apispec.json",
658+
"rule_filter": lambda rule: True,
659+
"model_filter": lambda tag: True
660+
}
661+
],
662+
"title": "BioAPI",
663+
"uiversion": 3,
664+
"version": VERSION,
665+
"termsOfService": False,
666+
"swagger_ui": True,
667+
"static_url_path": "/",
668+
"specs_route": "/apidocs/",
669+
"description": """
670+
## A powerful abstraction of genomics databases.
671+
BioAPI is part of the Multiomix project. For more information, visit our [website](https://omicsdatascience.org/).
672+
To contribute: [OmicsDatascience](https://github.com/omics-datascience/BioAPI)"""
673+
}
674+
675+
Swagger(flask_app, config=swagger_config)
676+
668677
# Endpoints
669678
@flask_app.route("/")
670679
def homepage():
671-
# return render_template('index.html', title=f"API v{VERSION}", services=services)
672-
return render_template('homepage.html', version=VERSION, services=services)
680+
return render_template('homepage.html', version=VERSION)
673681

674682
@flask_app.route("/ping")
675683
def ping_ok():
@@ -678,12 +686,13 @@ def ping_ok():
678686
return make_response(output, 200, headers)
679687

680688
@flask_app.route("/gene-symbols", methods=['POST'])
689+
@swag_from("swagger_specs/geneSymbols.yml")
681690
def gene_symbols():
682691
"""Receives a list of gene IDs in any standard and returns the standardized corresponding gene IDs.
683692
In case it is not found it returns an empty list for the specific not found gene."""
684693
response = {}
685694
if request.method == 'POST':
686-
body = request.get_json() # type: ignore
695+
body = request.get_json()
687696
if "gene_ids" not in body:
688697
abort(400, "gene_ids is mandatory")
689698

@@ -700,16 +709,17 @@ def gene_symbols():
700709
return make_response(response, 200, headers)
701710

702711
@flask_app.route("/gene-symbols-finder/", methods=['GET'])
712+
@swag_from("swagger_specs/geneSymbolFinder.yml")
703713
def gene_symbol_finder():
704714
"""Takes a string of any length and returns a list of genes that contain that search criteria."""
705715
if "query" not in request.args:
706716
abort(400, "'query' parameter is mandatory")
707717
else:
708-
query = request.args.get('query') # type: ignore
718+
query = request.args.get('query')
709719

710720
limit = 50
711721
if "limit" in request.args:
712-
limit_arg = request.args.get('limit') # type: ignore
722+
limit_arg = request.args.get('limit')
713723
if limit_arg.isnumeric():
714724
limit = int(limit_arg)
715725
else:
@@ -722,6 +732,7 @@ def gene_symbol_finder():
722732
abort(400, e)
723733

724734
@flask_app.route("/information-of-genes", methods=['POST'])
735+
@swag_from("swagger_specs/informationOfGenes.yml")
725736
def information_of_genes():
726737
"""Receives a list of gene IDs and returns information about them."""
727738
body = request.get_json() # type: ignore
@@ -739,7 +750,8 @@ def information_of_genes():
739750
return make_response(response, 200, headers)
740751

741752
@flask_app.route("/genes-of-its-group/<gene_id>", methods=['GET'])
742-
def genes_in_the_same_group(gene_id):
753+
@swag_from("swagger_specs/genesOfItsGroup.yml")
754+
def genes_in_the_same_group(gene_id: str):
743755
response = {"gene_id": None, "groups": [],
744756
"locus_group": None, "locus_type": None}
745757
try:
@@ -774,6 +786,7 @@ def genes_in_the_same_group(gene_id):
774786
return make_response(response, 200, headers)
775787

776788
@flask_app.route("/pathway-genes/<pathway_source>/<pathway_id>", methods=['GET'])
789+
@swag_from("swagger_specs/genesOfMetabolicPathway.yml")
777790
def pathway_genes(pathway_source, pathway_id):
778791
if pathway_source.lower() not in PATHWAYS_SOURCES:
779792
abort(404, f'{pathway_source} is an invalid pathway source')
@@ -782,6 +795,7 @@ def pathway_genes(pathway_source, pathway_id):
782795
return make_response(response, 200, headers)
783796

784797
@flask_app.route("/pathways-in-common", methods=['POST'])
798+
@swag_from("swagger_specs/pathwaysInCommon.yml")
785799
def pathways_in_common():
786800
body = request.get_json() # type: ignore
787801
if "gene_ids" not in body:
@@ -802,6 +816,7 @@ def pathways_in_common():
802816
return make_response(response, 200, headers)
803817

804818
@flask_app.route("/expression-of-genes", methods=['POST'])
819+
@swag_from("swagger_specs/expressionOfGenes.yml")
805820
def expression_data_from_gtex():
806821
body = request.get_json() # type: ignore
807822

@@ -839,6 +854,7 @@ def expression_data_from_gtex():
839854
return jsonify(expression_data)
840855

841856
@flask_app.route("/genes-to-terms", methods=['POST'])
857+
@swag_from("swagger_specs/genesToTerms.yml")
842858
def genes_to_go_terms():
843859
"""Receives a list of genes and returns the related terms"""
844860
valid_filter_types = ["union", "intersection", "enrichment"]
@@ -926,6 +942,7 @@ def genes_to_go_terms():
926942
return jsonify(response)
927943

928944
@flask_app.route("/related-terms", methods=['POST'])
945+
@swag_from("swagger_specs/relatedTerms.yml")
929946
def related_terms():
930947
"""Receives a term and returns the related terms"""
931948
valid_ontology_types = ["biological_process",
@@ -973,6 +990,7 @@ def related_terms():
973990
return jsonify(response)
974991

975992
@flask_app.route("/information-of-oncokb", methods=['POST'])
993+
@swag_from("swagger_specs/informationOfOncokb.yml")
976994
def oncokb_data():
977995
body = request.get_json() # type: ignore
978996

@@ -993,6 +1011,7 @@ def oncokb_data():
9931011
return jsonify(data)
9941012

9951013
@flask_app.route("/drugs-pharm-gkb", methods=['POST'])
1014+
@swag_from("swagger_specs/cancerDrugsRelatedToGenes.yml")
9961015
def cancer_drugs_related_to_genes():
9971016
"""Receives genes and returns the related drugs"""
9981017
response = {}
@@ -1007,6 +1026,7 @@ def cancer_drugs_related_to_genes():
10071026
return jsonify(response)
10081027

10091028
@flask_app.route("/string-relations", methods=['POST'])
1029+
@swag_from("swagger_specs/stringRelations.yml")
10101030
def string_relations_to_gene():
10111031
body = request.get_json()
10121032
optionals = {}
@@ -1024,6 +1044,7 @@ def string_relations_to_gene():
10241044
return jsonify(res)
10251045

10261046
@flask_app.route("/drugs-regulating-gene/<gene_id>", methods=['GET'])
1047+
@swag_from("swagger_specs/drugsRegulatingGene.yml")
10271048
def drugs_regulating_gene(gene_id):
10281049
return {
10291050
"link": "https://go.drugbank.com/pharmaco/transcriptomics?q%5Bg%5B0%5D%5D%5Bm%5D=or&q%5Bg%5B0%5D%5D"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
tags:
2+
- Accionable Genes and Drugs
3+
summary: "Cancer related drugs"
4+
description: "Gets a list of drugs from the PharmGKB database related to a list of genes."
5+
operationId: "Cancer related drugs"
6+
requestBody:
7+
required: true
8+
content:
9+
application/json:
10+
schema:
11+
type: object
12+
properties:
13+
gene_ids:
14+
type: array
15+
description: "List of gene identifiers."
16+
items:
17+
type: string
18+
example: ["JAK2", "EGFR"]
19+
responses:
20+
200:
21+
description: "List of all information related to drugs and genes."
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
tags:
2+
- Accionable Genes and Drugs
3+
summary: "Drugs that regulate a gene expression."
4+
description: "Service that takes gene symbol and returns a link to https://go.drugbank.com with all the drugs that upregulate and down regulate its expresion. Useful for embeding."
5+
operationId: "Drugs that regulate genetic expression."
6+
parameters:
7+
- in: path
8+
name: gene_id
9+
description: "Identifier of the gene."
10+
required: true
11+
schema:
12+
type: string
13+
example: "TP53"
14+
responses:
15+
200:
16+
description: "URL that points to the information on the DrugBank website."
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
tags:
2+
- Gene Expression
3+
summary: "Gene expression"
4+
description: "Gets gene expression in healthy tissue."
5+
operationId: "Gene expression in healthy tissues"
6+
requestBody:
7+
required: true
8+
content:
9+
application/json:
10+
schema:
11+
type: object
12+
properties:
13+
gene_ids:
14+
type: array
15+
description: "List of gene identifiers."
16+
items:
17+
type: string
18+
example: ["BRCA1", "BRCA2"]
19+
tissue:
20+
type: string
21+
enum:
22+
- Adipose Tissue
23+
- Adrenal Gland
24+
- Bladder
25+
- Blood
26+
- Blood Vessel
27+
- Brain
28+
- Breast
29+
- Cervix Uteri
30+
- Colon
31+
- Esophagus
32+
- Fallopian Tube
33+
- Heart
34+
- Kidney
35+
- Liver
36+
- Lung
37+
- Muscle
38+
- Nerve
39+
- Ovary
40+
- Pancreas
41+
- Pituitary
42+
- Prostate
43+
- Salivary Gland
44+
- Skin
45+
- Small Intestine
46+
- Spleen
47+
- Stomach
48+
- Testis
49+
- Thyroid
50+
- Uterus
51+
- Vagina
52+
example: "Skin"
53+
type:
54+
type: string
55+
enum:
56+
- json
57+
- gzip
58+
example: "gzip"
59+
responses:
60+
200:
61+
description: "Expression values ​​of each gene according to the GTEx database."
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
tags:
2+
- Gene Nomenclature
3+
summary: "Gene symbol finder"
4+
description: "Service that takes a string of any length and returns a list of genes that contain that search criteria."
5+
operationId: "Gene symbol finder"
6+
parameters:
7+
- in: query
8+
name: query
9+
description: "Gene search string."
10+
required: true
11+
schema:
12+
type: string
13+
example: "TP"
14+
- in: query
15+
name: limit
16+
description: "Limit the number of results returned (Default 50)."
17+
required: false
18+
schema:
19+
type: integer
20+
example: 10
21+
responses:
22+
200:
23+
description: "List of genes containing that search criterion in bioinformatics databases."

bio-api/swagger_specs/geneSymbols.yml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
tags:
2+
- Gene Nomenclature
3+
summary: "Gene symbol validator"
4+
description: "Searches the identifier of a list of genes of different genomics databases and returns the approved symbols according to HGNC nomenclature."
5+
operationId: "Gene symbol"
6+
requestBody:
7+
required: true
8+
content:
9+
application/json:
10+
schema:
11+
type: object
12+
properties:
13+
gene_ids:
14+
type: array
15+
description: "List of gene identifiers."
16+
items:
17+
type: string
18+
example: ["FANCS", "BRCC1"]
19+
responses:
20+
200:
21+
description: "HGNC approved gene symbols."

0 commit comments

Comments
 (0)