Skip to content
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

Experiment info retrieval #17

Open
wants to merge 5 commits into
base: paralell_normalization
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions transcriptomics_data_service/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,102 @@ async def delete_experiment_result(self, exp_id: str):
await self._execute(*("DELETE FROM experiment_results WHERE experiment_result_id = $1", exp_id))
self.logger.info(f"Deleted experiment_result row {exp_id}")

############################
# fetch experiment_results
############################

async def fetch_experiment_results(
self,
paginate: bool = False,
page: int = 1,
page_size: int = 100,
) -> Tuple[List[ExperimentResult], int]:

order_clause = " ORDER BY experiment_result_id"
base_query = "SELECT * FROM experiment_results"
count_query = "SELECT COUNT(*) FROM experiment_results"

async with self.connect() as conn:
total_records = await conn.fetchval(count_query)
query = base_query + order_clause
params = []
if paginate:
offset = (page - 1) * page_size
query += " LIMIT $1 OFFSET $2"
params = [page_size, offset]

rows = await conn.fetch(query, *params)

items = [
ExperimentResult(
experiment_result_id=r["experiment_result_id"],
assembly_id=r["assembly_id"],
assembly_name=r["assembly_name"],
)
for r in rows
]
return items, total_records

async def fetch_experiment_samples(
self, experiment_result_id: str, paginate: bool = True, page: int = 1, page_size: int = 100
) -> Tuple[List[str], int]:
"""
Returns (list_of_sample_ids, total_records) for a single experiment_result_id.
"""
count_query = """
SELECT COUNT(DISTINCT sample_id)
FROM gene_expressions
WHERE experiment_result_id = $1
"""
base_query = """
SELECT DISTINCT sample_id
FROM gene_expressions
WHERE experiment_result_id = $1
ORDER BY sample_id
"""

async with self.connect() as conn:
total_records = await conn.fetchval(count_query, experiment_result_id)
if paginate:
offset = (page - 1) * page_size
query = base_query + " LIMIT $2 OFFSET $3"
rows = await conn.fetch(query, experiment_result_id, page_size, offset)
else:
rows = await conn.fetch(base_query, experiment_result_id)

items = [r["sample_id"] for r in rows]
return items, total_records

async def fetch_experiment_features(
self, experiment_result_id: str, paginate: bool = True, page: int = 1, page_size: int = 100
) -> Tuple[List[str], int]:
"""
Returns (list_of_features, total_records) for a single experiment_result_id.
"""
count_query = """
SELECT COUNT(DISTINCT gene_code)
FROM gene_expressions
WHERE experiment_result_id = $1
"""
base_query = """
SELECT DISTINCT gene_code
FROM gene_expressions
WHERE experiment_result_id = $1
ORDER BY gene_code
"""

async with self.connect() as conn:
total_records = await conn.fetchval(count_query, experiment_result_id)
if paginate:
offset = (page - 1) * page_size
query = base_query + " LIMIT $2 OFFSET $3"
rows = await conn.fetch(query, experiment_result_id, page_size, offset)
else:
rows = await conn.fetch(base_query, experiment_result_id)

items = [r["gene_code"] for r in rows]
return items, total_records

########################
# CRUD: gene_expressions
########################
Expand Down
22 changes: 22 additions & 0 deletions transcriptomics_data_service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
"GeneExpressionResponse",
"NormalizationMethodEnum",
"ExpressionQueryBody",
"CountTypesEnum",
"PaginatedRequest",
"PaginatedResponse",
"FeaturesResponse",
"SamplesResponse",
]


Expand All @@ -32,6 +37,9 @@ class CountTypesEnum(str, Enum):
getmm = GETMM


#####################################
# PAGINATION MODELS
#####################################
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")
Expand All @@ -42,12 +50,26 @@ class PaginatedResponse(PaginatedRequest):
total_pages: int = Field(..., ge=1, description="Total number of pages")


