From f6d1458cd860cc00a147775b4d3aae6094a133fa Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Thu, 29 Aug 2019 19:42:53 -0500 Subject: [PATCH 1/4] Refactor footer_html view to class This is going to help us to extend this view more easily from the corporate site. --- readthedocs/api/v2/urls.py | 2 +- readthedocs/api/v2/views/footer_views.py | 249 +++++++++++++---------- 2 files changed, 144 insertions(+), 107 deletions(-) diff --git a/readthedocs/api/v2/urls.py b/readthedocs/api/v2/urls.py index 4d4c835d3cc..ceeec036979 100644 --- a/readthedocs/api/v2/urls.py +++ b/readthedocs/api/v2/urls.py @@ -56,7 +56,7 @@ function_urls = [ url(r'docurl/', core_views.docurl, name='docurl'), - url(r'footer_html/', footer_views.footer_html, name='footer_html'), + url(r'footer_html/', footer_views.FooterHTML.as_view(), name='footer_html'), ] task_urls = [ diff --git a/readthedocs/api/v2/views/footer_views.py b/readthedocs/api/v2/views/footer_views.py index 1036ea961b1..921210f21b8 100644 --- a/readthedocs/api/v2/views/footer_views.py +++ b/readthedocs/api/v2/views/footer_views.py @@ -3,9 +3,10 @@ from django.conf import settings from django.shortcuts import get_object_or_404 from django.template import loader as template_loader -from rest_framework import decorators, permissions +from rest_framework.permissions import AllowAny from rest_framework.renderers import JSONRenderer from rest_framework.response import Response +from rest_framework.views import APIView from rest_framework_jsonp.renderers import JSONPRenderer from readthedocs.api.v2.signals import footer_response @@ -63,113 +64,149 @@ def get_version_compare_data(project, base_version=None): return ret_val -@decorators.api_view(['GET']) -@decorators.permission_classes((permissions.AllowAny,)) -@decorators.renderer_classes((JSONRenderer, JSONPRenderer)) -def footer_html(request): +class FooterHTML(APIView): + """Render and return footer markup.""" - # TODO refactor this function - # pylint: disable=too-many-locals - project_slug = request.GET.get('project', None) - version_slug = request.GET.get('version', None) - page_slug = request.GET.get('page', '') - theme = request.GET.get('theme', False) - docroot = request.GET.get('docroot', '') - subproject = request.GET.get('subproject', False) - source_suffix = request.GET.get('source_suffix', '.rst') - - # Hack in a fix for missing version slug deploy that went out a while back - if version_slug == '': - version_slug = LATEST - - new_theme = (theme == 'sphinx_rtd_theme') - using_theme = (theme == 'default') - project = get_object_or_404(Project, slug=project_slug) - version = get_object_or_404( - Version.objects.public( - request.user, - project=project, - only_active=False, - ), - slug__iexact=version_slug, - ) - main_project = project.main_language_project or project - if page_slug and page_slug != 'index': - if main_project.documentation_type == 'sphinx_htmldir': - path = page_slug + '/' - else: - path = page_slug + '.html' - else: - path = '' - - version_compare_data = get_version_compare_data(project, version) - - context = { - 'project': project, - 'version': version, - 'path': path, - 'downloads': version.get_downloads(pretty=True), - 'current_version': version.verbose_name, - 'versions': project.ordered_active_versions(user=request.user), - 'main_project': main_project, - 'translations': main_project.translations.all(), - 'current_language': project.language, - 'using_theme': using_theme, - 'new_theme': new_theme, - 'settings': settings, - 'subproject': subproject, - 'github_edit_url': version.get_github_url( - docroot, - page_slug, - source_suffix, - 'edit', - ), - 'github_view_url': version.get_github_url( - docroot, - page_slug, - source_suffix, - 'view', - ), - 'gitlab_edit_url': version.get_gitlab_url( - docroot, - page_slug, - source_suffix, - 'edit', - ), - 'gitlab_view_url': version.get_gitlab_url( - docroot, - page_slug, - source_suffix, - 'view', - ), - 'bitbucket_url': version.get_bitbucket_url( - docroot, - page_slug, - source_suffix, - ), - 'theme': theme, - } + http_method_names = ['get'] + permission_classes = [AllowAny] + renderer_classes = [JSONRenderer, JSONPRenderer] - html = template_loader.get_template('restapi/footer.html').render( - context, - request, - ) - resp_data = { - 'html': html, - 'show_version_warning': project.show_version_warning, - 'version_active': version.active, - 'version_compare': version_compare_data, - 'version_supported': version.supported, - } + def _get_project(self): + cache_key = '_cached_project' + project = getattr(self, cache_key, None) - # Allow folks to hook onto the footer response for various information - # collection, or to modify the resp_data. - footer_response.send( - sender=None, - request=request, - context=context, - resp_data=resp_data, - ) + if not project: + project_slug = self.request.GET.get('project', None) + project = get_object_or_404(Project, slug=project_slug) + setattr(self, cache_key, project) + + return project + + def _get_version(self): + cache_key = '_cached_version' + version = getattr(self, cache_key, None) + + if not version: + version_slug = self.request.GET.get('version', None) - return Response(resp_data) + # Hack in a fix for missing version slug deploy + # that went out a while back + if version_slug == '': + version_slug = LATEST + + version = get_object_or_404( + Version.objects.public( + self.request.user, + project=self._get_project(), + only_active=False, + ), + slug__iexact=version_slug, + ) + setattr(self, cache_key, version) + + return version + + def _get_context(self): + theme = self.request.GET.get('theme', False) + docroot = self.request.GET.get('docroot', '') + subproject = self.request.GET.get('subproject', False) + source_suffix = self.request.GET.get('source_suffix', '.rst') + + new_theme = (theme == 'sphinx_rtd_theme') + using_theme = (theme == 'default') + + project = self._get_project() + main_project = project.main_language_project or project + version = self._get_version() + + page_slug = self.request.GET.get('page', '') + if page_slug and page_slug != 'index': + if main_project.documentation_type == 'sphinx_htmldir': + path = page_slug + '/' + else: + path = page_slug + '.html' + else: + path = '' + + context = { + 'project': project, + 'version': version, + 'path': path, + 'downloads': version.get_downloads(pretty=True), + 'current_version': version.verbose_name, + 'versions': project.ordered_active_versions( + user=self.request.user, + ), + 'main_project': main_project, + 'translations': main_project.translations.all(), + 'current_language': project.language, + 'using_theme': using_theme, + 'new_theme': new_theme, + 'settings': settings, + 'subproject': subproject, + 'github_edit_url': version.get_github_url( + docroot, + page_slug, + source_suffix, + 'edit', + ), + 'github_view_url': version.get_github_url( + docroot, + page_slug, + source_suffix, + 'view', + ), + 'gitlab_edit_url': version.get_gitlab_url( + docroot, + page_slug, + source_suffix, + 'edit', + ), + 'gitlab_view_url': version.get_gitlab_url( + docroot, + page_slug, + source_suffix, + 'view', + ), + 'bitbucket_url': version.get_bitbucket_url( + docroot, + page_slug, + source_suffix, + ), + 'theme': theme, + } + return context + + def get(self, request, format=None): + project = self._get_project() + version = self._get_version() + version_compare_data = get_version_compare_data( + project, + version, + ) + + context = self._get_context() + html = template_loader.get_template('restapi/footer.html').render( + context, + request, + ) + + resp_data = { + 'html': html, + 'show_version_warning': project.show_version_warning, + 'version_active': version.active, + 'version_compare': version_compare_data, + 'version_supported': version.supported, + } + + # Allow folks to hook onto the footer response for various information + # collection, or to modify the resp_data. + footer_response.send( + sender=None, + request=request, + context=context, + resp_data=resp_data, + ) + + return Response(resp_data) From 6b57a26d2a799b9e943739c87bc5f8263601962e Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Thu, 29 Aug 2019 19:43:02 -0500 Subject: [PATCH 2/4] Fix tests --- readthedocs/rtd_tests/tests/test_footer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readthedocs/rtd_tests/tests/test_footer.py b/readthedocs/rtd_tests/tests/test_footer.py index 241bc434f59..0c48c2ad3b9 100644 --- a/readthedocs/rtd_tests/tests/test_footer.py +++ b/readthedocs/rtd_tests/tests/test_footer.py @@ -4,7 +4,7 @@ from rest_framework.test import APIRequestFactory, APITestCase from readthedocs.api.v2.views.footer_views import ( - footer_html, + FooterHTML, get_version_compare_data, ) from readthedocs.builds.constants import BRANCH, LATEST, TAG @@ -26,7 +26,7 @@ def setUpTestData(cls): def render(self): request = self.factory.get(self.url) - response = footer_html(request) + response = FooterHTML.as_view()(request) response.render() return response @@ -243,7 +243,7 @@ def setUp(self): def render(self): request = self.factory.get(self.url) - response = footer_html(request) + response = FooterHTML.as_view()(request) response.render() return response From 9a38b27152bcbcb410e982e2f4266a75391aed1e Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Thu, 29 Aug 2019 20:49:58 -0500 Subject: [PATCH 3/4] Comment on cache --- readthedocs/api/v2/views/footer_views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/readthedocs/api/v2/views/footer_views.py b/readthedocs/api/v2/views/footer_views.py index 921210f21b8..3e76dbb2a5a 100644 --- a/readthedocs/api/v2/views/footer_views.py +++ b/readthedocs/api/v2/views/footer_views.py @@ -66,7 +66,14 @@ def get_version_compare_data(project, base_version=None): class FooterHTML(APIView): - """Render and return footer markup.""" + """ + Render and return footer markup. + + .. note:: + + The methods `_get_project` and `_get_version` + are called many times, so a basic cache is implemented. + """ http_method_names = ['get'] permission_classes = [AllowAny] From 9c5cf6f3a3211a474990629a29537e0ffee0065d Mon Sep 17 00:00:00 2001 From: Santos Gallegos Date: Thu, 29 Aug 2019 20:58:13 -0500 Subject: [PATCH 4/4] Use kwarg --- readthedocs/api/v2/views/footer_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs/api/v2/views/footer_views.py b/readthedocs/api/v2/views/footer_views.py index 3e76dbb2a5a..6a03ca9e5c2 100644 --- a/readthedocs/api/v2/views/footer_views.py +++ b/readthedocs/api/v2/views/footer_views.py @@ -104,7 +104,7 @@ def _get_version(self): version = get_object_or_404( Version.objects.public( - self.request.user, + user=self.request.user, project=self._get_project(), only_active=False, ),