-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from bento-platform/search_basic
feat: search basic
- Loading branch information
Showing
6 changed files
with
220 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,65 @@ | ||
from pydantic import BaseModel | ||
from pydantic import BaseModel, Field, validator, field_validator | ||
from typing import List, Optional | ||
from enum import Enum | ||
|
||
__all__ = [ | ||
"ExperimentResult", | ||
"GeneExpression", | ||
"GeneExpressionData", | ||
"PaginationMeta", | ||
"GeneExpressionResponse", | ||
"MethodEnum", | ||
"ExpressionQueryBody", | ||
] | ||
|
||
|
||
class MethodEnum(str, Enum): | ||
raw = "raw" | ||
tpm = "tpm" | ||
tmm = "tmm" | ||
getmm = "getmm" | ||
|
||
|
||
class PaginatedRequest(BaseModel): | ||
page: int = Field(1, ge=1, description="Current page number") | ||
page_size: int = Field(100, ge=1, le=1000, description="Number of records per page") | ||
|
||
|
||
class PaginatedResponse(PaginatedRequest): | ||
total_records: int = Field(..., ge=0, description="Total number of records") | ||
total_pages: int = Field(..., ge=1, description="Total number of pages") | ||
|
||
|
||
class ExperimentResult(BaseModel): | ||
experiment_result_id: str | ||
assembly_id: str | None = None | ||
assembly_name: str | None = None | ||
experiment_result_id: str = Field(..., min_length=1, max_length=255) | ||
assembly_id: Optional[str] = Field(None, max_length=255) | ||
assembly_name: Optional[str] = Field(None, max_length=255) | ||
|
||
|
||
class GeneExpression(BaseModel): | ||
gene_code: str | ||
sample_id: str | ||
experiment_result_id: str | ||
gene_code: str = Field(..., min_length=1, max_length=255) | ||
sample_id: str = Field(..., min_length=1, max_length=255) | ||
experiment_result_id: str = Field(..., min_length=1, max_length=255) | ||
raw_count: int | ||
tpm_count: float | None = None | ||
tmm_count: float | None = None | ||
getmm_count: float | None = None | ||
tpm_count: Optional[float] = None | ||
tmm_count: Optional[float] = None | ||
getmm_count: Optional[float] = None | ||
|
||
|
||
class GeneExpressionData(BaseModel): | ||
gene_code: str = Field(..., min_length=1, max_length=255, description="Gene code") | ||
sample_id: str = Field(..., min_length=1, max_length=255, description="Sample ID") | ||
experiment_result_id: str = Field(..., min_length=1, max_length=255, description="Experiment result ID") | ||
count: float = Field(..., description="Expression count") | ||
|
||
|
||
class ExpressionQueryBody(PaginatedRequest): | ||
genes: Optional[List[str]] = Field(None, description="List of gene codes to retrieve") | ||
experiments: Optional[List[str]] = Field(None, description="List of experiment result IDs to retrieve data from") | ||
sample_ids: Optional[List[str]] = Field(None, description="List of sample IDs to retrieve data from") | ||
method: MethodEnum = Field(MethodEnum.raw, description="Data method to retrieve: 'raw', 'tpm', 'tmm', 'getmm'") | ||
|
||
|
||
class GeneExpressionResponse(PaginatedResponse): | ||
query: ExpressionQueryBody = Field(..., description="The query that produced this response") | ||
expressions: List[GeneExpressionData] = Field(..., description="List of gene expressions") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,89 @@ | ||
from fastapi import APIRouter, status | ||
from fastapi import APIRouter, HTTPException, status, Query | ||
|
||
from transcriptomics_data_service.db import DatabaseDependency | ||
from transcriptomics_data_service.logger import LoggerDependency | ||
from transcriptomics_data_service.models import ( | ||
GeneExpressionData, | ||
GeneExpressionResponse, | ||
ExpressionQueryBody, | ||
) | ||
|
||
__all__ = ["expression_router"] | ||
expressions_router = APIRouter(prefix="/expressions") | ||
|
||
expression_router = APIRouter(prefix="/expressions") | ||
|
||
async def get_expressions_handler( | ||
query_body: ExpressionQueryBody, | ||
db: DatabaseDependency, | ||
logger: LoggerDependency, | ||
): | ||
""" | ||
Handler for fetching and returning gene expression data. | ||
""" | ||
logger.info(f"Received query parameters: {query_body}") | ||
|
||
@expression_router.get("", status_code=status.HTTP_200_OK) | ||
async def expressions_list(db: DatabaseDependency): | ||
return await db.fetch_expressions() | ||
expressions, total_records = await db.fetch_gene_expressions( | ||
genes=query_body.genes, | ||
experiments=query_body.experiments, | ||
sample_ids=query_body.sample_ids, | ||
method=query_body.method.value, | ||
page=query_body.page, | ||
page_size=query_body.page_size, | ||
) | ||
|
||
if not expressions: | ||
raise HTTPException( | ||
status_code=status.HTTP_404_NOT_FOUND, | ||
detail="No gene expression data found for the given parameters.", | ||
) | ||
|
||
method = query_body.method.value | ||
method_count_field = f"{method}_count" | ||
response_data = [ | ||
GeneExpressionData( | ||
gene_code=expr.gene_code, | ||
sample_id=expr.sample_id, | ||
experiment_result_id=expr.experiment_result_id, | ||
count=getattr(expr, method_count_field), | ||
) | ||
for expr in expressions | ||
] | ||
|
||
total_pages = (total_records + query_body.page_size - 1) // query_body.page_size | ||
|
||
return GeneExpressionResponse( | ||
# pagination | ||
page=query_body.page, | ||
page_size=query_body.page_size, | ||
total_records=total_records, | ||
total_pages=total_pages, | ||
# data | ||
expressions=response_data, | ||
query=query_body, | ||
) | ||
|
||
|
||
@expressions_router.post( | ||
"", | ||
status_code=status.HTTP_200_OK, | ||
response_model=GeneExpressionResponse, | ||
) | ||
async def get_expressions_post( | ||
params: ExpressionQueryBody, | ||
db: DatabaseDependency, | ||
logger: LoggerDependency, | ||
): | ||
""" | ||
Retrieve gene expression data via POST request. | ||
Using POST instead of GET in order to add a body of type ExpressionQueryBody | ||
Example JSON body: | ||
{ | ||
"genes": ["gene1", "gene2"], | ||
"experiments": ["exp1"], | ||
"sample_ids": ["sample1"], | ||
"method": "tmm", | ||
"page": 1, | ||
"page_size": 100 | ||
} | ||
""" | ||
return await get_expressions_handler(params, db, logger) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters