We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Versions aren't deleted if there is one with the same commit
If we create two versions pointing to the same commit (say, master and a tag on master). If we delete the tag, when syncing the repositories that tag isn't deleted from the versions list (because master has the same commit).

This is also seeing when creating two tags that point to the same commit.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `readthedocs/restapi/utils.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 """Utility functions that are used by both views and celery tasks."""
3 
4 from __future__ import (
5     absolute_import, division, print_function, unicode_literals)
6 
7 import hashlib
8 import logging
9 
10 from rest_framework.pagination import PageNumberPagination
11 
12 from readthedocs.builds.constants import (LATEST, LATEST_VERBOSE_NAME,
13                                           NON_REPOSITORY_VERSIONS, STABLE,
14                                           STABLE_VERBOSE_NAME)
15 from readthedocs.builds.models import Version
16 from readthedocs.search.indexes import PageIndex, ProjectIndex, SectionIndex
17 
18 log = logging.getLogger(__name__)
19 
20 
21 def sync_versions(project, versions, type):  # pylint: disable=redefined-builtin
22     """Update the database with the current versions from the repository."""
23     old_version_values = project.versions.filter(type=type).values_list(
24         'verbose_name', 'identifier'
25     )
26     old_versions = dict(old_version_values)
27 
28     # Add new versions
29     added = set()
30     has_user_stable = False
31     has_user_latest = False
32     for version in versions:
33         version_id = version['identifier']
34         version_name = version['verbose_name']
35         if version_name == STABLE_VERBOSE_NAME:
36             has_user_stable = True
37             created_version, created = set_or_create_version(
38                 project=project,
39                 slug=STABLE,
40                 version_id=version_id,
41                 verbose_name=version_name,
42                 type_=type
43             )
44             if created:
45                 added.add(created_version.slug)
46         elif version_name == LATEST_VERBOSE_NAME:
47             has_user_latest = True
48             created_version, created = set_or_create_version(
49                 project=project,
50                 slug=LATEST,
51                 version_id=version_id,
52                 verbose_name=version_name,
53                 type_=type
54             )
55             if created:
56                 added.add(created_version.slug)
57         elif version_name in old_versions:
58             if version_id == old_versions[version_name]:
59                 # Version is correct
60                 continue
61             else:
62                 # Update slug with new identifier
63                 Version.objects.filter(
64                     project=project, verbose_name=version_name).update(
65                         identifier=version_id,
66                         type=type,
67                         machine=False,
68                     )  # noqa
69 
70                 log.info(
71                     '(Sync Versions) Updated Version: [%s=%s] ',
72                     version_name,
73                     version_id,
74                 )
75         else:
76             # New Version
77             created_version = Version.objects.create(
78                 project=project,
79                 type=type,
80                 identifier=version_id,
81                 verbose_name=version_name,
82             )
83             added.add(created_version.slug)
84     if not has_user_stable:
85         stable_version = (
86             project.versions
87             .filter(slug=STABLE, type=type)
88             .first()
89         )
90         if stable_version:
91             # Put back the RTD's stable version
92             stable_version.machine = True
93             stable_version.save()
94     if not has_user_latest:
95         latest_version = (
96             project.versions
97             .filter(slug=LATEST, type=type)
98             .first()
99         )
100         if latest_version:
101             # Put back the RTD's latest version
102             latest_version.machine = True
103             latest_version.identifier = project.get_default_branch()
104             latest_version.verbose_name = LATEST_VERBOSE_NAME
105             latest_version.save()
106     if added:
107         log.info('(Sync Versions) Added Versions: [%s] ', ' '.join(added))
108     return added
109 
110 
111 def set_or_create_version(project, slug, version_id, verbose_name, type_):
112     """Search or create a version and set its machine attribute to false."""
113     version = (
114         project.versions
115         .filter(slug=slug)
116         .first()
117     )
118     if version:
119         version.identifier = version_id
120         version.machine = False
121         version.type = type_
122         version.save()
123     else:
124         created_version = Version.objects.create(
125             project=project,
126             type=type_,
127             identifier=version_id,
128             verbose_name=verbose_name,
129         )
130         return created_version, True
131     return version, False
132 
133 
134 def delete_versions(project, version_data):
135     """Delete all versions not in the current repo."""
136     current_versions = []
137     if 'tags' in version_data:
138         for version in version_data['tags']:
139             current_versions.append(version['identifier'])
140     if 'branches' in version_data:
141         for version in version_data['branches']:
142             current_versions.append(version['identifier'])
143     to_delete_qs = project.versions.all()
144     to_delete_qs = to_delete_qs.exclude(identifier__in=current_versions)
145     to_delete_qs = to_delete_qs.exclude(uploaded=True)
146     to_delete_qs = to_delete_qs.exclude(active=True)
147     to_delete_qs = to_delete_qs.exclude(slug__in=NON_REPOSITORY_VERSIONS)
148 
149     if to_delete_qs.count():
150         ret_val = {obj.slug for obj in to_delete_qs}
151         log.info('(Sync Versions) Deleted Versions: [%s]', ' '.join(ret_val))
152         to_delete_qs.delete()
153         return ret_val
154     return set()
155 
156 
157 def index_search_request(
158         version, page_list, commit, project_scale, page_scale, section=True,
159         delete=True):
160     """
161     Update search indexes with build output JSON.
162 
163     In order to keep sub-projects all indexed on the same shard, indexes will be
164     updated using the parent project's slug as the routing value.
165     """
166     # TODO refactor this function
167     # pylint: disable=too-many-locals
168     project = version.project
169 
170     log_msg = ' '.join([page['path'] for page in page_list])
171     log.info(
172         'Updating search index: project=%s pages=[%s]',
173         project.slug,
174         log_msg,
175     )
176 
177     project_obj = ProjectIndex()
178     project_obj.index_document(
179         data={
180             'id': project.pk,
181             'name': project.name,
182             'slug': project.slug,
183             'description': project.description,
184             'lang': project.language,
185             'author': [user.username for user in project.users.all()],
186             'url': project.get_absolute_url(),
187             'tags': None,
188             'weight': project_scale,
189         })
190 
191     page_obj = PageIndex()
192     section_obj = SectionIndex()
193     index_list = []
194     section_index_list = []
195     routes = [project.slug]
196     routes.extend([p.parent.slug for p in project.superprojects.all()])
197     for page in page_list:
198         log.debug('Indexing page: %s:%s', project.slug, page['path'])
199         to_hash = '-'.join([project.slug, version.slug, page['path']])
200         page_id = hashlib.md5(to_hash.encode('utf-8')).hexdigest()
201         index_list.append({
202             'id': page_id,
203             'project': project.slug,
204             'version': version.slug,
205             'path': page['path'],
206             'title': page['title'],
207             'headers': page['headers'],
208             'content': page['content'],
209             'taxonomy': None,
210             'commit': commit,
211             'weight': page_scale + project_scale,
212         })
213         if section:
214             for sect in page['sections']:
215                 id_to_hash = '-'.join([
216                     project.slug,
217                     version.slug,
218                     page['path'],
219                     sect['id'],
220                 ])
221                 section_index_list.append({
222                     'id': (hashlib.md5(id_to_hash.encode('utf-8')).hexdigest()),
223                     'project': project.slug,
224                     'version': version.slug,
225                     'path': page['path'],
226                     'page_id': sect['id'],
227                     'title': sect['title'],
228                     'content': sect['content'],
229                     'weight': page_scale,
230                 })
231             for route in routes:
232                 section_obj.bulk_index(
233                     section_index_list,
234                     parent=page_id,
235                     routing=route,
236                 )
237 
238     for route in routes:
239         page_obj.bulk_index(index_list, parent=project.slug, routing=route)
240 
241     if delete:
242         log.info('Deleting files not in commit: %s', commit)
243         # TODO: AK Make sure this works
244         delete_query = {
245             'query': {
246                 'bool': {
247                     'must': [
248                         {
249                             'term': {
250                                 'project': project.slug,
251                             },
252                         },
253                         {
254                             'term': {
255                                 'version': version.slug,
256                             },
257                         },
258                     ],
259                     'must_not': {
260                         'term': {
261                             'commit': commit,
262                         },
263                     },
264                 },
265             },
266         }
267         page_obj.delete_document(body=delete_query)
268 
269 
270 class RemoteOrganizationPagination(PageNumberPagination):
271     page_size = 25
272 
273 
274 class RemoteProjectPagination(PageNumberPagination):
275     page_size = 15
276 
277 
278 class ProjectPagination(PageNumberPagination):
279     page_size = 100
280     max_page_size = 1000
281
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/readthedocs/restapi/utils.py b/readthedocs/restapi/utils.py
--- a/readthedocs/restapi/utils.py
+++ b/readthedocs/restapi/utils.py
@@ -2,16 +2,26 @@
 """Utility functions that are used by both views and celery tasks."""
 
 from __future__ import (
-    absolute_import, division, print_function, unicode_literals)
+    absolute_import,
+    division,
+    print_function,
+    unicode_literals,
+)
 
 import hashlib
 import logging
 
 from rest_framework.pagination import PageNumberPagination
 
-from readthedocs.builds.constants import (LATEST, LATEST_VERBOSE_NAME,
-                                          NON_REPOSITORY_VERSIONS, STABLE,
-                                          STABLE_VERBOSE_NAME)
+from readthedocs.builds.constants import (
+    BRANCH,
+    LATEST,
+    LATEST_VERBOSE_NAME,
+    NON_REPOSITORY_VERSIONS,
+    STABLE,
+    STABLE_VERBOSE_NAME,
+    TAG,
+)
 from readthedocs.builds.models import Version
 from readthedocs.search.indexes import PageIndex, ProjectIndex, SectionIndex
 
@@ -133,15 +143,25 @@
 
 def delete_versions(project, version_data):
     """Delete all versions not in the current repo."""
-    current_versions = []
-    if 'tags' in version_data:
-        for version in version_data['tags']:
-            current_versions.append(version['identifier'])
-    if 'branches' in version_data:
-        for version in version_data['branches']:
-            current_versions.append(version['identifier'])
+    # We use verbose_name for tags
+    # because several tags can point to the same identifier.
+    versions_tags = [
+        version['verbose_name']
+        for version in version_data.get('tags', [])
+    ]
+    versions_branches = [
+        version['identifier']
+        for version in version_data.get('branches', [])
+    ]
     to_delete_qs = project.versions.all()
-    to_delete_qs = to_delete_qs.exclude(identifier__in=current_versions)
+    to_delete_qs = to_delete_qs.exclude(
+        type=TAG,
+        verbose_name__in=versions_tags,
+    )
+    to_delete_qs = to_delete_qs.exclude(
+        type=BRANCH,
+        identifier__in=versions_branches,
+    )
     to_delete_qs = to_delete_qs.exclude(uploaded=True)
     to_delete_qs = to_delete_qs.exclude(active=True)
     to_delete_qs = to_delete_qs.exclude(slug__in=NON_REPOSITORY_VERSIONS)

{"golden_diff": "diff --git a/readthedocs/restapi/utils.py b/readthedocs/restapi/utils.py\n--- a/readthedocs/restapi/utils.py\n+++ b/readthedocs/restapi/utils.py\n@@ -2,16 +2,26 @@\n \"\"\"Utility functions that are used by both views and celery tasks.\"\"\"\n \n from __future__ import (\n-    absolute_import, division, print_function, unicode_literals)\n+    absolute_import,\n+    division,\n+    print_function,\n+    unicode_literals,\n+)\n \n import hashlib\n import logging\n \n from rest_framework.pagination import PageNumberPagination\n \n-from readthedocs.builds.constants import (LATEST, LATEST_VERBOSE_NAME,\n-                                          NON_REPOSITORY_VERSIONS, STABLE,\n-                                          STABLE_VERBOSE_NAME)\n+from readthedocs.builds.constants import (\n+    BRANCH,\n+    LATEST,\n+    LATEST_VERBOSE_NAME,\n+    NON_REPOSITORY_VERSIONS,\n+    STABLE,\n+    STABLE_VERBOSE_NAME,\n+    TAG,\n+)\n from readthedocs.builds.models import Version\n from readthedocs.search.indexes import PageIndex, ProjectIndex, SectionIndex\n \n@@ -133,15 +143,25 @@\n \n def delete_versions(project, version_data):\n     \"\"\"Delete all versions not in the current repo.\"\"\"\n-    current_versions = []\n-    if 'tags' in version_data:\n-        for version in version_data['tags']:\n-            current_versions.append(version['identifier'])\n-    if 'branches' in version_data:\n-        for version in version_data['branches']:\n-            current_versions.append(version['identifier'])\n+    # We use verbose_name for tags\n+    # because several tags can point to the same identifier.\n+    versions_tags = [\n+        version['verbose_name']\n+        for version in version_data.get('tags', [])\n+    ]\n+    versions_branches = [\n+        version['identifier']\n+        for version in version_data.get('branches', [])\n+    ]\n     to_delete_qs = project.versions.all()\n-    to_delete_qs = to_delete_qs.exclude(identifier__in=current_versions)\n+    to_delete_qs = to_delete_qs.exclude(\n+        type=TAG,\n+        verbose_name__in=versions_tags,\n+    )\n+    to_delete_qs = to_delete_qs.exclude(\n+        type=BRANCH,\n+        identifier__in=versions_branches,\n+    )\n     to_delete_qs = to_delete_qs.exclude(uploaded=True)\n     to_delete_qs = to_delete_qs.exclude(active=True)\n     to_delete_qs = to_delete_qs.exclude(slug__in=NON_REPOSITORY_VERSIONS)\n", "issue": "Versions aren't deleted if there is one with the same commit\nIf we create two versions pointing to the same commit (say, master and a tag on master). If we delete the tag, when syncing the repositories that tag isn't deleted from the versions list (because master has the same commit).\r\n\r\nThis is also seeing when creating two tags that point to the same commit.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Utility functions that are used by both views and celery tasks.\"\"\"\n\nfrom __future__ import (\n    absolute_import, division, print_function, unicode_literals)\n\nimport hashlib\nimport logging\n\nfrom rest_framework.pagination import PageNumberPagination\n\nfrom readthedocs.builds.constants import (LATEST, LATEST_VERBOSE_NAME,\n                                          NON_REPOSITORY_VERSIONS, STABLE,\n                                          STABLE_VERBOSE_NAME)\nfrom readthedocs.builds.models import Version\nfrom readthedocs.search.indexes import PageIndex, ProjectIndex, SectionIndex\n\nlog = logging.getLogger(__name__)\n\n\ndef sync_versions(project, versions, type):  # pylint: disable=redefined-builtin\n    \"\"\"Update the database with the current versions from the repository.\"\"\"\n    old_version_values = project.versions.filter(type=type).values_list(\n        'verbose_name', 'identifier'\n    )\n    old_versions = dict(old_version_values)\n\n    # Add new versions\n    added = set()\n    has_user_stable = False\n    has_user_latest = False\n    for version in versions:\n        version_id = version['identifier']\n        version_name = version['verbose_name']\n        if version_name == STABLE_VERBOSE_NAME:\n            has_user_stable = True\n            created_version, created = set_or_create_version(\n                project=project,\n                slug=STABLE,\n                version_id=version_id,\n                verbose_name=version_name,\n                type_=type\n            )\n            if created:\n                added.add(created_version.slug)\n        elif version_name == LATEST_VERBOSE_NAME:\n            has_user_latest = True\n            created_version, created = set_or_create_version(\n                project=project,\n                slug=LATEST,\n                version_id=version_id,\n                verbose_name=version_name,\n                type_=type\n            )\n            if created:\n                added.add(created_version.slug)\n        elif version_name in old_versions:\n            if version_id == old_versions[version_name]:\n                # Version is correct\n                continue\n            else:\n                # Update slug with new identifier\n                Version.objects.filter(\n                    project=project, verbose_name=version_name).update(\n                        identifier=version_id,\n                        type=type,\n                        machine=False,\n                    )  # noqa\n\n                log.info(\n                    '(Sync Versions) Updated Version: [%s=%s] ',\n                    version_name,\n                    version_id,\n                )\n        else:\n            # New Version\n            created_version = Version.objects.create(\n                project=project,\n                type=type,\n                identifier=version_id,\n                verbose_name=version_name,\n            )\n            added.add(created_version.slug)\n    if not has_user_stable:\n        stable_version = (\n            project.versions\n            .filter(slug=STABLE, type=type)\n            .first()\n        )\n        if stable_version:\n            # Put back the RTD's stable version\n            stable_version.machine = True\n            stable_version.save()\n    if not has_user_latest:\n        latest_version = (\n            project.versions\n            .filter(slug=LATEST, type=type)\n            .first()\n        )\n        if latest_version:\n            # Put back the RTD's latest version\n            latest_version.machine = True\n            latest_version.identifier = project.get_default_branch()\n            latest_version.verbose_name = LATEST_VERBOSE_NAME\n            latest_version.save()\n    if added:\n        log.info('(Sync Versions) Added Versions: [%s] ', ' '.join(added))\n    return added\n\n\ndef set_or_create_version(project, slug, version_id, verbose_name, type_):\n    \"\"\"Search or create a version and set its machine attribute to false.\"\"\"\n    version = (\n        project.versions\n        .filter(slug=slug)\n        .first()\n    )\n    if version:\n        version.identifier = version_id\n        version.machine = False\n        version.type = type_\n        version.save()\n    else:\n        created_version = Version.objects.create(\n            project=project,\n            type=type_,\n            identifier=version_id,\n            verbose_name=verbose_name,\n        )\n        return created_version, True\n    return version, False\n\n\ndef delete_versions(project, version_data):\n    \"\"\"Delete all versions not in the current repo.\"\"\"\n    current_versions = []\n    if 'tags' in version_data:\n        for version in version_data['tags']:\n            current_versions.append(version['identifier'])\n    if 'branches' in version_data:\n        for version in version_data['branches']:\n            current_versions.append(version['identifier'])\n    to_delete_qs = project.versions.all()\n    to_delete_qs = to_delete_qs.exclude(identifier__in=current_versions)\n    to_delete_qs = to_delete_qs.exclude(uploaded=True)\n    to_delete_qs = to_delete_qs.exclude(active=True)\n    to_delete_qs = to_delete_qs.exclude(slug__in=NON_REPOSITORY_VERSIONS)\n\n    if to_delete_qs.count():\n        ret_val = {obj.slug for obj in to_delete_qs}\n        log.info('(Sync Versions) Deleted Versions: [%s]', ' '.join(ret_val))\n        to_delete_qs.delete()\n        return ret_val\n    return set()\n\n\ndef index_search_request(\n        version, page_list, commit, project_scale, page_scale, section=True,\n        delete=True):\n    \"\"\"\n    Update search indexes with build output JSON.\n\n    In order to keep sub-projects all indexed on the same shard, indexes will be\n    updated using the parent project's slug as the routing value.\n    \"\"\"\n    # TODO refactor this function\n    # pylint: disable=too-many-locals\n    project = version.project\n\n    log_msg = ' '.join([page['path'] for page in page_list])\n    log.info(\n        'Updating search index: project=%s pages=[%s]',\n        project.slug,\n        log_msg,\n    )\n\n    project_obj = ProjectIndex()\n    project_obj.index_document(\n        data={\n            'id': project.pk,\n            'name': project.name,\n            'slug': project.slug,\n            'description': project.description,\n            'lang': project.language,\n            'author': [user.username for user in project.users.all()],\n            'url': project.get_absolute_url(),\n            'tags': None,\n            'weight': project_scale,\n        })\n\n    page_obj = PageIndex()\n    section_obj = SectionIndex()\n    index_list = []\n    section_index_list = []\n    routes = [project.slug]\n    routes.extend([p.parent.slug for p in project.superprojects.all()])\n    for page in page_list:\n        log.debug('Indexing page: %s:%s', project.slug, page['path'])\n        to_hash = '-'.join([project.slug, version.slug, page['path']])\n        page_id = hashlib.md5(to_hash.encode('utf-8')).hexdigest()\n        index_list.append({\n            'id': page_id,\n            'project': project.slug,\n            'version': version.slug,\n            'path': page['path'],\n            'title': page['title'],\n            'headers': page['headers'],\n            'content': page['content'],\n            'taxonomy': None,\n            'commit': commit,\n            'weight': page_scale + project_scale,\n        })\n        if section:\n            for sect in page['sections']:\n                id_to_hash = '-'.join([\n                    project.slug,\n                    version.slug,\n                    page['path'],\n                    sect['id'],\n                ])\n                section_index_list.append({\n                    'id': (hashlib.md5(id_to_hash.encode('utf-8')).hexdigest()),\n                    'project': project.slug,\n                    'version': version.slug,\n                    'path': page['path'],\n                    'page_id': sect['id'],\n                    'title': sect['title'],\n                    'content': sect['content'],\n                    'weight': page_scale,\n                })\n            for route in routes:\n                section_obj.bulk_index(\n                    section_index_list,\n                    parent=page_id,\n                    routing=route,\n                )\n\n    for route in routes:\n        page_obj.bulk_index(index_list, parent=project.slug, routing=route)\n\n    if delete:\n        log.info('Deleting files not in commit: %s', commit)\n        # TODO: AK Make sure this works\n        delete_query = {\n            'query': {\n                'bool': {\n                    'must': [\n                        {\n                            'term': {\n                                'project': project.slug,\n                            },\n                        },\n                        {\n                            'term': {\n                                'version': version.slug,\n                            },\n                        },\n                    ],\n                    'must_not': {\n                        'term': {\n                            'commit': commit,\n                        },\n                    },\n                },\n            },\n        }\n        page_obj.delete_document(body=delete_query)\n\n\nclass RemoteOrganizationPagination(PageNumberPagination):\n    page_size = 25\n\n\nclass RemoteProjectPagination(PageNumberPagination):\n    page_size = 15\n\n\nclass ProjectPagination(PageNumberPagination):\n    page_size = 100\n    max_page_size = 1000\n", "path": "readthedocs/restapi/utils.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Utility functions that are used by both views and celery tasks.\"\"\"\n\nfrom __future__ import (\n    absolute_import,\n    division,\n    print_function,\n    unicode_literals,\n)\n\nimport hashlib\nimport logging\n\nfrom rest_framework.pagination import PageNumberPagination\n\nfrom readthedocs.builds.constants import (\n    BRANCH,\n    LATEST,\n    LATEST_VERBOSE_NAME,\n    NON_REPOSITORY_VERSIONS,\n    STABLE,\n    STABLE_VERBOSE_NAME,\n    TAG,\n)\nfrom readthedocs.builds.models import Version\nfrom readthedocs.search.indexes import PageIndex, ProjectIndex, SectionIndex\n\nlog = logging.getLogger(__name__)\n\n\ndef sync_versions(project, versions, type):  # pylint: disable=redefined-builtin\n    \"\"\"Update the database with the current versions from the repository.\"\"\"\n    old_version_values = project.versions.filter(type=type).values_list(\n        'verbose_name', 'identifier'\n    )\n    old_versions = dict(old_version_values)\n\n    # Add new versions\n    added = set()\n    has_user_stable = False\n    has_user_latest = False\n    for version in versions:\n        version_id = version['identifier']\n        version_name = version['verbose_name']\n        if version_name == STABLE_VERBOSE_NAME:\n            has_user_stable = True\n            created_version, created = set_or_create_version(\n                project=project,\n                slug=STABLE,\n                version_id=version_id,\n                verbose_name=version_name,\n                type_=type\n            )\n            if created:\n                added.add(created_version.slug)\n        elif version_name == LATEST_VERBOSE_NAME:\n            has_user_latest = True\n            created_version, created = set_or_create_version(\n                project=project,\n                slug=LATEST,\n                version_id=version_id,\n                verbose_name=version_name,\n                type_=type\n            )\n            if created:\n                added.add(created_version.slug)\n        elif version_name in old_versions:\n            if version_id == old_versions[version_name]:\n                # Version is correct\n                continue\n            else:\n                # Update slug with new identifier\n                Version.objects.filter(\n                    project=project, verbose_name=version_name).update(\n                        identifier=version_id,\n                        type=type,\n                        machine=False,\n                    )  # noqa\n\n                log.info(\n                    '(Sync Versions) Updated Version: [%s=%s] ',\n                    version_name,\n                    version_id,\n                )\n        else:\n            # New Version\n            created_version = Version.objects.create(\n                project=project,\n                type=type,\n                identifier=version_id,\n                verbose_name=version_name,\n            )\n            added.add(created_version.slug)\n    if not has_user_stable:\n        stable_version = (\n            project.versions\n            .filter(slug=STABLE, type=type)\n            .first()\n        )\n        if stable_version:\n            # Put back the RTD's stable version\n            stable_version.machine = True\n            stable_version.save()\n    if not has_user_latest:\n        latest_version = (\n            project.versions\n            .filter(slug=LATEST, type=type)\n            .first()\n        )\n        if latest_version:\n            # Put back the RTD's latest version\n            latest_version.machine = True\n            latest_version.identifier = project.get_default_branch()\n            latest_version.verbose_name = LATEST_VERBOSE_NAME\n            latest_version.save()\n    if added:\n        log.info('(Sync Versions) Added Versions: [%s] ', ' '.join(added))\n    return added\n\n\ndef set_or_create_version(project, slug, version_id, verbose_name, type_):\n    \"\"\"Search or create a version and set its machine attribute to false.\"\"\"\n    version = (\n        project.versions\n        .filter(slug=slug)\n        .first()\n    )\n    if version:\n        version.identifier = version_id\n        version.machine = False\n        version.type = type_\n        version.save()\n    else:\n        created_version = Version.objects.create(\n            project=project,\n            type=type_,\n            identifier=version_id,\n            verbose_name=verbose_name,\n        )\n        return created_version, True\n    return version, False\n\n\ndef delete_versions(project, version_data):\n    \"\"\"Delete all versions not in the current repo.\"\"\"\n    # We use verbose_name for tags\n    # because several tags can point to the same identifier.\n    versions_tags = [\n        version['verbose_name']\n        for version in version_data.get('tags', [])\n    ]\n    versions_branches = [\n        version['identifier']\n        for version in version_data.get('branches', [])\n    ]\n    to_delete_qs = project.versions.all()\n    to_delete_qs = to_delete_qs.exclude(\n        type=TAG,\n        verbose_name__in=versions_tags,\n    )\n    to_delete_qs = to_delete_qs.exclude(\n        type=BRANCH,\n        identifier__in=versions_branches,\n    )\n    to_delete_qs = to_delete_qs.exclude(uploaded=True)\n    to_delete_qs = to_delete_qs.exclude(active=True)\n    to_delete_qs = to_delete_qs.exclude(slug__in=NON_REPOSITORY_VERSIONS)\n\n    if to_delete_qs.count():\n        ret_val = {obj.slug for obj in to_delete_qs}\n        log.info('(Sync Versions) Deleted Versions: [%s]', ' '.join(ret_val))\n        to_delete_qs.delete()\n        return ret_val\n    return set()\n\n\ndef index_search_request(\n        version, page_list, commit, project_scale, page_scale, section=True,\n        delete=True):\n    \"\"\"\n    Update search indexes with build output JSON.\n\n    In order to keep sub-projects all indexed on the same shard, indexes will be\n    updated using the parent project's slug as the routing value.\n    \"\"\"\n    # TODO refactor this function\n    # pylint: disable=too-many-locals\n    project = version.project\n\n    log_msg = ' '.join([page['path'] for page in page_list])\n    log.info(\n        'Updating search index: project=%s pages=[%s]',\n        project.slug,\n        log_msg,\n    )\n\n    project_obj = ProjectIndex()\n    project_obj.index_document(\n        data={\n            'id': project.pk,\n            'name': project.name,\n            'slug': project.slug,\n            'description': project.description,\n            'lang': project.language,\n            'author': [user.username for user in project.users.all()],\n            'url': project.get_absolute_url(),\n            'tags': None,\n            'weight': project_scale,\n        })\n\n    page_obj = PageIndex()\n    section_obj = SectionIndex()\n    index_list = []\n    section_index_list = []\n    routes = [project.slug]\n    routes.extend([p.parent.slug for p in project.superprojects.all()])\n    for page in page_list:\n        log.debug('Indexing page: %s:%s', project.slug, page['path'])\n        to_hash = '-'.join([project.slug, version.slug, page['path']])\n        page_id = hashlib.md5(to_hash.encode('utf-8')).hexdigest()\n        index_list.append({\n            'id': page_id,\n            'project': project.slug,\n            'version': version.slug,\n            'path': page['path'],\n            'title': page['title'],\n            'headers': page['headers'],\n            'content': page['content'],\n            'taxonomy': None,\n            'commit': commit,\n            'weight': page_scale + project_scale,\n        })\n        if section:\n            for sect in page['sections']:\n                id_to_hash = '-'.join([\n                    project.slug,\n                    version.slug,\n                    page['path'],\n                    sect['id'],\n                ])\n                section_index_list.append({\n                    'id': (hashlib.md5(id_to_hash.encode('utf-8')).hexdigest()),\n                    'project': project.slug,\n                    'version': version.slug,\n                    'path': page['path'],\n                    'page_id': sect['id'],\n                    'title': sect['title'],\n                    'content': sect['content'],\n                    'weight': page_scale,\n                })\n            for route in routes:\n                section_obj.bulk_index(\n                    section_index_list,\n                    parent=page_id,\n                    routing=route,\n                )\n\n    for route in routes:\n        page_obj.bulk_index(index_list, parent=project.slug, routing=route)\n\n    if delete:\n        log.info('Deleting files not in commit: %s', commit)\n        # TODO: AK Make sure this works\n        delete_query = {\n            'query': {\n                'bool': {\n                    'must': [\n                        {\n                            'term': {\n                                'project': project.slug,\n                            },\n                        },\n                        {\n                            'term': {\n                                'version': version.slug,\n                            },\n                        },\n                    ],\n                    'must_not': {\n                        'term': {\n                            'commit': commit,\n                        },\n                    },\n                },\n            },\n        }\n        page_obj.delete_document(body=delete_query)\n\n\nclass RemoteOrganizationPagination(PageNumberPagination):\n    page_size = 25\n\n\nclass RemoteProjectPagination(PageNumberPagination):\n    page_size = 15\n\n\nclass ProjectPagination(PageNumberPagination):\n    page_size = 100\n    max_page_size = 1000\n", "path": "readthedocs/restapi/utils.py"}]}