Repository: readthedocs/readthedocs.org

Issue:
Add Version Compare information to the RTD Footer Response
We want to unify the API calls for `/api/v1/<project>/highest/<version>` and `/api/v2/footer_html` in order to reduce the number of API calls.



Discussion:
@ericholscher I have a few questions regarding this task.
1. The version comparison call is part of API v1. Shall this be moved into API v2? Do we need to stay backwards compatible?
2. The API calls are made from the `readthedocs/core/static-src/core/js/readthedocs-doc-embed.js` file. I noticed that the API is also called from the `media/javascript/rtd.js` file. Is this file still relevant?

We should just move the logic into the footer response. I don't believe we
need to maintain backwards compat, I don't think. It might make sense to
keep the logic seperate, and have an API endpoint for it, but to embed it
in the footer response on doc page load to decrease server requests, but
keep it available.

I believe rtd.js is old -- likely around for docs that haven't been rebuilt
in a long time.

On Tue, Jul 21, 2015 at 6:41 AM, Gregor Müllegger notifications@github.com
wrote:

> @ericholscher https://github.com/ericholscher I have a few questions
> regarding this task.
> 1. The version comparison call is part of API v1. Shall this be moved
>    into API v2? Do we need to stay backwards compatible?
> 2. The API calls are made from the
>    readthedocs/core/static-src/core/js/readthedocs-doc-embed.js file. I
>    noticed that the API is also called from the media/javascript/rtd.js
>    file. Is this file still relevant?
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/rtfd/readthedocs.org/issues/1463#issuecomment-123308650
> .

## 

Eric Holscher
Maker of the internet residing in Portland, Oregon
http://ericholscher.com

> The API calls are made from the readthedocs/core/static-src/core/js/readthedocs-doc-embed.js file. I noticed that the API is also called from the media/javascript/rtd.js file. Is this file still relevant?

@ericholscher What's your take on this?

Sorry ignore my comment. Your take on the rtd.js was hidden in the github UI because it thought it was part of the quoted email text...


Resolving patch:
diff --git a/readthedocs/api/base.py b/readthedocs/api/base.py
--- a/readthedocs/api/base.py
+++ b/readthedocs/api/base.py
@@ -18,8 +18,7 @@
 from builds.models import Build, Version
 from core.utils import trigger_build
 from projects.models import Project, ImportedFile
-from projects.version_handling import highest_version
-from projects.version_handling import parse_version_failsafe
+from restapi.views.footer_views import get_version_compare_data
 
 from .utils import SearchMixin, PostAuthentication
 
@@ -144,34 +143,16 @@ def get_object_list(self, request):
         self._meta.queryset = Version.objects.api(user=request.user, only_active=False)
         return super(VersionResource, self).get_object_list(request)
 
-    def version_compare(self, request, **kwargs):
-        project = get_object_or_404(Project, slug=kwargs['project_slug'])
-        highest_version_obj, highest_version_comparable = highest_version(
-            project.versions.filter(active=True))
-        base = kwargs.get('base', None)
-        ret_val = {
-            'project': highest_version_obj,
-            'version': highest_version_comparable,
-            'is_highest': True,
-        }
-        if highest_version_obj:
-            ret_val['url'] = highest_version_obj.get_absolute_url()
-            ret_val['slug'] = highest_version_obj.slug,
+    def version_compare(self, request, project_slug, base=None, **kwargs):
+        project = get_object_or_404(Project, slug=project_slug)
         if base and base != LATEST:
             try:
-                base_version_obj = project.versions.get(slug=base)
-                base_version_comparable = parse_version_failsafe(
-                    base_version_obj.verbose_name)
-                if base_version_comparable:
-                    # This is only place where is_highest can get set.  All
-                    # error cases will be set to True, for non- standard
-                    # versions.
-                    ret_val['is_highest'] = (
-                        base_version_comparable >= highest_version_comparable)
-                else:
-                    ret_val['is_highest'] = True
+                base_version = project.versions.get(slug=base)
             except (Version.DoesNotExist, TypeError):
-                ret_val['is_highest'] = True
+                base_version = None
+        else:
+            base_version = None
+        ret_val = get_version_compare_data(project, base_version)
         return self.create_response(request, ret_val)
 
     def build_version(self, request, **kwargs):
diff --git a/readthedocs/restapi/views/footer_views.py b/readthedocs/restapi/views/footer_views.py
--- a/readthedocs/restapi/views/footer_views.py
+++ b/readthedocs/restapi/views/footer_views.py
@@ -7,9 +7,39 @@
 from rest_framework.renderers import JSONPRenderer, JSONRenderer, BrowsableAPIRenderer
 from rest_framework.response import Response
 
+from builds.constants import LATEST
 from builds.models import Version
