From b87da1f862c1966dba0c60cb5368b86bb698104e Mon Sep 17 00:00:00 2001 From: SerenaFeng Date: Tue, 20 Jun 2017 17:40:38 +0800 Subject: support pagination in TestAPI In this patch, pagination is supported, so you can go through results leveraging: http://testresults.opnfv.org/test/api/v1/results?page=2 Change-Id: Ibe31c787643f27dbb06c4899e713b3c8e716e784 Signed-off-by: SerenaFeng --- .../components/results/resultsController.js | 4 ++-- testapi/etc/config.ini | 4 ++++ testapi/opnfv_testapi/common/config.py | 5 +++++ testapi/opnfv_testapi/resources/handlers.py | 26 ++++++++++++++++++---- testapi/opnfv_testapi/resources/result_handlers.py | 21 ++++++++++++----- testapi/opnfv_testapi/tests/unit/fake_pymongo.py | 25 ++++++++++++++++----- 6 files changed, 68 insertions(+), 17 deletions(-) diff --git a/testapi/3rd_party/static/testapi-ui/components/results/resultsController.js b/testapi/3rd_party/static/testapi-ui/components/results/resultsController.js index 93a549a..9e3540d 100644 --- a/testapi/3rd_party/static/testapi-ui/components/results/resultsController.js +++ b/testapi/3rd_party/static/testapi-ui/components/results/resultsController.js @@ -123,8 +123,8 @@ ctrl.resultsRequest = $http.get(content_url).success(function (data) { ctrl.data = data; - ctrl.totalItems = 20 // ctrl.data.pagination.total_pages * ctrl.itemsPerPage; - ctrl.currentPage = 1 // ctrl.data.pagination.current_page; + ctrl.totalItems = ctrl.data.pagination.total_pages * ctrl.itemsPerPage; + ctrl.currentPage = ctrl.data.pagination.current_page; }).error(function (error) { ctrl.data = null; ctrl.totalItems = 0; diff --git a/testapi/etc/config.ini b/testapi/etc/config.ini index 692e488..cf46bf7 100644 --- a/testapi/etc/config.ini +++ b/testapi/etc/config.ini @@ -10,6 +10,10 @@ dbname = test_results_collection # Listening port url = http://localhost:8000/api/v1 port = 8000 + +# Number of results for one page (integer value) +#results_per_page = 20 + # With debug_on set to true, error traces will be shown in HTTP responses debug = True authenticate = False diff --git a/testapi/opnfv_testapi/common/config.py b/testapi/opnfv_testapi/common/config.py index 46765ff..f73c0ab 100644 --- a/testapi/opnfv_testapi/common/config.py +++ b/testapi/opnfv_testapi/common/config.py @@ -17,6 +17,7 @@ class Config(object): def __init__(self): self.file = self.CONFIG if self.CONFIG else self._default_config() self._parse() + self._parse_per_page() self.static_path = os.path.join( os.path.dirname(os.path.normpath(__file__)), os.pardir, @@ -37,6 +38,10 @@ class Config(object): [setattr(self, '{}_{}'.format(section, k), self._parse_value(v)) for k, v in config.items(section)] + def _parse_per_page(self): + if not hasattr(self, 'api_results_per_page'): + self.api_results_per_page = 20 + @staticmethod def _parse_value(value): try: diff --git a/testapi/opnfv_testapi/resources/handlers.py b/testapi/opnfv_testapi/resources/handlers.py index 2fc31ca..42372e8 100644 --- a/testapi/opnfv_testapi/resources/handlers.py +++ b/testapi/opnfv_testapi/resources/handlers.py @@ -104,17 +104,35 @@ class GenericApiHandler(web.RequestHandler): if query is None: query = {} data = [] + sort = kwargs.get('sort') + page = kwargs.get('page') + last = kwargs.get('last') + per_page = kwargs.get('per_page') + cursor = self._eval_db(self.table, 'find', query) - if 'sort' in kwargs: - cursor = cursor.sort(kwargs.get('sort')) - if 'last' in kwargs: - cursor = cursor.limit(kwargs.get('last')) + if sort: + cursor = cursor.sort(sort) + if last and last != 0: + cursor = cursor.limit(last) + if page: + records_count = yield cursor.count() + total_pages, remainder = divmod(records_count, per_page) + if remainder > 0: + total_pages += 1 + cursor = cursor.skip((page - 1) * per_page).limit(per_page) while (yield cursor.fetch_next): data.append(self.format_data(cursor.next_object())) if res_op is None: res = {self.table: data} else: res = res_op(data, *args) + if page: + res.update({ + 'pagination': { + 'current_page': page, + 'total_pages': total_pages + } + }) self.finish_request(res) @web.asynchronous diff --git a/testapi/opnfv_testapi/resources/result_handlers.py b/testapi/opnfv_testapi/resources/result_handlers.py index 824a89e..208af6d 100644 --- a/testapi/opnfv_testapi/resources/result_handlers.py +++ b/testapi/opnfv_testapi/resources/result_handlers.py @@ -11,12 +11,15 @@ from datetime import timedelta from bson import objectid +from opnfv_testapi.common import config from opnfv_testapi.common import message from opnfv_testapi.common import raises from opnfv_testapi.resources import handlers from opnfv_testapi.resources import result_models from opnfv_testapi.tornado_swagger import swagger +CONF = config.Config() + class GenericResultHandler(handlers.GenericApiHandler): def __init__(self, application, request, **kwargs): @@ -135,22 +138,28 @@ class ResultsCLHandler(GenericResultHandler): @type last: L{string} @in last: query @required last: False + @param page: which page to list + @type page: L{int} + @in page: query + @required page: False @param trust_indicator: must be float @type trust_indicator: L{float} @in trust_indicator: query @required trust_indicator: False """ + limitations = {'sort': [('start_date', -1)]} last = self.get_query_argument('last', 0) if last is not None: last = self.get_int('last', last) + limitations.update({'last': last}) - page = self.get_query_argument('page', 0) - if page: - last = 20 + page = self.get_query_argument('page', 1) + if page is not None: + page = self.get_int('page', page) + limitations.update({'page': page, + 'per_page': CONF.api_results_per_page}) - self._list(query=self.set_query(), - sort=[('start_date', -1)], - last=last) + self._list(query=self.set_query(), **limitations) @swagger.operation(nickname="createTestResult") def post(self): diff --git a/testapi/opnfv_testapi/tests/unit/fake_pymongo.py b/testapi/opnfv_testapi/tests/unit/fake_pymongo.py index ef74a08..b2564a6 100644 --- a/testapi/opnfv_testapi/tests/unit/fake_pymongo.py +++ b/testapi/opnfv_testapi/tests/unit/fake_pymongo.py @@ -20,18 +20,18 @@ def thread_execute(method, *args, **kwargs): class MemCursor(object): def __init__(self, collection): self.collection = collection - self.count = len(self.collection) + self.length = len(self.collection) self.sorted = [] def _is_next_exist(self): - return self.count != 0 + return self.length != 0 @property def fetch_next(self): return thread_execute(self._is_next_exist) def next_object(self): - self.count -= 1 + self.length -= 1 return self.collection.pop() def sort(self, key_or_list): @@ -48,10 +48,25 @@ class MemCursor(object): def limit(self, limit): if limit != 0 and limit < len(self.collection): - self.collection = self.collection[0:limit] - self.count = limit + self.collection = self.collection[0: limit] + self.length = limit return self + def skip(self, skip): + if skip < self.length and (skip > 0): + self.collection = self.collection[self.length - skip: -1] + self.length -= skip + elif skip >= self.length: + self.collection = [] + self.length = 0 + return self + + def _count(self): + return self.length + + def count(self): + return thread_execute(self._count) + class MemDb(object): -- cgit 1.2.3-korg