From 6175e3186166d2254c4ae3153778007ad95bc578 Mon Sep 17 00:00:00 2001 From: George Schneeloch Date: Mon, 14 Sep 2015 15:04:40 -0400 Subject: [PATCH] Implemented page_size parameter to allow users to set page size --- lore/settings.py | 2 +- rest/pagination.py | 16 ++++ rest/tests/test_misc.py | 57 +++++++++++++ ui/tests/test_repository_views.py | 132 ++++++++++++++++++++++++++++++ ui/views.py | 14 +++- 5 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 rest/pagination.py create mode 100644 ui/tests/test_repository_views.py diff --git a/lore/settings.py b/lore/settings.py index 473f4837..9b0da97e 100644 --- a/lore/settings.py +++ b/lore/settings.py @@ -345,7 +345,7 @@ def get_var(name, default): 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ), - 'PAGE_SIZE': 20, + 'DEFAULT_PAGINATION_CLASS': 'rest.pagination.LorePagination', 'UPLOADED_FILES_USE_URL': False, } diff --git a/rest/pagination.py b/rest/pagination.py new file mode 100644 index 00000000..d07e0656 --- /dev/null +++ b/rest/pagination.py @@ -0,0 +1,16 @@ +""" +Pagination classes for Lore's REST API. +""" + +from __future__ import unicode_literals + +from rest_framework.pagination import PageNumberPagination + + +class LorePagination(PageNumberPagination): + """ + Pagination class for Lore's REST API. + """ + page_size = 20 + page_size_query_param = 'page_size' + max_page_size = 1000 diff --git a/rest/tests/test_misc.py b/rest/tests/test_misc.py index 416a691c..305dcbe4 100644 --- a/rest/tests/test_misc.py +++ b/rest/tests/test_misc.py @@ -4,13 +4,17 @@ from __future__ import unicode_literals from rest_framework.status import ( + HTTP_200_OK, HTTP_404_NOT_FOUND, ) +from learningresources.models import LearningResourceType from rest.tests.base import ( RESTTestCase, API_BASE, + as_json, ) +from rest.pagination import LorePagination class TestMisc(RESTTestCase): @@ -18,6 +22,30 @@ class TestMisc(RESTTestCase): REST test """ + def assert_page_size(self, page_size, expected_num_results): + """ + Helper function to assert len(results) == expected_num_results. + """ + num_types = LearningResourceType.objects.filter().count() + + if page_size is None: + page_size_param = "" + else: + page_size_param = "?{query_param}={page_size}".format( + query_param=LorePagination.page_size_query_param, + page_size=page_size + ) + resp = self.client.get( + "{api_base}learning_resource_types/{page_size_param}".format( + api_base=API_BASE, + page_size_param=page_size_param + ) + ) + self.assertEqual(resp.status_code, HTTP_200_OK) + resources = as_json(resp) + self.assertEqual(len(resources['results']), expected_num_results) + self.assertEqual(resources['count'], num_types) + def test_root(self): """ Test root of API @@ -48,3 +76,32 @@ def test_root(self): self.assertEqual(HTTP_404_NOT_FOUND, resp.status_code) resp = self.client.options(API_BASE) self.assertEqual(HTTP_404_NOT_FOUND, resp.status_code) + + def test_pagination(self): + """ + Test page_size query parameter. + """ + types = [ + LearningResourceType(name="type_{i}".format(i=i)) + for i in range(LorePagination.max_page_size + 10) + ] + LearningResourceType.objects.bulk_create(types) + + self.assert_page_size(2, 2) + self.assert_page_size(10, 10) + self.assert_page_size(0, LorePagination.page_size) + self.assert_page_size(None, LorePagination.page_size) + self.assert_page_size("xyz", LorePagination.page_size) + self.assert_page_size(-1, LorePagination.page_size) + self.assert_page_size( + LorePagination.max_page_size + 1, + LorePagination.max_page_size + ) + self.assert_page_size( + LorePagination.max_page_size, + LorePagination.max_page_size + ) + self.assert_page_size( + LorePagination.max_page_size - 1, + LorePagination.max_page_size - 1 + ) diff --git a/ui/tests/test_repository_views.py b/ui/tests/test_repository_views.py new file mode 100644 index 00000000..c4fb8c1d --- /dev/null +++ b/ui/tests/test_repository_views.py @@ -0,0 +1,132 @@ +""" +Tests for repository views. +""" + +from __future__ import unicode_literals + +import json + +from rest_framework.status import HTTP_201_CREATED + +from learningresources.tests.base import LoreTestCase +from rest.pagination import LorePagination +from search.sorting import LoreSortingFields + + +class TestRepositoryViews(LoreTestCase): + """ + Tests for repository views. + """ + + def assert_page_size(self, page_size, expected_page_size): + """ + Helper function to assert page size in context. + """ + if page_size is not None: + page_size_param = "?{query_param}={page_size}".format( + query_param=LorePagination.page_size_query_param, + page_size=page_size + ) + else: + page_size_param = "" + + resp = self.client.get( + "/repositories/{slug}/{page_size_param}".format( + slug=self.repo.slug, + page_size_param=page_size_param + ) + ) + self.assertEqual( + json.loads(resp.context['page_size_json']), + expected_page_size + ) + + def test_page_size(self): + """ + Test that page size context is set properly. + """ + self.assert_page_size(2, 2) + self.assert_page_size(10, 10) + self.assert_page_size(0, LorePagination.page_size) + self.assert_page_size(None, LorePagination.page_size) + self.assert_page_size("xyz", LorePagination.page_size) + self.assert_page_size(-1, LorePagination.page_size) + self.assert_page_size( + LorePagination.max_page_size + 1, + LorePagination.max_page_size + ) + self.assert_page_size( + LorePagination.max_page_size, + LorePagination.max_page_size + ) + self.assert_page_size( + LorePagination.max_page_size - 1, + LorePagination.max_page_size - 1 + ) + + def test_repo(self): + """ + Test that repo is present. + """ + resp = self.client.get( + "/repositories/{slug}/".format(slug=self.repo.slug)) + self.assertEqual(self.repo, resp.context['repo']) + + def test_perms_on_cur_repo(self): + """ + Test that perms_on_cur_repo context is set. + """ + resp = self.client.get( + "/repositories/{slug}/".format(slug=self.repo.slug)) + expected_perms = [ + 'manage_repo_users', + 'add_edit_metadata', + 'manage_taxonomy', + 'view_repo', + 'import_course' + ] + self.assertEqual( + sorted(resp.context["perms_on_cur_repo"]), + sorted(expected_perms) + ) + + def test_exports(self): + """ + Test presence of exports. + """ + resp = self.client.get( + "/repositories/{slug}/".format(slug=self.repo.slug)) + self.assertEqual(json.loads(resp.context["exports_json"]), []) + + lid = self.repo.course_set.first().resources.first().id + resp = self.client.post( + "/api/v1/repositories/{slug}/" + "learning_resource_exports/{user}/".format( + slug=self.repo.slug, + user=self.user.username, + ), {"id": lid}) + self.assertEqual(resp.status_code, HTTP_201_CREATED) + resp = self.client.get( + "/repositories/{slug}/".format(slug=self.repo.slug)) + self.assertEqual(json.loads(resp.context["exports_json"]), [lid]) + + def test_sorting_options(self): + """ + Test presence of sorting options. + """ + + sortby = "nr_views" + resp = self.client.get( + "/repositories/{slug}/?sortby={sortby}".format( + slug=self.repo.slug, + sortby=sortby + ) + ) + self.assertEqual( + json.loads(resp.context["sorting_options_json"]), + { + "current": list(LoreSortingFields.get_sorting_option(sortby)), + "all": [list(x) for x in + LoreSortingFields.all_sorting_options_but(sortby)] + } + ) diff --git a/ui/views.py b/ui/views.py index d42f65d2..74ad55d8 100644 --- a/ui/views.py +++ b/ui/views.py @@ -34,7 +34,7 @@ StaticAsset, STATIC_ASSET_PREFIX, ) -from lore.settings import REST_FRAMEWORK +from rest.pagination import LorePagination from roles.permissions import RepoPermission from search.sorting import LoreSortingFields from taxonomy.models import Vocabulary, Term @@ -209,12 +209,22 @@ def repository_view(request, repo_slug): sortby_field) } + try: + page_size = int(request.GET.get(LorePagination.page_size_query_param)) + except (ValueError, KeyError, TypeError): + page_size = LorePagination.page_size + + if page_size <= 0: + page_size = LorePagination.page_size + elif page_size > LorePagination.max_page_size: + page_size = LorePagination.max_page_size + context = { "repo": repo, "perms_on_cur_repo": get_perms(request.user, repo), "sorting_options_json": json.dumps(sorting_options), "exports_json": json.dumps(exports), - "page_size_json": json.dumps(REST_FRAMEWORK['PAGE_SIZE']) + "page_size_json": json.dumps(page_size) } return render(