-from projects.models import Project
 from donate.models import SupporterPromo
+from projects.models import Project
+from projects.version_handling import highest_version
+from projects.version_handling import parse_version_failsafe
+
+
+def get_version_compare_data(project, base_version=None):
+    highest_version_obj, highest_version_comparable = highest_version(
+        project.versions.filter(active=True))
+    ret_val = {
+        'project': unicode(highest_version_obj),
+        'version': unicode(highest_version_comparable),
+        'is_highest': True,
+    }
+    if highest_version_obj:
+        ret_val['url'] = highest_version_obj.get_absolute_url()
+        ret_val['slug'] = highest_version_obj.slug,
+    if base_version and base_version.slug != LATEST:
+        try:
+            base_version_comparable = parse_version_failsafe(
+                base_version.verbose_name)
+            if base_version_comparable:
+                # This is only place where is_highest can get set. All error
+                # cases will be set to True, for non- standard versions.
+                ret_val['is_highest'] = (
+                    base_version_comparable >= highest_version_comparable)
+            else:
+                ret_val['is_highest'] = True
+        except (Version.DoesNotExist, TypeError):
+            ret_val['is_highest'] = True
+    return ret_val
 
 
 @decorators.api_view(['GET'])
@@ -64,6 +94,8 @@ def footer_html(request):
     if not promo_obj:
         show_promo = False
 
+    version_compare_data = get_version_compare_data(project, version)
+
     context = Context({
         'project': project,
         'path': path,
@@ -88,6 +120,7 @@ def footer_html(request):
     resp_data = {
         'html': html,
         'version_active': version.active,
+        'version_compare': version_compare_data,
         'version_supported': version.supported,
         'promo': show_promo,
     }


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_api_version_compare.py b/readthedocs/rtd_tests/tests/test_api_version_compare.py
new file mode 100644
--- /dev/null
+++ b/readthedocs/rtd_tests/tests/test_api_version_compare.py
@@ -0,0 +1,33 @@
+from django.test import TestCase
+
+from builds.constants import LATEST
+from projects.models import Project
+from restapi.views.footer_views import get_version_compare_data
+
+
+class VersionCompareTests(TestCase):
+    fixtures = ['eric.json', 'test_data.json']
+
+    def test_not_highest(self):
+        project = Project.objects.get(slug='read-the-docs')
+        version = project.versions.get(slug='0.2.1')
+
+        data = get_version_compare_data(project, version)
+        self.assertEqual(data['is_highest'], False)
+
+    def test_latest_version_highest(self):
+        project = Project.objects.get(slug='read-the-docs')
+
+        data = get_version_compare_data(project)
+        self.assertEqual(data['is_highest'], True)
+
+        version = project.versions.get(slug=LATEST)
+        data = get_version_compare_data(project, version)
+        self.assertEqual(data['is_highest'], True)
+
+    def test_real_highest(self):
+        project = Project.objects.get(slug='read-the-docs')
+        version = project.versions.get(slug='0.2.2')
+
+        data = get_version_compare_data(project, version)
+        self.assertEqual(data['is_highest'], True)
diff --git a/readthedocs/rtd_tests/tests/test_footer.py b/readthedocs/rtd_tests/tests/test_footer.py
--- a/readthedocs/rtd_tests/tests/test_footer.py
+++ b/readthedocs/rtd_tests/tests/test_footer.py
@@ -1,4 +1,5 @@
 import json
+import mock
 
 from django.test import TestCase
 
@@ -18,6 +19,7 @@ def test_footer(self):
         r = self.client.get('/api/v2/footer_html/?project=pip&version=latest&page=index', {})
         resp = json.loads(r.content)
         self.assertEqual(resp['version_active'], True)
+        self.assertEqual(resp['version_compare']['is_highest'], True)
         self.assertEqual(resp['version_supported'], True)
         self.assertEqual(r.context['main_project'], self.pip)
         self.assertEqual(r.status_code, 200)
@@ -29,6 +31,19 @@ def test_footer(self):
         self.assertEqual(resp['version_active'], False)
         self.assertEqual(r.status_code, 200)
 
+    def test_footer_uses_version_compare(self):
+        version_compare = 'restapi.views.footer_views.get_version_compare_data'
+        with mock.patch(version_compare) as get_version_compare_data:
+            get_version_compare_data.return_value = {
+                'MOCKED': True
+            }
+
+            r = self.client.get('/api/v2/footer_html/?project=pip&version=latest&page=index', {})
+            self.assertEqual(r.status_code, 200)
+
+            resp = json.loads(r.content)
+            self.assertEqual(resp['version_compare'], {'MOCKED': True})
+
     def test_pdf_build_mentioned_in_footer(self):
         with fake_paths_by_regex('\.pdf$'):
             response = self.client.get(