Skip to content

Commit 4a67afc

Browse files
committed
Add Filter and Remove OPTION methods and Models Section from Swagger UI
1 parent 5fca6dc commit 4a67afc

File tree

4 files changed

+94
-72
lines changed

4 files changed

+94
-72
lines changed

bio-api/bioapi.py

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@
33
import json
44
import gzip
55
import logging
6+
7+
from apispec import APISpec
8+
from apispec.ext.marshmallow import MarshmallowPlugin
9+
from apispec_webframeworks.flask import FlaskPlugin
10+
from flask_apispec import FlaskApiSpec, doc, use_kwargs
11+
from flask_swagger_ui import get_swaggerui_blueprint
612
from db import get_mongo_connection
713
from concurrent.futures import ThreadPoolExecutor
814
import configparser
9-
import urllib.parse
1015
from typing import List, Dict, Optional, Any
1116
from flask import Flask, jsonify, make_response, abort, render_template, request
1217
from utils import map_gene
1318
from gprofiler import GProfiler
19+
from schemas import swagger_schemas
20+
1421

1522
# Gets production flag
1623
IS_DEBUG: bool = os.environ.get('DEBUG', 'true') == 'true'
@@ -515,9 +522,6 @@ def bfs_on_terms(term_id, relations: Optional[List[str]] = None, general_depth=0
515522
return list(graph.values())
516523

517524

518-
# PharmGKB
519-
520-
521525
def cancer_drugs_related_to_gene(gene: str) -> List:
522526
"""
523527
Gets all cancer related drugs associated with a gene .
@@ -527,6 +531,7 @@ def cancer_drugs_related_to_gene(gene: str) -> List:
527531
collection_pharm = mydb["pharmgkb"]
528532
return list(collection_pharm.find({"genes": gene}, {"_id": 0}))
529533

534+
530535
def get_data_from_oncokb(genes: List[str], query: str) -> Dict[str, Dict[str, Any]]:
531536
"""
532537
Gets all data from OncoKB database associated with a gene list.
@@ -643,33 +648,52 @@ def associated_string_genes(gene_symbol: str, min_combined_score: int = 400) ->
643648
return res
644649

645650

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-
]
651+
# Create an APISpec
652+
spec = APISpec(
653+
title="BioAPI",
654+
version=VERSION,
655+
openapi_version="2.0.1",
656+
info=dict(description="A powerful abstraction of genomics databases."),
657+
plugins=[FlaskPlugin(), MarshmallowPlugin()]
658+
)
662659

663660

664661
def create_app():
665662
# Creates and configures the app
666663
flask_app = Flask(__name__, instance_relative_config=True)
667664

665+
# URL for exposing Swagger UI
666+
SWAGGER_URL = '/api/docs'
667+
# Spec API url or path
668+
API_URL = '/static/apispec.json'
669+
670+
# Call factory function to create our blueprint
671+
swaggerui_blueprint = get_swaggerui_blueprint(SWAGGER_URL, API_URL, config={
672+
'operationsSorter': 'alpha',
673+
'tagsSorter': 'alpha',
674+
"defaultModelsExpandDepth": -1, # Oculta la sección "Models"
675+
'filter': True # Permite usar un campo de búsqueda para filtrar métodos en Swagger UI
676+
})
677+
flask_app.register_blueprint(swaggerui_blueprint)
678+
679+
flask_app.config.update({'APISPEC_SPEC': spec, 'APISPEC_SWAGGER_UI_URL': SWAGGER_URL})
680+
681+
docs = FlaskApiSpec(flask_app)
682+
668683
# Endpoints
684+
@flask_app.route(API_URL)
685+
def swagger_json():
686+
""" Path to get OpenAPI Spec in ${API_URL}"""
687+
schema = app.config['APISPEC_SPEC'].to_dict()
688+
689+
for path, methods in schema.get("paths", {}).items():
690+
methods.pop("options", None)
691+
692+
return jsonify(schema)
693+
669694
@flask_app.route("/")
670695
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)
696+
return render_template('homepage.html', version=VERSION)
673697

674698
@flask_app.route("/ping")
675699
def ping_ok():
@@ -678,16 +702,20 @@ def ping_ok():
678702
return make_response(output, 200, headers)
679703

680704
@flask_app.route("/gene-symbols", methods=['POST'])
681-
def gene_symbols():
705+
@doc(description='Gene symbols validator', tags=['Genes'], consumes=["application/json"])
706+
@use_kwargs(args=swagger_schemas.GeneSymbolsRequestSchema, location="json")
707+
def gene_symbols(gene_ids):
682708
"""Receives a list of gene IDs in any standard and returns the standardized corresponding gene IDs.
683709
In case it is not found it returns an empty list for the specific not found gene."""
684710
response = {}
685711
if request.method == 'POST':
686-
body = request.get_json() # type: ignore
712+
if not request.is_json:
713+
abort(400, "NO ES JSON!")
714+
body = request.get_json()
687715
if "gene_ids" not in body:
688716
abort(400, "gene_ids is mandatory")
689717

