-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Search basic #15
base: paralell_normalization
Are you sure you want to change the base?
Search basic #15
Changes from 5 commits
e3c407b
7453f10
0f41887
4e062bf
3b9c026
0a45a5e
d7b3cfd
dea2fe4
3b01289
afe2e0d
44d6dee
58aaa33
594cb47
e02e1f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,82 @@ | ||
from pydantic import BaseModel | ||
from pydantic import BaseModel, Field, validator | ||
from typing import List, Optional | ||
from enum import Enum | ||
|
||
__all__ = [ | ||
"ExperimentResult", | ||
"GeneExpression", | ||
"GeneExpressionData", | ||
"PaginationMeta", | ||
"GeneExpressionResponse", | ||
"MethodEnum", | ||
"QueryParameters", | ||
] | ||
|
||
|
||
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") | ||
method: str = Field(..., description="Method used to calculate the expression count") | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
class PaginationMeta(BaseModel): | ||
total_records: int = Field(..., ge=0, description="Total number of records") | ||
page: int = Field(..., ge=1, description="Current page number") | ||
page_size: int = Field(..., ge=1, le=1000, description="Number of records per page") | ||
total_pages: int = Field(..., ge=1, description="Total number of pages") | ||
|
||
|
||
class GeneExpressionResponse(BaseModel): | ||
expressions: List[GeneExpressionData] | ||
pagination: PaginationMeta | ||
|
||
|
||
class MethodEnum(str, Enum): | ||
raw = "raw" | ||
tpm = "tpm" | ||
tmm = "tmm" | ||
getmm = "getmm" | ||
|
||
|
||
class QueryParameters(BaseModel): | ||
genes: Optional[List[str]] = Field(None, description="List of gene codes to retrieve") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename to |
||
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'") | ||
page: int = Field( | ||
1, | ||
ge=1, | ||
description="Page number for pagination (must be greater than or equal to 1)", | ||
) | ||
page_size: int = Field( | ||
100, | ||
ge=1, | ||
le=1000, | ||
description="Number of records per page (between 1 and 1000)", | ||
) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to have this bundled in a model. Something like this: class PaginatedRequest(BaseModel):
page: int = Field(..., ge=1, description="Current page number")
page_size: int = Field(..., ge=1, le=1000, description="Number of records per page")
# extends PaginatedRequest
class PaginatedRespone(PaginatedRequest):
total_records: int = Field(..., ge=0, description="Total number of records")
total_pages: int = Field(..., ge=1, description="Total number of pages")
class SomePaginatedQuery(PaginatedRequest):
...
class SomePaginatedResponse(PaginatedRespone):
... This way we reuse the same model for requests or responses, and we have less code duplication! |
||
@validator("genes", "experiments", "sample_ids", each_item=True) | ||
def validate_identifiers(cls, value): | ||
if not (1 <= len(value) <= 255): | ||
raise ValueError("Each identifier must be between 1 and 255 characters long.") | ||
if not value.replace("_", "").isalnum(): | ||
raise ValueError("Identifiers must contain only alphanumeric characters and underscores.") | ||
return value |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be in the expression router instead, with no There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be in the expressions router, since it is has the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
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, | ||
PaginationMeta, | ||
MethodEnum, | ||
QueryParameters, | ||
) | ||
|
||
query_router = APIRouter(prefix="/query") | ||
|
||
|
||
async def get_expressions_handler( | ||
params: QueryParameters, | ||
db: DatabaseDependency, | ||
logger: LoggerDependency, | ||
): | ||
""" | ||
Handler for fetching and returning gene expression data. | ||
""" | ||
logger.info(f"Received query parameters: {params}") | ||
|
||
expressions, total_records = await db.fetch_gene_expressions( | ||
genes=params.genes, | ||
experiments=params.experiments, | ||
sample_ids=params.sample_ids, | ||
method=params.method.value, | ||
page=params.page, | ||
page_size=params.page_size, | ||
) | ||
|
||
if not expressions: | ||
raise HTTPException( | ||
status_code=status.HTTP_404_NOT_FOUND, | ||
detail="No gene expression data found for the given parameters.", | ||
) | ||
|
||
response_data = [] | ||
method_field = f"{params.method.value}_count" if params.method != MethodEnum.raw else "raw_count" | ||
for expr in expressions: | ||
count = getattr(expr, method_field) | ||
response_item = GeneExpressionData( | ||
gene_code=expr.gene_code, | ||
sample_id=expr.sample_id, | ||
experiment_result_id=expr.experiment_result_id, | ||
count=count, | ||
method=method_field, | ||
) | ||
response_data.append(response_item) | ||
|
||
total_pages = (total_records + params.page_size - 1) // params.page_size | ||
pagination_meta = PaginationMeta( | ||
total_records=total_records, | ||
page=params.page, | ||
page_size=params.page_size, | ||
total_pages=total_pages, | ||
) | ||
|
||
return GeneExpressionResponse(expressions=response_data, pagination=pagination_meta) | ||
|
||
|
||
@query_router.get( | ||
"/expressions_all", | ||
noctillion marked this conversation as resolved.
Show resolved
Hide resolved
|
||
status_code=status.HTTP_200_OK, | ||
response_model=GeneExpressionResponse, | ||
) | ||
async def get_expressions_all( | ||
db: DatabaseDependency, | ||
logger: LoggerDependency, | ||
method: MethodEnum = Query( | ||
MethodEnum.raw, | ||
description="Data method to retrieve: 'raw', 'tpm', 'tmm', 'getmm'", | ||
), | ||
page: int = Query( | ||
1, | ||
ge=1, | ||
description="Page number for pagination (must be greater than or equal to 1)", | ||
), | ||
page_size: int = Query( | ||
100, | ||
ge=1, | ||
le=1000, | ||
description="Number of records per page (between 1 and 1000)", | ||
), | ||
): | ||
""" | ||
Retrieve all gene expression data via GET request with pagination. | ||
""" | ||
params = QueryParameters( | ||
genes=None, | ||
experiments=None, | ||
sample_ids=None, | ||
method=method, | ||
page=page, | ||
page_size=page_size, | ||
) | ||
|
||
return await get_expressions_handler(params, db, logger) | ||
|
||
|
||
@query_router.post( | ||
"/expressions", | ||
status_code=status.HTTP_200_OK, | ||
response_model=GeneExpressionResponse, | ||
) | ||
async def get_expressions_post( | ||
params: QueryParameters, | ||
db: DatabaseDependency, | ||
logger: LoggerDependency, | ||
): | ||
""" | ||
Retrieve gene expression data via POST request. | ||
|
||
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was there a specific reason to only include one count?