Files
cellxgene/server/app/rest_api/rest.py
Charlotte Weaver 85506db971 Updating to latest cellxgene client code
Fixes the build error in the old version. 

Reverted the API version since we changed directions from updating the API to refactoring the server structure instead.
2018-07-05 13:37:26 -07:00

442 lines
18 KiB
Python

from flask import (
Blueprint, request
)
from flask_restful_swagger_2 import Api, swagger, Resource
from ..util.utils import make_payload
from ..util.filter import parse_filter
class InitializeAPI(Resource):
@swagger.doc({
"summary": "get metadata schema, ranges for values, and cell count to initialize cellxgene app",
"tags": ["initialize"],
"parameters": [],
"responses": {
"200": {
"description": "initialization data for UI",
"examples": {
"application/json": {
"data": {
"cellcount": 3589,
"options": {
"Sample.type": {
"options": {
"Glioblastoma": 3589
}
},
"Selection": {
"options": {
"Astrocytes(HEPACAM)": 714,
"Endothelial(BSC)": 123,
"Microglia(CD45)": 1108,
"Neurons(Thy1)": 685,
"Oligodendrocytes(GC)": 294,
"Unpanned": 665
}
},
"Splice_sites_AT.AC": {
"range": {
"max": 1025,
"min": 152
}
},
"Splice_sites_Annotated": {
"range": {
"max": 1075869,
"min": 26
}
}
},
"schema": {
"CellName": {
"displayname": "Name",
"type": "string",
"variabletype": "categorical"
},
"Class": {
"displayname": "Class",
"type": "string",
"variabletype": "categorical"
},
"ERCC_reads": {
"displayname": "ERCC Reads",
"type": "int",
"variabletype": "continuous"
},
"ERCC_to_non_ERCC": {
"displayname": "ERCC:Non-ERCC",
"type": "float",
"variabletype": "continuous"
},
"Genes_detected": {
"displayname": "Genes Detected",
"type": "int",
"variabletype": "continuous"
}
},
"genes": ["1/2-SBSRNA4", "A1BG", "A1BG-AS1"]
},
"status": {
"error": False,
"errormessage": ""
}
}
}
}
}
})
def get(self):
from app import data, REACTIVE_LIMIT
return make_payload({
"schema": data.schema,
"cellcount": data.cell_count,
"reactivelimit": REACTIVE_LIMIT,
"genes": data.genes(),
"ranges": data.metadata_ranges(),
})
class CellsAPI(Resource):
@swagger.doc({
"summary": "filter based on metadata fields to get a subset cells, expression data, and metadata",
"tags": ["cells"],
"description": "Cells takes query parameters defined in the schema retrieved from the /initialize enpoint. "
"<br>For categorical metadata keys filter based on `key=value` <br>"
" For continuous metadata keys filter by `key=min,max`<br> Either value "
"can be replaced by a \*. To have only a minimum value `key=min,\*` To have only a maximum "
"value `key=\*,max` <br>Graph data (if retrieved) is normalized"
" To only retrieve cells that don't have a value for the key filter by `key`",
"parameters": [],
"responses": {
"200": {
"description": "initialization data for UI",
"examples": {
"application/json": {
"data": {
"badmetadatacount": 0,
"cellcount": 0,
"cellids": ["..."],
"metadata": [
{
"CellName": "1001000173.G8",
"Class": "Neoplastic",
"Cluster_2d": "11",
"Cluster_2d_color": "#8C564B",
"Cluster_CNV": "1",
"Cluster_CNV_color": "#1F77B4",
"ERCC_reads": "152104",
"ERCC_to_non_ERCC": "0.562454470489481",
"Genes_detected": "1962",
"Location": "Tumor",
"Location.color": "#FF7F0E",
"Multimapping_reads_percent": "2.67",
"Neoplastic": "Neoplastic",
"Non_ERCC_reads": "270429",
"Sample.name": "BT_S2",
"Sample.name.color": "#AEC7E8",
"Sample.type": "Glioblastoma",
"Sample.type.color": "#1F77B4",
"Selection": "Unpanned",
"Selection.color": "#98DF8A",
"Splice_sites_AT.AC": "102",
"Splice_sites_Annotated": "122397",
"Splice_sites_GC.AG": "761",
"Splice_sites_GT.AG": "125741",
"Splice_sites_non_canonical": "56",
"Splice_sites_total": "126660",
"Total_reads": "1741039",
"Unique_reads": "1400382",
"Unique_reads_percent": "80.43",
"Unmapped_mismatch": "2.15",
"Unmapped_other": "0.18",
"Unmapped_short": "14.56",
"housekeeping_cluster": "2",
"housekeeping_cluster_color": "#AEC7E8",
"recluster_myeloid": "NA",
"recluster_myeloid_color": "NA"
},
],
"reactive": True,
"graph": [
[
"1001000173.G8",
0.93836,
0.28623
],
[
"1001000173.D4",
0.1662,
0.79438
]
],
"status": {
"error": False,
"errormessage": ""
}
},
}
},
},
"400": {
"description": "bad query params",
}
}
})
def get(self):
from app import data
payload = {
"cellids": [],
"metadata": [],
"cellcount": 0,
"graph": [],
"ranges": {},
}
# get query params
filter = parse_filter(request.args, data.schema)
filtered_data = data.filter_cells(filter)
payload["metadata"] = data.metadata(filtered_data)
payload["ranges"] = data.metadata_ranges(filtered_data)
payload["graph"] = data.create_graph(filtered_data)
payload["cellids"] = data.cellids(filtered_data)
payload["cellcount"] = len(payload["cellids"])
return make_payload(payload)
class ExpressionAPI(Resource):
@swagger.doc({
"summary": "Json with gene list and expression data by cell, limited to first 40 cells",
"tags": ["expression"],
"parameters": [
{
"name": "include_unexpressed_genes",
"description": "Include genes that have 0 expression across all cells in set",
"in": "path",
"type": "bool",
}
],
"responses": {
"200": {
"description": "Json for heatmap",
"examples": {
"application/json": {
"data": {
"cells": [
{
"cellname": "1/2-SBSRNA4",
"e": [0, 0, 214, 0, 0]
},
],
"genes": [
"1001000173.G8",
"1001000173.D4",
"1001000173.B4",
"1001000173.A2",
"1001000173.E2"
],
"nonzero_gene_count": 2857
},
"status": {
"error": False,
"errormessage": ""
}
}
}
}
}
})
def get(self):
from app import data
expression_data = data.expression()
return make_payload(expression_data)
@swagger.doc({
"summary": "Json with gene list and expression data by cell",
"tags": ["expression"],
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"example": {
"celllist": ["1001000173.G8", "1001000173.D4"],
"genelist": ["1/2-SBSRNA4", "A1BG", "A1BG-AS1", "A1CF", "A2LD1", "A2M", "A2ML1", "A2MP1",
"A4GALT"],
"include_unexpressed_genes": True,
}
}
},
],
"responses": {
"200": {
"description": "Json for expressiondata",
"examples": {
"application/json": {
"data": {
"cells": [
{
"cellname": "1001000173.D4",
"e": [0, 0]
},
{
"cellname": "1001000173.G8",
"e": [0, 0]
}
],
"genes": [
"ABCD4",
"ZWINT"
],
"nonzero_gene_count": 2857
},
"status": {
"error": False,
"errormessage": ""
}
}
}
},
"400": {
"description": "Required parameter missing/incorrect",
}
}
})
def post(self):
from app import data
args = request.get_json()
cell_list = args.get("celllist", [])
gene_list = args.get("genelist", [])
if not cell_list and not gene_list:
return make_payload([], "must include celllist and/or genelist parameter", 400)
expression_data = data.expression(cell_list, gene_list)
if cell_list and len(expression_data["cells"]) < len(cell_list):
return make_payload([], "Some cell ids not available", 400)
if gene_list and len(expression_data["genes"]) < len(gene_list):
return make_payload([], "Some genes not available", 400)
return make_payload(expression_data)
class DifferentialExpressionAPI(Resource):
@swagger.doc({
"summary": "Get the top expressed genes for two cell sets. Calculated using t-test",
"tags": ["expression"],
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"example": {
"celllist1": ["1001000176.C12", "1001000176.C7", "1001000177.F11"],
"celllist2": ["1001000012.D2", "1001000017.F10", "1001000033.C3", "1001000229.D4"],
"num_genes": 5,
"pval": 0.000001,
},
}
}
],
"responses": {
"200": {
"description": "top expressed genes for cellset1, cellset2",
"examples": {
"application/json": {
"data": {
"celllist1": {
"ave_diff": [
432.0132935431362,
12470.5623982637,
957.0246880086814
],
"mean_expression_cellset1": [
438.6185567010309,
13315.536082474227,
1076.5773195876288
],
"mean_expression_cellset2": [
6.605263157894737,
844.9736842105264,
119.55263157894737
],
"pval": [
3.8906598089944563e-35,
1.9086226376018916e-25,
7.847480544069826e-21
],
"topgenes": [
"TMSB10",
"FTL",
"TMSB4X"
]
},
"celllist2": {
"ave_diff": [
-6860.599158979924,
-519.1314432989691,
-10278.328269126423
],
"mean_expression_cellset1": [
2.8350515463917527,
0.6185567010309279,
23.09278350515464
],
"mean_expression_cellset2": [
6863.434210526316,
519.75,
10301.421052631578
],
"pval": [
4.662891833748732e-44,
3.6278087029927103e-37,
8.396825170618402e-35
],
"topgenes": [
"SPARCL1",
"C1orf61",
"CLU"
]
}
},
"status": {
"error": False,
"errormessage": ""
}
}
}
}
}
})
def post(self):
from app import data
args = request.get_json()
cell_list_1 = args.get("celllist1", [])
cell_list_2 = args.get("celllist2", [])
num_genes = args.get("num_genes", 7)
pval = args.get("pval", 0.5)
if not (cell_list_1 and cell_list_2):
return make_payload([],
"must include celllist1 and celllist2 parameters",
400)
data = data.diffexp(cell_list_1, cell_list_2, pval, num_genes)
return make_payload(data)
def get_api_resources():
bp = Blueprint("api", __name__, url_prefix="/api/v0.1")
api = Api(bp, add_api_spec_resource=False)
api.add_resource(InitializeAPI, "/initialize")
api.add_resource(CellsAPI, "/cells")
api.add_resource(ExpressionAPI, "/expression")
api.add_resource(DifferentialExpressionAPI, "/diffexp")
return api