From 95c7ce143215a7730cc957cdd6c56e82f6cc2e01 Mon Sep 17 00:00:00 2001 From: Haresh Kainth Date: Wed, 16 Oct 2024 09:44:58 +0100 Subject: [PATCH] chore:enhance pagination handling for search results Refactor pagination to use Django Paginator for improved reliability. The context is now updated with pagination details and results are consistently aggregated across all pages for accurate representation. --- orp/orp_search/public_gateway.py | 43 +++++++++++++++++++------ orp/orp_search/views.py | 54 ++++++++++++++++++-------------- 2 files changed, 65 insertions(+), 32 deletions(-) diff --git a/orp/orp_search/public_gateway.py b/orp/orp_search/public_gateway.py index b8b4286..d1da0bb 100644 --- a/orp/orp_search/public_gateway.py +++ b/orp/orp_search/public_gateway.py @@ -1,5 +1,7 @@ import logging +from typing import Tuple + import pandas as pd import requests # type: ignore @@ -7,6 +9,8 @@ from orp_search.config import SearchDocumentConfig from orp_search.dummy_data import get_construction_data_as_dataframe +from django.core.paginator import InvalidPage, Paginator + logger = logging.getLogger(__name__) @@ -35,20 +39,40 @@ def _build_like_conditions(self, field, terms): """ return " OR ".join([f"{field} LIKE '%{term}%'" for term in terms]) - def paginate_results(self, config: SearchDocumentConfig, results): + def paginate_results( + self, config: SearchDocumentConfig, results, context + ) -> Tuple[dict, Paginator]: """ - Paginates the search results. + Paginates the given search results based on the provided configuration. - Args: - results (list): The search results to paginate. + Arguments: + config (SearchDocumentConfig): Configuration parameters for search, + including pagination limits. + results: A collection of search results to be paginated. + context: A dictionary containing context data which will be updated + with pagination details. Returns: - list: The paginated search results. - count: Total number of pages from results + A tuple containing: + - Updated context dictionary with pagination information. + - Paginator instance used for paginating the results. + + The context dictionary is updated with the following keys: + is_paginated: A boolean indicating if the results span multiple pages. + reports: The paginated results for the current page. + paginator: The Paginator instance. + page_obj: The current page of results. """ - start = (config.offset - 1) * config.limit - end = start + config.limit - return results[start:end] + paginator = Paginator(results, config.limit) + try: + paginated_reports = paginator.page(config.offset) + except InvalidPage: + paginated_reports = paginator.page(1) + context["is_paginated"] = paginator.num_pages > 1 + context["reports"] = paginated_reports + context["paginator"] = paginator + context["page_obj"] = paginated_reports + return context, paginator def calculate_total_pages(self, config, results_count): """ @@ -61,6 +85,7 @@ def calculate_total_pages(self, config, results_count): Returns: int: The total number of pages. """ + if config.limit <= 0: raise ValueError("limit must be greater than 0") return (results_count + config.limit - 1) // config.limit diff --git a/orp/orp_search/views.py b/orp/orp_search/views.py index 8b3d850..0d23782 100644 --- a/orp/orp_search/views.py +++ b/orp/orp_search/views.py @@ -174,32 +174,40 @@ def search(request: HttpRequest) -> HttpResponse: # on the landing page. if search_results: - paginated_search_results = public_gateway.paginate_results( - config, search_results - ) - logger.info( - "paginated search results after paginate: %s", - paginated_search_results, + context, paginator = public_gateway.paginate_results( + config, search_results, context ) + # Initialize a list to hold all results from all pages + paginated_search_results = [] + + # Iterate over each page + for page_num in paginator.page_range: + # Get the page + page = paginator.page(page_num) + # Add the objects of the current page to the aggregate list + + for result in page.object_list: + paginated_search_results.append( + { + "id": result["id"], + "title": result["title"], + "publisher": result["publisher"], + "description": ( + result["description"][:100] + "..." + if len(result["description"]) > 100 + else result["description"] + ), + "date_issued": result["date_issued"], + "date_modified": result["date_modified"], + "document_type": result["type"], + "regulatory_topics": result["regulatory_topics"].split( + "\n" + ), + } + ) + # We can use the following code to filter the search_results list: - paginated_search_results = [ - { - "id": result["id"], - "title": result["title"], - "publisher": result["publisher"], - "description": ( - result["description"][:100] + "..." - if len(result["description"]) > 100 - else result["description"] - ), - "date_issued": result["date_issued"], - "date_modified": result["date_modified"], - "document_type": result["type"], - "regulatory_topics": result["regulatory_topics"].split("\n"), - } - for result in paginated_search_results - ] context["results_count"] = len(paginated_search_results) else: paginated_search_results = []