#####################################
# EXPERIMENTS
#####################################
class ExperimentResult(BaseModel):
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 SamplesResponse(PaginatedResponse):
samples: List[str]


class FeaturesResponse(PaginatedResponse):
features: List[str]


#####################################
# GENE EXPRESSIONS
#####################################
class GeneExpression(BaseModel):
gene_code: str = Field(..., min_length=1, max_length=255, description="Feature identifier")
sample_id: str = Field(..., min_length=1, max_length=255, description="Sample identifier")
Expand Down
114 changes: 110 additions & 4 deletions transcriptomics_data_service/routers/experiment_results.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,123 @@
from fastapi import APIRouter
from fastapi import APIRouter, HTTPException, status

from transcriptomics_data_service.db import DatabaseDependency
from transcriptomics_data_service.logger import LoggerDependency
from transcriptomics_data_service.models import (
PaginatedRequest,
SamplesResponse,
FeaturesResponse,
)

__all__ = ["experiment_router"]

experiment_router = APIRouter(prefix="/experiment")


@experiment_router.delete("/{experiment_result_id}")
async def delete_experiment_result(db: DatabaseDependency, experiment_result_id: str):
await db.delete_experiment_result(experiment_result_id)
async def get_experiment_samples_handler(
experiment_result_id: str,
params: PaginatedRequest,
db: DatabaseDependency,
logger: LoggerDependency,
) -> SamplesResponse:
"""
Handler for fetching and returning samples for a experiment_result_id.
"""
logger.info(f"Received query parameters for samples: {params}")

samples, total_records = await db.fetch_experiment_samples(
experiment_result_id=experiment_result_id,
paginate=True,
page=params.page,
page_size=params.page_size,
)

if not samples:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No samples found for experiment '{experiment_result_id}'.",
)

total_pages = (total_records + params.page_size - 1) // params.page_size

return SamplesResponse(
page=params.page,
page_size=params.page_size,
total_records=total_records,
total_pages=total_pages,
samples=samples,
)


async def get_experiment_features_handler(
experiment_result_id: str,
params: PaginatedRequest,
db: DatabaseDependency,
logger: LoggerDependency,
) -> FeaturesResponse:
"""
Handler for fetching and returning features for a experiment_result_id.
"""
logger.info(f"Received query parameters for features: {params}")

features, total_records = await db.fetch_experiment_features(
experiment_result_id=experiment_result_id,
paginate=True,
page=params.page,
page_size=params.page_size,
)

if not features:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"No features found for experiment '{experiment_result_id}'.",
)

total_pages = (total_records + params.page_size - 1) // params.page_size

return FeaturesResponse(
page=params.page,
page_size=params.page_size,
total_records=total_records,
total_pages=total_pages,
features=features,
)


@experiment_router.get("")
async def get_all_experiments(db: DatabaseDependency):
experiments, _ = await db.fetch_experiment_results(paginate=False)
return experiments


@experiment_router.get("/{experiment_result_id}")
async def get_experiment_result(db: DatabaseDependency, experiment_result_id: str):
return await db.read_experiment_result(experiment_result_id)


@experiment_router.post(
"/{experiment_result_id}/samples", status_code=status.HTTP_200_OK, response_model=SamplesResponse
)
async def post_experiment_samples(
experiment_result_id: str,
params: PaginatedRequest,
db: DatabaseDependency,
logger: LoggerDependency,
):
return await get_experiment_samples_handler(experiment_result_id, params, db, logger)


@experiment_router.post(
"/{experiment_result_id}/features", status_code=status.HTTP_200_OK, response_model=FeaturesResponse
)
async def post_experiment_features(
experiment_result_id: str,
params: PaginatedRequest,
db: DatabaseDependency,
logger: LoggerDependency,
):
return await get_experiment_features_handler(experiment_result_id, params, db, logger)


@experiment_router.delete("/{experiment_result_id}")
async def delete_experiment_result(db: DatabaseDependency, experiment_result_id: str):
await db.delete_experiment_result(experiment_result_id)
Loading