690-
gene_ids = body['gene_ids']
718+
# gene_ids = body['gene_ids']
691719
if not isinstance(gene_ids, list):
692720
abort(400, "gene_ids must be a list")
693721

@@ -700,7 +728,9 @@ def gene_symbols():
700728
return make_response(response, 200, headers)
701729

702730
@flask_app.route("/gene-symbols-finder/", methods=['GET'])
703-
def gene_symbol_finder():
731+
@doc(description='Gene symbols finder', tags=['Genes'])
732+
@use_kwargs(args=swagger_schemas.GeneSymbolsFinderRequestSchema, location="query")
733+
def gene_symbol_finder(query: str, limit: int|None):
704734
"""Takes a string of any length and returns a list of genes that contain that search criteria."""
705735
if "query" not in request.args:
706736
abort(400, "'query' parameter is mandatory")
@@ -1045,6 +1075,10 @@ def bad_request(e):
10451075
def not_found(e):
10461076
return jsonify(error=str(e)), 404
10471077

1078+
with flask_app.test_request_context():
1079+
docs.register(target=gene_symbols)
1080+
docs.register(target=gene_symbol_finder)
1081+
10481082
return flask_app
10491083

10501084

bio-api/schemas/swagger_schemas.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from marshmallow import Schema, fields
2+
3+
4+
class GeneSymbolsRequestSchema(Schema):
5+
gene_ids = fields.List(fields.String(), required=True, example=["FANCS", "BRCC1"])
6+
7+
8+
class GeneSymbolsFinderRequestSchema(Schema):
9+
query = fields.String(
10+
required=True,
11+
description="gene search string",
12+
example="TP"
13+
)
14+
limit = fields.Integer(
15+
missing=50,
16+
description="number of elements returned by the service. Default 50."
17+
)

bio-api/templates/homepage.html

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,19 @@
1-
<!-- templates/index.html -->
21
<!DOCTYPE html>
3-
<html lang="es">
2+
<html lang="en" dir="ltr">
43
<head>
5-
<meta charset="UTF-8">
6-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7-
<title>BioAPI v{{ version }}</title>
8-
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
4+
<meta charset="utf-8">
5+
<title>BioAPI {{version}}</title>
6+
97
<style>
108
body {
11-
background-color: #f8f9fa;
12-
font-family: Arial, sans-serif;
13-
padding-top: 50px;
14-
padding: 10px 15px;
15-
}
16-
.container {
17-
max-width: 600px;
18-
}
19-
h1 {
20-
color: #343a40;
21-
margin-bottom: 20px;
22-
}
23-
h2 {
24-
color: #6c757d;
25-
margin-top: 30px;
26-
}
27-
.service-list .service-item {
28-
padding: 5px 5px;
29-
border-bottom: 1px solid #dee2e6;
30-
text-align: left; /* Alinea el contenido a la izquierda */
31-
}
32-
.service-item span {
33-
display: block; /* Coloca el nombre y el endpoint en líneas separadas */
34-
}
35-
.service-item code {
36-
color: #6c757d;
37-
font-size: 0.9em;
9+
background-color: #f5f5f5;
10+
font-family: Arial, Helvetica, sans-serif;
3811
}
3912
</style>
4013
</head>
4114
<body>
42-
<h1>BioAPI v{{ version }}</h1>
43-
<h2 class="mb-4">Services included:</h2>
44-
<ul class="list-group service-list">
45-
{% for service in services %}
46-
<li class="service-item">
47-
<span>{{ service.name }}</span>
48-
<code>{{ service.url }}</code>
49-
</li>
50-
{% endfor %}
51-
</ul>
52-
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
15+
<h1>BioAPI {{ version }}</h1>
16+
<h2>A powerful abstraction of genomics databases</h2>
17+
<p><a href="/api/docs/">Documentation</a></p>
5318
</body>
5419
</html>

config/bioapi_conf/requirements.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
pymongo==4.6.1
22
Flask==3.0.0
3-
flask-cors==4.0.0
3+
flask-cors==5.0.0
44
gunicorn==21.2.0
55
ConfigParser==6.0.0
66
pytest==7.1.2
77
tqdm==4.66.1
88
gprofiler-official==1.0.0
9-
Werkzeug==3.0.1
9+
Werkzeug==3.0.1
10+
flask_swagger_ui==4.11.1
11+
apispec==6.7.1
12+
flask-apispec==0.11.4
13+
apispec-webframeworks==1.2.0
14+
marshmallow==3.23.1
15+
setuptools==75.5.0

0 commit comments

Comments
 (0)