diff --git a/dataportal_api/dataportal/api.py b/dataportal_api/dataportal/api.py index 2975e51..c1ecc6d 100644 --- a/dataportal_api/dataportal/api.py +++ b/dataportal_api/dataportal/api.py @@ -156,11 +156,20 @@ async def gene_autocomplete_suggestions( # API Endpoint to search genes by query string @gene_router.get("/search", response=GenePaginationSchema) async def search_genes_by_string( - request, query: str, page: int = 1, per_page: int = 10 + request, + query: str, + page: int = 1, + per_page: int = 10, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", ): try: paginated_results = await gene_service.search_genes( - query=query, page=page, per_page=per_page + query=query, + page=page, + per_page=per_page, + sort_field=sort_field, + sort_order=sort_order, ) return paginated_results except Exception as e: @@ -176,17 +185,30 @@ async def get_gene_by_id(request, gene_id: int): # API Endpoint to retrieve all genes @gene_router.get("/", response=GenePaginationSchema) -async def get_all_genes(request, page: int = 1, per_page: int = 10): - return await gene_service.get_all_genes(page, per_page) +async def get_all_genes( + request, + page: int = 1, + per_page: int = 10, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", +): + return await gene_service.get_all_genes(page, per_page, sort_field, sort_order) # API Endpoint to retrieve genes filtered by a single genome ID @genome_router.get("/{genome_id}/genes", response=GenePaginationSchema) async def get_genes_by_genome( - request, genome_id: int, page: int = 1, per_page: int = 10 + request, + genome_id: int, + page: int = 1, + per_page: int = 10, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", ): try: - return await gene_service.get_genes_by_genome(genome_id, page, per_page) + return await gene_service.get_genes_by_genome( + genome_id, page, per_page, sort_field, sort_order + ) except Exception as e: logger.error(f"Error in get_genes_by_genome: {e}") raise HttpError(500, f"Internal Server Error: {str(e)}") @@ -195,10 +217,18 @@ async def get_genes_by_genome( # API Endpoint to search genes by genome ID and gene string @genome_router.get("/{genome_id}/genes/search", response=GenePaginationSchema) async def search_genes_by_genome_and_string( - request, genome_id: int, query: str, page: int = 1, per_page: int = 10 + request, + genome_id: int, + query: str, + page: int = 1, + per_page: int = 10, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", ): try: - return await gene_service.search_genes(query, genome_id, page, per_page) + return await gene_service.search_genes( + query, genome_id, page, per_page, sort_field, sort_order + ) except Exception as e: logger.error(f"Error in search_genes_by_genome_and_string: {e}") raise HttpError(500, f"Internal Server Error: {str(e)}") @@ -213,10 +243,12 @@ async def search_genes_by_multiple_genomes_and_species_and_string( query: str = "", page: int = 1, per_page: int = 10, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", ): try: return await gene_service.get_genes_by_multiple_genomes_and_string( - genome_ids, species_id, query, page, per_page + genome_ids, species_id, query, page, per_page, sort_field, sort_order ) except HttpError as e: raise e diff --git a/dataportal_api/dataportal/services/gene_service.py b/dataportal_api/dataportal/services/gene_service.py index a8aebf1..22a6e7d 100644 --- a/dataportal_api/dataportal/services/gene_service.py +++ b/dataportal_api/dataportal/services/gene_service.py @@ -61,21 +61,19 @@ async def get_gene_by_id(self, gene_id: int) -> GeneResponseSchema: raise HttpError(500, "Internal Server Error") async def get_all_genes( - self, page: int = 1, per_page: int = 10 + self, + page: int = 1, + per_page: int = 10, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", ) -> GenePaginationSchema: try: genes, total_results = await self._fetch_paginated_genes( - Q(), page, per_page + Q(), page, per_page, sort_field, sort_order ) serialized_genes = [self._serialize_gene(gene) for gene in genes] - - return GenePaginationSchema( - results=serialized_genes, - page_number=page, - num_pages=(total_results + per_page - 1) // per_page, - has_previous=page > 1, - has_next=(page * per_page) < total_results, - total_results=total_results, + return self._create_pagination_schema( + serialized_genes, page, per_page, total_results ) except Exception as e: logger.error(f"Error fetching all genes: {e}") @@ -87,7 +85,9 @@ async def search_genes( genome_id: Optional[int] = None, page: int = 1, per_page: int = 10, - ): + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", + ) -> GenePaginationSchema: try: filters = Q() if query: @@ -98,38 +98,31 @@ async def search_genes( filters &= Q(strain_id=genome_id) genes, total_results = await self._fetch_paginated_genes( - filters, page, per_page + filters, page, per_page, sort_field, sort_order ) serialized_genes = [self._serialize_gene(gene) for gene in genes] - - return GenePaginationSchema( - results=serialized_genes, - page_number=page, - num_pages=(total_results + per_page - 1) // per_page, - has_previous=page > 1, - has_next=(page * per_page) < total_results, - total_results=total_results, + return self._create_pagination_schema( + serialized_genes, page, per_page, total_results ) except Exception as e: logger.error(f"Error searching genes: {e}") raise HttpError(500, "Internal Server Error") async def get_genes_by_genome( - self, genome_id: int, page: int = 1, per_page: int = 10 - ): + self, + genome_id: int, + page: int = 1, + per_page: int = 10, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", + ) -> GenePaginationSchema: try: genes, total_results = await self._fetch_paginated_genes( - Q(strain_id=genome_id), page, per_page + Q(strain_id=genome_id), page, per_page, sort_field, sort_order ) serialized_genes = [self._serialize_gene(gene) for gene in genes] - - return GenePaginationSchema( - results=serialized_genes, - page_number=page, - num_pages=(total_results + per_page - 1) // per_page, - has_previous=page > 1, - has_next=(page * per_page) < total_results, - total_results=total_results, + return self._create_pagination_schema( + serialized_genes, page, per_page, total_results ) except Exception as e: logger.error(f"Error fetching genes for genome ID {genome_id}: {e}") @@ -158,6 +151,8 @@ async def get_genes_by_multiple_genomes_and_string( query: str = None, page: int = 1, per_page: int = 10, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", ) -> GenePaginationSchema: try: genome_id_list = ( @@ -175,17 +170,11 @@ async def get_genes_by_multiple_genomes_and_string( filters &= Q(gene_name__icontains=query.strip()) genes, total_results = await self._fetch_paginated_genes( - filters, page, per_page + filters, page, per_page, sort_field, sort_order ) serialized_genes = [self._serialize_gene(gene) for gene in genes] - - return GenePaginationSchema( - results=serialized_genes, - page_number=page, - num_pages=(total_results + per_page - 1) // per_page, - has_previous=page > 1, - has_next=(page * per_page) < total_results, - total_results=total_results, + return self._create_pagination_schema( + serialized_genes, page, per_page, total_results ) except ValueError: logger.error("Invalid genome ID provided") @@ -278,14 +267,22 @@ def _create_pagination_schema( ) async def _fetch_paginated_genes( - self, filter_criteria: Q, page: int, per_page: int + self, + filter_criteria: Q, + page: int, + per_page: int, + sort_field: Optional[str] = None, + sort_order: Optional[str] = "asc", ) -> Tuple[List[Gene], int]: start = (page - 1) * per_page + order_prefix = "-" if sort_order == "desc" else "" + sort_by = f"{order_prefix}{sort_field}" if sort_field else "gene_name" + genes = await sync_to_async( lambda: list( Gene.objects.select_related("strain") .filter(filter_criteria) - .order_by("gene_name")[start : start + per_page] + .order_by(sort_by)[start : start + per_page] ) )() total_results = await sync_to_async( diff --git a/k8s-hl/dev/mett-dataportal-api-deployment.yaml b/k8s-hl/dev/mett-dataportal-api-deployment.yaml index 36b6027..dad2526 100644 --- a/k8s-hl/dev/mett-dataportal-api-deployment.yaml +++ b/k8s-hl/dev/mett-dataportal-api-deployment.yaml @@ -19,7 +19,7 @@ spec: runAsUser: 7123 runAsGroup: 1347 containers: - - image: quay.io/microbiome-informatics/mett-dataportal-api:v0.0.27 + - image: quay.io/microbiome-informatics/mett-dataportal-api:v0.0.29 imagePullPolicy: Always name: mett-dataportal-api-dev env: diff --git a/k8s-hl/dev/mett-dataportal-app-deployment.yaml b/k8s-hl/dev/mett-dataportal-app-deployment.yaml index d939ad2..a5ebb83 100644 --- a/k8s-hl/dev/mett-dataportal-app-deployment.yaml +++ b/k8s-hl/dev/mett-dataportal-app-deployment.yaml @@ -16,7 +16,7 @@ spec: app: mett-dataportal-react-app-dev spec: containers: - - image: quay.io/microbiome-informatics/mett-dataportal-react-app:v0.0.27 + - image: quay.io/microbiome-informatics/mett-dataportal-react-app:v0.0.29 imagePullPolicy: Always name: mett-dataportal-react-app-dev env: