From 6055b13ee26f967ac619932e8f6dd9e31900d2f1 Mon Sep 17 00:00:00 2001 From: CM Lubinski Date: Mon, 22 May 2017 21:14:50 -0700 Subject: [PATCH 1/7] Resolve first round of linting errors in restapi. This is a first step and only adds docstrings, etc. It doesn't refactor any code. --- prospector-more.yml | 1 - readthedocs/restapi/client.py | 1 + readthedocs/restapi/permissions.py | 1 + readthedocs/restapi/serializers.py | 1 + readthedocs/restapi/signals.py | 1 + readthedocs/restapi/urls.py | 1 + readthedocs/restapi/utils.py | 15 +++++++-------- readthedocs/restapi/views/core_views.py | 1 + readthedocs/restapi/views/footer_views.py | 7 +++++++ readthedocs/restapi/views/integrations.py | 8 +++++--- readthedocs/restapi/views/model_views.py | 7 +++++-- readthedocs/restapi/views/search_views.py | 1 + readthedocs/restapi/views/task_views.py | 1 + 13 files changed, 32 insertions(+), 14 deletions(-) diff --git a/prospector-more.yml b/prospector-more.yml index fa80c27334b..59b9b9f8a30 100644 --- a/prospector-more.yml +++ b/prospector-more.yml @@ -12,7 +12,6 @@ ignore-paths: - profiles/ - projects/ - redirects/ - - restapi/ - search/ pylint: diff --git a/readthedocs/restapi/client.py b/readthedocs/restapi/client.py index 233d3e0b334..bbda3ac4d71 100644 --- a/readthedocs/restapi/client.py +++ b/readthedocs/restapi/client.py @@ -1,3 +1,4 @@ +"""Simple client to access our API with SLUMBER credentials.""" import logging from slumber import API, serialize diff --git a/readthedocs/restapi/permissions.py b/readthedocs/restapi/permissions.py index 25c7085a387..95664537f22 100644 --- a/readthedocs/restapi/permissions.py +++ b/readthedocs/restapi/permissions.py @@ -1,3 +1,4 @@ +"""Defines access permissions for the API.""" from rest_framework import permissions from readthedocs.privacy.backend import AdminPermission diff --git a/readthedocs/restapi/serializers.py b/readthedocs/restapi/serializers.py index e779382124e..3d9076dea64 100644 --- a/readthedocs/restapi/serializers.py +++ b/readthedocs/restapi/serializers.py @@ -1,3 +1,4 @@ +"""Defines serializers for each of our models.""" from rest_framework import serializers from readthedocs.builds.models import Build, BuildCommandResult, Version diff --git a/readthedocs/restapi/signals.py b/readthedocs/restapi/signals.py index 12fe14063db..f17be08a046 100644 --- a/readthedocs/restapi/signals.py +++ b/readthedocs/restapi/signals.py @@ -1,3 +1,4 @@ +"""We define custom Django signals to trigger when a footer is rendered.""" import django.dispatch footer_response = django.dispatch.Signal( diff --git a/readthedocs/restapi/urls.py b/readthedocs/restapi/urls.py index de85d60894d..0538b3e76a1 100644 --- a/readthedocs/restapi/urls.py +++ b/readthedocs/restapi/urls.py @@ -1,3 +1,4 @@ +"""Define routes between URL paths and views/endpoints.""" from django.conf.urls import url, include from rest_framework import routers diff --git a/readthedocs/restapi/utils.py b/readthedocs/restapi/utils.py index e4c67f7bb93..aab5fd18c1b 100644 --- a/readthedocs/restapi/utils.py +++ b/readthedocs/restapi/utils.py @@ -1,8 +1,7 @@ +"""Utility functions that are used by both views and celery tasks.""" import hashlib import logging -import requests - from readthedocs.builds.constants import NON_REPOSITORY_VERSIONS from readthedocs.builds.models import Version from readthedocs.search.indexes import PageIndex, ProjectIndex, SectionIndex @@ -10,7 +9,7 @@ log = logging.getLogger(__name__) -def sync_versions(project, versions, type): +def sync_versions(project, versions, type): # pylint: disable=redefined-builtin """Update the database with the current versions from the repository.""" # Bookkeeping for keeping tag/branch identifies correct verbose_names = [v['verbose_name'] for v in versions] @@ -39,8 +38,8 @@ def sync_versions(project, versions, type): type=type, machine=False, ) - log.info("(Sync Versions) Updated Version: [%s=%s] " % ( - version['verbose_name'], version['identifier'])) + log.info("(Sync Versions) Updated Version: [%s=%s] ", + version['verbose_name'], version['identifier']) else: # New Version created_version = Version.objects.create( @@ -51,7 +50,7 @@ def sync_versions(project, versions, type): ) added.add(created_version.slug) if added: - log.info("(Sync Versions) Added Versions: [%s] " % ' '.join(added)) + log.info("(Sync Versions) Added Versions: [%s] ", ' '.join(added)) return added @@ -72,7 +71,7 @@ def delete_versions(project, version_data): if to_delete_qs.count(): ret_val = {obj.slug for obj in to_delete_qs} - log.info("(Sync Versions) Deleted Versions: [%s]" % ' '.join(ret_val)) + log.info("(Sync Versions) Deleted Versions: [%s]", ' '.join(ret_val)) to_delete_qs.delete() return ret_val else: @@ -112,7 +111,7 @@ def index_search_request(version, page_list, commit, project_scale, page_scale, routes = [project.slug] routes.extend([p.parent.slug for p in project.superprojects.all()]) for page in page_list: - log.debug("Indexing page: %s:%s" % (project.slug, page['path'])) + log.debug("Indexing page: %s:%s", project.slug, page['path']) page_id = (hashlib .md5('-'.join([project.slug, version.slug, page['path']])) .hexdigest()) diff --git a/readthedocs/restapi/views/core_views.py b/readthedocs/restapi/views/core_views.py index fff134e237e..c216e01a528 100644 --- a/readthedocs/restapi/views/core_views.py +++ b/readthedocs/restapi/views/core_views.py @@ -1,3 +1,4 @@ +"""Utility endpoints relating to canonical urls, embedded content, etc.""" from rest_framework import decorators, permissions, status from rest_framework.renderers import JSONRenderer from rest_framework.response import Response diff --git a/readthedocs/restapi/views/footer_views.py b/readthedocs/restapi/views/footer_views.py index f3317119f01..7378c689ba2 100644 --- a/readthedocs/restapi/views/footer_views.py +++ b/readthedocs/restapi/views/footer_views.py @@ -1,3 +1,4 @@ +"""Endpoint to generate footer HTML.""" from django.shortcuts import get_object_or_404 from django.template import RequestContext, loader as template_loader from django.conf import settings @@ -18,6 +19,11 @@ def get_version_compare_data(project, base_version=None): + """Retrieve metadata about the highest version available for this project. + + :param base_version: We assert whether or not the base_version is also the + highest version in the resulting "is_highest" value. + """ highest_version_obj, highest_version_comparable = highest_version( project.versions.public().filter(active=True)) ret_val = { @@ -48,6 +54,7 @@ def get_version_compare_data(project, base_version=None): @decorators.permission_classes((permissions.AllowAny,)) @decorators.renderer_classes((JSONRenderer, JSONPRenderer)) def footer_html(request): + """Render and return footer markup.""" project_slug = request.GET.get('project', None) version_slug = request.GET.get('version', None) page_slug = request.GET.get('page', None) diff --git a/readthedocs/restapi/views/integrations.py b/readthedocs/restapi/views/integrations.py index 97bf5b06a81..4a3d6d0c63e 100644 --- a/readthedocs/restapi/views/integrations.py +++ b/readthedocs/restapi/views/integrations.py @@ -1,3 +1,4 @@ +"""Endpoints integrating with Github, Bitbucket, and other webhooks.""" import json import logging @@ -5,10 +6,9 @@ from rest_framework.views import APIView from rest_framework.renderers import JSONRenderer from rest_framework.response import Response -from rest_framework.exceptions import APIException, ParseError, NotFound +from rest_framework.exceptions import ParseError, NotFound from django.shortcuts import get_object_or_404 -from django.http import Http404 from readthedocs.core.views.hooks import build_branches from readthedocs.core.signals import (webhook_github, webhook_bitbucket, @@ -27,12 +27,14 @@ class WebhookMixin(object): + """Base class for Webhook mixins.""" + permission_classes = (permissions.AllowAny,) renderer_classes = (JSONRenderer,) integration = None integration_type = None - def post(self, request, project_slug, format=None): + def post(self, request, project_slug): """Set up webhook post view with request and project objects""" self.request = request self.project = None diff --git a/readthedocs/restapi/views/model_views.py b/readthedocs/restapi/views/model_views.py index 8f8b89845c0..381079ac35c 100644 --- a/readthedocs/restapi/views/model_views.py +++ b/readthedocs/restapi/views/model_views.py @@ -1,3 +1,4 @@ +"""Endpoints for listing Projects, Versions, Builds, etc.""" import logging from django.shortcuts import get_object_or_404 @@ -13,7 +14,6 @@ from readthedocs.core.utils import trigger_build from readthedocs.oauth.services import GitHubService, registry from readthedocs.oauth.models import RemoteOrganization, RemoteRepository -from readthedocs.builds.constants import STABLE from readthedocs.projects.filters import ProjectFilter, DomainFilter from readthedocs.projects.models import Project, EmailHook, Domain from readthedocs.projects.version_handling import determine_stable_version @@ -31,6 +31,9 @@ class ProjectViewSet(viewsets.ModelViewSet): + + """List, filter, etc. Projects.""" + permission_classes = [APIPermission] renderer_classes = (JSONRenderer,) serializer_class = ProjectSerializer @@ -127,7 +130,7 @@ def sync_versions(self, request, **kwargs): added_versions.update(ret_set) deleted_versions = api_utils.delete_versions(project, data) except Exception, e: - log.exception("Sync Versions Error: %s" % e.message) + log.exception("Sync Versions Error: %s", e.message) return Response({'error': e.message}, status=status.HTTP_400_BAD_REQUEST) promoted_version = project.update_stable_version() diff --git a/readthedocs/restapi/views/search_views.py b/readthedocs/restapi/views/search_views.py index cef4af957a3..9f060192143 100644 --- a/readthedocs/restapi/views/search_views.py +++ b/readthedocs/restapi/views/search_views.py @@ -1,3 +1,4 @@ +"""Endpoints related to searching through projects, sections, etc.""" import logging from rest_framework import decorators, permissions, status diff --git a/readthedocs/restapi/views/task_views.py b/readthedocs/restapi/views/task_views.py index 0f0bb38b351..3b7c07c8e0b 100644 --- a/readthedocs/restapi/views/task_views.py +++ b/readthedocs/restapi/views/task_views.py @@ -1,3 +1,4 @@ +"""Endpoints relating to task/job status, etc.""" import logging from django.core.urlresolvers import reverse From e1d48d43b87f1ed1ae993f0dd737062b4a0a9d3e Mon Sep 17 00:00:00 2001 From: CM Lubinski Date: Mon, 22 May 2017 21:23:30 -0700 Subject: [PATCH 2/7] Reduce unused params. In several cases, we can use the provided `request` rather than access `self` (which could make these functions easier to test in isolation). In others, the unused args could be folded into an unnamed kwarg param. --- readthedocs/restapi/views/model_views.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/readthedocs/restapi/views/model_views.py b/readthedocs/restapi/views/model_views.py index 381079ac35c..cdf4930f8fd 100644 --- a/readthedocs/restapi/views/model_views.py +++ b/readthedocs/restapi/views/model_views.py @@ -50,7 +50,7 @@ def get_queryset(self): def valid_versions(self, request, **kwargs): """Maintain state of versions that are wanted.""" project = get_object_or_404( - Project.objects.api(self.request.user), pk=kwargs['pk']) + Project.objects.api(request.user), pk=kwargs['pk']) if not project.num_major or not project.num_minor or not project.num_point: return Response( {'error': 'Project does not support point version control'}, @@ -65,7 +65,7 @@ def valid_versions(self, request, **kwargs): }) @detail_route() - def translations(self, request, pk, **kwargs): + def translations(self, _, **__): translations = self.get_object().translations.all() return Response({ 'translations': ProjectSerializer(translations, many=True).data @@ -74,7 +74,7 @@ def translations(self, request, pk, **kwargs): @detail_route() def subprojects(self, request, **kwargs): project = get_object_or_404( - Project.objects.api(self.request.user), pk=kwargs['pk']) + Project.objects.api(request.user), pk=kwargs['pk']) rels = project.subprojects.all() children = [rel.child for rel in rels] return Response({ @@ -84,7 +84,7 @@ def subprojects(self, request, **kwargs): @decorators.detail_route(permission_classes=[permissions.IsAdminUser]) def token(self, request, **kwargs): project = get_object_or_404( - Project.objects.api(self.request.user), pk=kwargs['pk']) + Project.objects.api(request.user), pk=kwargs['pk']) token = GitHubService.get_token_for_project(project, force_local=True) return Response({ 'token': token @@ -93,7 +93,7 @@ def token(self, request, **kwargs): @decorators.detail_route() def canonical_url(self, request, **kwargs): project = get_object_or_404( - Project.objects.api(self.request.user), pk=kwargs['pk']) + Project.objects.api(request.user), pk=kwargs['pk']) return Response({ 'url': project.get_docs_url() }) @@ -106,7 +106,7 @@ def sync_versions(self, request, **kwargs): Returns the identifiers for the versions that have been deleted. """ project = get_object_or_404( - Project.objects.api(self.request.user), pk=kwargs['pk']) + Project.objects.api(request.user), pk=kwargs['pk']) # If the currently highest non-prerelease version is active, then make # the new latest version active as well. From 67e936d69dae1161b37e9a1abba7bd6c6d85bdc5 Mon Sep 17 00:00:00 2001 From: CM Lubinski Date: Mon, 22 May 2017 21:51:20 -0700 Subject: [PATCH 3/7] Split footer_html view in two. Smaller functions should be easier to test independently. This will also resolve a pyflakes warning. --- readthedocs/restapi/views/footer_views.py | 84 +++++++++++++---------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/readthedocs/restapi/views/footer_views.py b/readthedocs/restapi/views/footer_views.py index 7378c689ba2..38401576752 100644 --- a/readthedocs/restapi/views/footer_views.py +++ b/readthedocs/restapi/views/footer_views.py @@ -50,38 +50,29 @@ 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): - """Render and return footer markup.""" - project_slug = request.GET.get('project', None) - version_slug = request.GET.get('version', None) - page_slug = request.GET.get('page', None) - theme = request.GET.get('theme', False) +def footer_path(main_project, page_slug): + if page_slug and page_slug != "index": + if main_project.documentation_type in ("sphinx_htmldir", "mkdocs"): + return page_slug + "/" + elif main_project.documentation_type == "sphinx_singlehtml": + return "index.html#document-" + page_slug + else: + return page_slug + ".html" + else: + return "" + + +def version_context(project, request): + """Derive extra footer context by looking up the requested version.""" + page_slug = request.GET.get('page') + version_slug = request.GET.get('version') docroot = request.GET.get('docroot', '') - subproject = request.GET.get('subproject', False) source_suffix = request.GET.get('source_suffix', '.rst') + main_project = project.main_language_project or project - 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=version_slug) - main_project = project.main_language_project or project - - if page_slug and page_slug != "index": - if ( - main_project.documentation_type == "sphinx_htmldir" or - main_project.documentation_type == "mkdocs"): - path = page_slug + "/" - elif main_project.documentation_type == "sphinx_singlehtml": - path = "index.html#document-" + page_slug - else: - path = page_slug + ".html" - else: - path = "" if version.type == TAG and version.project.has_pdf(version.slug): print_url = ( @@ -92,35 +83,52 @@ def footer_html(request): else: print_url = None - version_compare_data = get_version_compare_data(project, version) - - context = { - 'project': project, + return { 'version': version, - 'path': path, + 'path': footer_path(main_project, page_slug), '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_version': version.verbose_name, + 'print_url': print_url, + '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'), + 'bitbucket_url': version.get_bitbucket_url(docroot, page_slug, source_suffix), + } + + +@decorators.api_view(['GET']) +@decorators.permission_classes((permissions.AllowAny,)) +@decorators.renderer_classes((JSONRenderer, JSONPRenderer)) +def footer_html(request): + """Render and return footer markup.""" + project_slug = request.GET.get('project', None) + theme = request.GET.get('theme', False) + subproject = request.GET.get('subproject', False) + + new_theme = (theme == "sphinx_rtd_theme") + using_theme = (theme == "default") + project = get_object_or_404(Project, slug=project_slug) + + context = { + 'project': project, + 'versions': project.ordered_active_versions(user=request.user), 'current_language': project.language, 'using_theme': using_theme, 'new_theme': new_theme, 'settings': settings, 'subproject': subproject, - 'print_url': print_url, - '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'), - 'bitbucket_url': version.get_bitbucket_url(docroot, page_slug, source_suffix), 'theme': theme, } + context.update(version_context(project, request)) request_context = RequestContext(request, context) html = template_loader.get_template('restapi/footer.html').render(request_context) + version = context['version'] resp_data = { 'html': html, 'version_active': version.active, - 'version_compare': version_compare_data, + 'version_compare': get_version_compare_data(project, version), 'version_supported': version.supported, } From b830828b453b956a58e9a28aa17d26a9e793190d Mon Sep 17 00:00:00 2001 From: CM Lubinski Date: Mon, 22 May 2017 21:57:16 -0700 Subject: [PATCH 4/7] Don't replace section variable. This worked as intended, but only because the overridden section variable would later evaluate to True. Using a different variable name makes this a bit less error-prone. --- readthedocs/restapi/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/readthedocs/restapi/utils.py b/readthedocs/restapi/utils.py index aab5fd18c1b..720dc257f38 100644 --- a/readthedocs/restapi/utils.py +++ b/readthedocs/restapi/utils.py @@ -128,18 +128,18 @@ def index_search_request(version, page_list, commit, project_scale, page_scale, 'weight': page_scale + project_scale, }) if section: - for section in page['sections']: + for sect in page['sections']: section_index_list.append({ 'id': (hashlib .md5('-'.join([project.slug, version.slug, - page['path'], section['id']])) + page['path'], sect['id']])) .hexdigest()), 'project': project.slug, 'version': version.slug, 'path': page['path'], - 'page_id': section['id'], - 'title': section['title'], - 'content': section['content'], + 'page_id': sect['id'], + 'title': sect['title'], + 'content': sect['content'], 'weight': page_scale, }) for route in routes: From c955155fd38a68f37050443e30b65ff0b151ee70 Mon Sep 17 00:00:00 2001 From: CM Lubinski Date: Mon, 22 May 2017 22:44:23 -0700 Subject: [PATCH 5/7] Split index_search_request into several functions. By splitting the function up, we not only resolve pyflakes issues around local variables, we also make this function easier to test. I don't like the SectionIndexer approach, but there was a chunk of state (section_index_list) which was extended in each loop iteration. To recreate that functionality while limiting the number of lines in each loop, the state is moved into a separate object. If the original logic was faulty, this could be much more legible. --- readthedocs/restapi/utils.py | 146 ++++++++++++++++++++++------------- 1 file changed, 91 insertions(+), 55 deletions(-) diff --git a/readthedocs/restapi/utils.py b/readthedocs/restapi/utils.py index 720dc257f38..b768348129a 100644 --- a/readthedocs/restapi/utils.py +++ b/readthedocs/restapi/utils.py @@ -78,19 +78,13 @@ def delete_versions(project, version_data): return set() -def index_search_request(version, page_list, commit, project_scale, page_scale, - section=True, delete=True): - """Update search indexes with build output JSON - - In order to keep sub-projects all indexed on the same shard, indexes will be - updated using the parent project's slug as the routing value. - """ - project = version.project - - log_msg = ' '.join([page['path'] for page in page_list]) +def log_page_list(project, page_list): + log_msg = ' '.join(page['path'] for page in page_list) log.info("Updating search index: project=%s pages=[%s]", project.slug, log_msg) + +def index_project(project, project_scale): project_obj = ProjectIndex() project_obj.index_document(data={ 'id': project.pk, @@ -104,18 +98,76 @@ def index_search_request(version, page_list, commit, project_scale, page_scale, 'weight': project_scale, }) + +def index_pages(routes, index_list, project): page_obj = PageIndex() - section_obj = SectionIndex() - index_list = [] - section_index_list = [] - routes = [project.slug] - routes.extend([p.parent.slug for p in project.superprojects.all()]) + for route in routes: + page_obj.bulk_index(index_list, parent=project.slug, routing=route) + + +def delete_pages(version, commit, project): + page_obj = PageIndex() + log.info("Deleting files not in commit: %s", commit) + # TODO: AK Make sure this works + delete_query = { + "query": { + "bool": { + "must": [ + {"term": {"project": project.slug, }}, + {"term": {"version": version.slug, }}, + ], + "must_not": { + "term": { + "commit": commit + } + } + } + } + } + page_obj.delete_document(body=delete_query) + + +class SectionIndexer(object): + + """Stores section metadata. Wraps state between calls.""" + + def __init__(self): + self.section_index_list = [] + self.section_obj = SectionIndex() + + def __call__(self, page, project, version, page_scale, page_id, routes): + for section in page['sections']: + self.section_index_list.append({ + 'id': (hashlib + .md5('-'.join([project.slug, version.slug, + page['path'], section['id']])) + .hexdigest()), + 'project': project.slug, + 'version': version.slug, + 'path': page['path'], + 'page_id': section['id'], + 'title': section['title'], + 'content': section['content'], + 'weight': page_scale, + }) + for route in routes: + self.section_obj.bulk_index(self.section_index_list, parent=page_id, + routing=route) + + +def generate_index_list(version, project, page_list, commit, project_scale, + page_scale, routes, index_sections): + """Generate an index dict per page. + + If requested, also index the relevant page sections. + """ + section_indexer = SectionIndexer() for page in page_list: log.debug("Indexing page: %s:%s", project.slug, page['path']) page_id = (hashlib .md5('-'.join([project.slug, version.slug, page['path']])) .hexdigest()) - index_list.append({ + yield { 'id': page_id, 'project': project.slug, 'version': version.slug, @@ -126,45 +178,29 @@ def index_search_request(version, page_list, commit, project_scale, page_scale, 'taxonomy': None, 'commit': commit, 'weight': page_scale + project_scale, - }) - if section: - for sect in page['sections']: - section_index_list.append({ - 'id': (hashlib - .md5('-'.join([project.slug, version.slug, - page['path'], sect['id']])) - .hexdigest()), - 'project': project.slug, - 'version': version.slug, - 'path': page['path'], - 'page_id': sect['id'], - 'title': sect['title'], - 'content': sect['content'], - 'weight': page_scale, - }) - for route in routes: - section_obj.bulk_index(section_index_list, parent=page_id, - routing=route) + } + if index_sections: + section_indexer(page, project, version, page_scale, routes, page_id) - for route in routes: - page_obj.bulk_index(index_list, parent=project.slug, routing=route) + +def index_search_request(version, page_list, commit, project_scale, page_scale, + section=True, delete=True): + """Update search indexes with build output JSON + + In order to keep sub-projects all indexed on the same shard, indexes will be + updated using the parent project's slug as the routing value. + """ + project = version.project + log_page_list(project, page_list) + routes = [project.slug] + routes.extend(p.parent.slug for p in project.superprojects.all()) + + index_project(project, project_scale) + index_list = list(generate_index_list( + version, project, page_list, commit, project_scale, page_scale, + routes, index_sections=section + )) + index_pages(routes, index_list, project) if delete: - log.info("Deleting files not in commit: %s", commit) - # TODO: AK Make sure this works - delete_query = { - "query": { - "bool": { - "must": [ - {"term": {"project": project.slug, }}, - {"term": {"version": version.slug, }}, - ], - "must_not": { - "term": { - "commit": commit - } - } - } - } - } - page_obj.delete_document(body=delete_query) + delete_pages(version, commit, project) From 919425c7b843b158d1bc6124cf09157f26abb5a7 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 25 May 2017 21:34:23 -0700 Subject: [PATCH 6/7] Minor docstring update --- readthedocs/restapi/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs/restapi/client.py b/readthedocs/restapi/client.py index bbda3ac4d71..f263726cacb 100644 --- a/readthedocs/restapi/client.py +++ b/readthedocs/restapi/client.py @@ -1,4 +1,4 @@ -"""Simple client to access our API with SLUMBER credentials.""" +"""Simple client to access our API with Slumber credentials.""" import logging from slumber import API, serialize From 92d05f300120f98b863dc7ef0d4e1bcabfa45183 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 25 May 2017 21:37:43 -0700 Subject: [PATCH 7/7] Fix indent on docstring --- readthedocs/restapi/views/footer_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs/restapi/views/footer_views.py b/readthedocs/restapi/views/footer_views.py index 38401576752..dcecc89cc6a 100644 --- a/readthedocs/restapi/views/footer_views.py +++ b/readthedocs/restapi/views/footer_views.py @@ -22,7 +22,7 @@ def get_version_compare_data(project, base_version=None): """Retrieve metadata about the highest version available for this project. :param base_version: We assert whether or not the base_version is also the - highest version in the resulting "is_highest" value. + highest version in the resulting "is_highest" value. """ highest_version_obj, highest_version_comparable = highest_version( project.versions.public().filter(active=True))