We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Provide an API to query the build status by commit
In order to do a check before release that everything is ok, I would like to have a way to obtain the current build status for a given commit. So, in addition to:

```
GET /api/v1/build/{id}/
```

also have this:

```
GET /api/v1/commit/{sha1}/
```

or 

```
GET /api/v1/{user}/{project}/commit/{sha1}/
```

Is this possible right now?


--- 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/views/model_views.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 """Endpoints for listing Projects, Versions, Builds, etc."""
3 
4 from __future__ import (
5     absolute_import, division, print_function, unicode_literals)
6 
7 import logging
8 
9 from allauth.socialaccount.models import SocialAccount
10 from builtins import str
11 from django.shortcuts import get_object_or_404
12 from django.template.loader import render_to_string
13 from rest_framework import decorators, permissions, status, viewsets
14 from rest_framework.decorators import detail_route
15 from rest_framework.renderers import BaseRenderer, JSONRenderer
16 from rest_framework.response import Response
17 
18 from readthedocs.builds.constants import BRANCH, TAG
19 from readthedocs.builds.models import Build, BuildCommandResult, Version
20 from readthedocs.core.utils import trigger_build
21 from readthedocs.core.utils.extend import SettingsOverrideObject
22 from readthedocs.oauth.models import RemoteOrganization, RemoteRepository
23 from readthedocs.oauth.services import GitHubService, registry
24 from readthedocs.projects.models import Domain, EmailHook, Project
25 from readthedocs.projects.version_handling import determine_stable_version
26 
27 from .. import utils as api_utils
28 from ..permissions import (
29     APIPermission, APIRestrictedPermission, IsOwner, RelatedProjectIsOwner)
30 from ..serializers import (
31     BuildAdminSerializer, BuildCommandSerializer, BuildSerializer,
32     DomainSerializer, ProjectAdminSerializer, ProjectSerializer,
33     RemoteOrganizationSerializer, RemoteRepositorySerializer,
34     SocialAccountSerializer, VersionAdminSerializer, VersionSerializer)
35 
36 log = logging.getLogger(__name__)
37 
38 
39 class PlainTextBuildRenderer(BaseRenderer):
40 
41     """
42     Custom renderer for text/plain format.
43 
44     charset is 'utf-8' by default.
45     """
46 
47     media_type = 'text/plain'
48     format = 'txt'
49 
50     def render(self, data, accepted_media_type=None, renderer_context=None):
51         renderer_context = renderer_context or {}
52         response = renderer_context.get('response')
53         if not response or response.exception:
54             return data.get('detail', '').encode(self.charset)
55         data = render_to_string(
56             'restapi/log.txt', {'build': data}
57         )
58         return data.encode(self.charset)
59 
60 
61 class UserSelectViewSet(viewsets.ModelViewSet):
62 
63     """
64     View set that varies serializer class based on request user credentials.
65 
66     Viewsets using this class should have an attribute `admin_serializer_class`,
67     which is a serializer that might have more fields that only admin/staff
68     users require. If the user is staff, this class will be returned instead.
69     """
70 
71     def get_serializer_class(self):
72         try:
73             if (self.request.user.is_staff and
74                     self.admin_serializer_class is not None):
75                 return self.admin_serializer_class
76         except AttributeError:
77             pass
78         return self.serializer_class
79 
80     def get_queryset(self):
81         """Use our API manager method to determine authorization on queryset."""
82         return self.model.objects.api(self.request.user)
83 
84 
85 class ProjectViewSet(UserSelectViewSet):
86 
87     """List, filter, etc, Projects."""
88 
89     permission_classes = [APIPermission]
90     renderer_classes = (JSONRenderer,)
91     serializer_class = ProjectSerializer
92     admin_serializer_class = ProjectAdminSerializer
93     model = Project
94     pagination_class = api_utils.ProjectPagination
95     filter_fields = ('slug',)
96 
97     @decorators.detail_route()
98     def valid_versions(self, request, **kwargs):
99         """Maintain state of versions that are wanted."""
100         project = get_object_or_404(
101             Project.objects.api(request.user), pk=kwargs['pk'])
102         if (not project.num_major or not project.num_minor or
103                 not project.num_point):
104             return Response(
105                 {
106                     'error': 'Project does not support point version control',
107                 },
108                 status=status.HTTP_400_BAD_REQUEST,
109             )
110         version_strings = project.supported_versions()
111         # Disable making old versions inactive for now.
112         # project.versions.exclude(verbose_name__in=version_strings).update(active=False)
113         project.versions.filter(verbose_name__in=version_strings).update(
114             active=True,
115         )
116         return Response({
117             'flat': version_strings,
118         })
119 
120     @detail_route()
121     def translations(self, *_, **__):
122         translations = self.get_object().translations.all()
123         return Response({
124             'translations': ProjectSerializer(translations, many=True).data,
125         })
126 
127     @detail_route()
128     def subprojects(self, request, **kwargs):
129         project = get_object_or_404(
130             Project.objects.api(request.user), pk=kwargs['pk'])
131         rels = project.subprojects.all()
132         children = [rel.child for rel in rels]
133         return Response({
134             'subprojects': ProjectSerializer(children, many=True).data,
135         })
136 
137     @detail_route()
138     def active_versions(self, request, **kwargs):
139         project = get_object_or_404(
140             Project.objects.api(request.user), pk=kwargs['pk'])
141         versions = project.versions.filter(active=True)
142         return Response({
143             'versions': VersionSerializer(versions, many=True).data,
144         })
145 
146     @decorators.detail_route(permission_classes=[permissions.IsAdminUser])
147     def token(self, request, **kwargs):
148         project = get_object_or_404(
149             Project.objects.api(request.user), pk=kwargs['pk'])
150         token = GitHubService.get_token_for_project(project, force_local=True)
151         return Response({
152             'token': token,
153         })
154 
155     @decorators.detail_route()
156     def canonical_url(self, request, **kwargs):
157         project = get_object_or_404(
158             Project.objects.api(request.user), pk=kwargs['pk'])
159         return Response({
160             'url': project.get_docs_url(),
161         })
162 
163     @decorators.detail_route(
164         permission_classes=[permissions.IsAdminUser], methods=['post'])
165     def sync_versions(self, request, **kwargs):  # noqa: D205
166         """
167         Sync the version data in the repo (on the build server).
168 
169         Version data in the repo is synced with what we have in the database.
170 
171         :returns: the identifiers for the versions that have been deleted.
172         """
173         project = get_object_or_404(
174             Project.objects.api(request.user), pk=kwargs['pk'])
175 
176         # If the currently highest non-prerelease version is active, then make
177         # the new latest version active as well.
178         old_highest_version = determine_stable_version(project.versions.all())
179         if old_highest_version is not None:
180             activate_new_stable = old_highest_version.active
181         else:
182             activate_new_stable = False
183 
184         try:
185             # Update All Versions
186             data = request.data
187             added_versions = set()
188             if 'tags' in data:
189                 ret_set = api_utils.sync_versions(
190                     project=project, versions=data['tags'], type=TAG)
191                 added_versions.update(ret_set)
192             if 'branches' in data:
193                 ret_set = api_utils.sync_versions(
194                     project=project, versions=data['branches'], type=BRANCH)
195                 added_versions.update(ret_set)
196             deleted_versions = api_utils.delete_versions(project, data)
197         except Exception as e:
198             log.exception('Sync Versions Error')
199             return Response(
200                 {
201                     'error': str(e),
202                 },
203                 status=status.HTTP_400_BAD_REQUEST,
204             )
205 
206         promoted_version = project.update_stable_version()
207         if promoted_version:
208             new_stable = project.get_stable_version()
209             log.info(
210                 'Triggering new stable build: {project}:{version}'.format(
211                     project=project.slug,
212                     version=new_stable.identifier,
213                 ))
214             trigger_build(project=project, version=new_stable)
215 
216             # Marking the tag that is considered the new stable version as
217             # active and building it if it was just added.
218             if (activate_new_stable and
219                     promoted_version.slug in added_versions):
220                 promoted_version.active = True
221                 promoted_version.save()
222                 trigger_build(project=project, version=promoted_version)
223 
224         return Response({
225             'added_versions': added_versions,
226             'deleted_versions': deleted_versions,
227         })
228 
229 
230 class VersionViewSet(UserSelectViewSet):
231 
232     permission_classes = [APIRestrictedPermission]
233     renderer_classes = (JSONRenderer,)
234     serializer_class = VersionSerializer
235     admin_serializer_class = VersionAdminSerializer
236     model = Version
237     filter_fields = ('active', 'project__slug',)
238 
239 
240 class BuildViewSetBase(UserSelectViewSet):
241     permission_classes = [APIRestrictedPermission]
242     renderer_classes = (JSONRenderer, PlainTextBuildRenderer)
243     serializer_class = BuildSerializer
244     admin_serializer_class = BuildAdminSerializer
245     model = Build
246     filter_fields = ('project__slug',)
247 
248 
249 class BuildViewSet(SettingsOverrideObject):
250 
251     """A pluggable class to allow for build cold storage."""
252 
253     _default_class = BuildViewSetBase
254 
255 
256 class BuildCommandViewSet(UserSelectViewSet):
257     permission_classes = [APIRestrictedPermission]
258     renderer_classes = (JSONRenderer,)
259     serializer_class = BuildCommandSerializer
260     model = BuildCommandResult
261 
262 
263 class NotificationViewSet(viewsets.ReadOnlyModelViewSet):
264     permission_classes = (permissions.IsAuthenticated, RelatedProjectIsOwner)
265     renderer_classes = (JSONRenderer,)
266     model = EmailHook
267 
268     def get_queryset(self):
269         return self.model.objects.api(self.request.user)
270 
271 
272 class DomainViewSet(UserSelectViewSet):
273     permission_classes = [APIRestrictedPermission]
274     renderer_classes = (JSONRenderer,)
275     serializer_class = DomainSerializer
276     model = Domain
277 
278 
279 class RemoteOrganizationViewSet(viewsets.ReadOnlyModelViewSet):
280     permission_classes = [IsOwner]
281     renderer_classes = (JSONRenderer,)
282     serializer_class = RemoteOrganizationSerializer
283     model = RemoteOrganization
284     pagination_class = api_utils.RemoteOrganizationPagination
285 
286     def get_queryset(self):
287         return (
288             self.model.objects.api(self.request.user).filter(
289                 account__provider__in=[
290                     service.adapter.provider_id for service in registry
291                 ]))
292 
293 
294 class RemoteRepositoryViewSet(viewsets.ReadOnlyModelViewSet):
295     permission_classes = [IsOwner]
296     renderer_classes = (JSONRenderer,)
297     serializer_class = RemoteRepositorySerializer
298     model = RemoteRepository
299     pagination_class = api_utils.RemoteProjectPagination
300 
301     def get_queryset(self):
302         query = self.model.objects.api(self.request.user)
303         org = self.request.query_params.get('org', None)
304         if org is not None:
305             query = query.filter(organization__pk=org)
306 
307         own = self.request.query_params.get('own', None)
308         if own is not None:
309             query = query.filter(
310                 account__provider=own,
311                 organization=None,
312             )
313 
314         query = query.filter(
315             account__provider__in=[
316                 service.adapter.provider_id for service in registry
317             ])
318         return query
319 
320 
321 class SocialAccountViewSet(viewsets.ReadOnlyModelViewSet):
322     permission_classes = [IsOwner]
323     renderer_classes = (JSONRenderer,)
324     serializer_class = SocialAccountSerializer
325     model = SocialAccount
326 
327     def get_queryset(self):
328         return self.model.objects.filter(user=self.request.user.pk)
329
```
--- 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/views/model_views.py b/readthedocs/restapi/views/model_views.py
--- a/readthedocs/restapi/views/model_views.py
+++ b/readthedocs/restapi/views/model_views.py
@@ -243,7 +243,7 @@
     serializer_class = BuildSerializer
     admin_serializer_class = BuildAdminSerializer
     model = Build
-    filter_fields = ('project__slug',)
+    filter_fields = ('project__slug', 'commit')
 
 
 class BuildViewSet(SettingsOverrideObject):

{"golden_diff": "diff --git a/readthedocs/restapi/views/model_views.py b/readthedocs/restapi/views/model_views.py\n--- a/readthedocs/restapi/views/model_views.py\n+++ b/readthedocs/restapi/views/model_views.py\n@@ -243,7 +243,7 @@\n     serializer_class = BuildSerializer\n     admin_serializer_class = BuildAdminSerializer\n     model = Build\n-    filter_fields = ('project__slug',)\n+    filter_fields = ('project__slug', 'commit')\n \n \n class BuildViewSet(SettingsOverrideObject):\n", "issue": "Provide an API to query the build status by commit\nIn order to do a check before release that everything is ok, I would like to have a way to obtain the current build status for a given commit. So, in addition to:\n\n```\nGET /api/v1/build/{id}/\n```\n\nalso have this:\n\n```\nGET /api/v1/commit/{sha1}/\n```\n\nor \n\n```\nGET /api/v1/{user}/{project}/commit/{sha1}/\n```\n\nIs this possible right now?\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Endpoints for listing Projects, Versions, Builds, etc.\"\"\"\n\nfrom __future__ import (\n    absolute_import, division, print_function, unicode_literals)\n\nimport logging\n\nfrom allauth.socialaccount.models import SocialAccount\nfrom builtins import str\nfrom django.shortcuts import get_object_or_404\nfrom django.template.loader import render_to_string\nfrom rest_framework import decorators, permissions, status, viewsets\nfrom rest_framework.decorators import detail_route\nfrom rest_framework.renderers import BaseRenderer, JSONRenderer\nfrom rest_framework.response import Response\n\nfrom readthedocs.builds.constants import BRANCH, TAG\nfrom readthedocs.builds.models import Build, BuildCommandResult, Version\nfrom readthedocs.core.utils import trigger_build\nfrom readthedocs.core.utils.extend import SettingsOverrideObject\nfrom readthedocs.oauth.models import RemoteOrganization, RemoteRepository\nfrom readthedocs.oauth.services import GitHubService, registry\nfrom readthedocs.projects.models import Domain, EmailHook, Project\nfrom readthedocs.projects.version_handling import determine_stable_version\n\nfrom .. import utils as api_utils\nfrom ..permissions import (\n    APIPermission, APIRestrictedPermission, IsOwner, RelatedProjectIsOwner)\nfrom ..serializers import (\n    BuildAdminSerializer, BuildCommandSerializer, BuildSerializer,\n    DomainSerializer, ProjectAdminSerializer, ProjectSerializer,\n    RemoteOrganizationSerializer, RemoteRepositorySerializer,\n    SocialAccountSerializer, VersionAdminSerializer, VersionSerializer)\n\nlog = logging.getLogger(__name__)\n\n\nclass PlainTextBuildRenderer(BaseRenderer):\n\n    \"\"\"\n    Custom renderer for text/plain format.\n\n    charset is 'utf-8' by default.\n    \"\"\"\n\n    media_type = 'text/plain'\n    format = 'txt'\n\n    def render(self, data, accepted_media_type=None, renderer_context=None):\n        renderer_context = renderer_context or {}\n        response = renderer_context.get('response')\n        if not response or response.exception:\n            return data.get('detail', '').encode(self.charset)\n        data = render_to_string(\n            'restapi/log.txt', {'build': data}\n        )\n        return data.encode(self.charset)\n\n\nclass UserSelectViewSet(viewsets.ModelViewSet):\n\n    \"\"\"\n    View set that varies serializer class based on request user credentials.\n\n    Viewsets using this class should have an attribute `admin_serializer_class`,\n    which is a serializer that might have more fields that only admin/staff\n    users require. If the user is staff, this class will be returned instead.\n    \"\"\"\n\n    def get_serializer_class(self):\n        try:\n            if (self.request.user.is_staff and\n                    self.admin_serializer_class is not None):\n                return self.admin_serializer_class\n        except AttributeError:\n            pass\n        return self.serializer_class\n\n    def get_queryset(self):\n        \"\"\"Use our API manager method to determine authorization on queryset.\"\"\"\n        return self.model.objects.api(self.request.user)\n\n\nclass ProjectViewSet(UserSelectViewSet):\n\n    \"\"\"List, filter, etc, Projects.\"\"\"\n\n    permission_classes = [APIPermission]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = ProjectSerializer\n    admin_serializer_class = ProjectAdminSerializer\n    model = Project\n    pagination_class = api_utils.ProjectPagination\n    filter_fields = ('slug',)\n\n    @decorators.detail_route()\n    def valid_versions(self, request, **kwargs):\n        \"\"\"Maintain state of versions that are wanted.\"\"\"\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        if (not project.num_major or not project.num_minor or\n                not project.num_point):\n            return Response(\n                {\n                    'error': 'Project does not support point version control',\n                },\n                status=status.HTTP_400_BAD_REQUEST,\n            )\n        version_strings = project.supported_versions()\n        # Disable making old versions inactive for now.\n        # project.versions.exclude(verbose_name__in=version_strings).update(active=False)\n        project.versions.filter(verbose_name__in=version_strings).update(\n            active=True,\n        )\n        return Response({\n            'flat': version_strings,\n        })\n\n    @detail_route()\n    def translations(self, *_, **__):\n        translations = self.get_object().translations.all()\n        return Response({\n            'translations': ProjectSerializer(translations, many=True).data,\n        })\n\n    @detail_route()\n    def subprojects(self, request, **kwargs):\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        rels = project.subprojects.all()\n        children = [rel.child for rel in rels]\n        return Response({\n            'subprojects': ProjectSerializer(children, many=True).data,\n        })\n\n    @detail_route()\n    def active_versions(self, request, **kwargs):\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        versions = project.versions.filter(active=True)\n        return Response({\n            'versions': VersionSerializer(versions, many=True).data,\n        })\n\n    @decorators.detail_route(permission_classes=[permissions.IsAdminUser])\n    def token(self, request, **kwargs):\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        token = GitHubService.get_token_for_project(project, force_local=True)\n        return Response({\n            'token': token,\n        })\n\n    @decorators.detail_route()\n    def canonical_url(self, request, **kwargs):\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        return Response({\n            'url': project.get_docs_url(),\n        })\n\n    @decorators.detail_route(\n        permission_classes=[permissions.IsAdminUser], methods=['post'])\n    def sync_versions(self, request, **kwargs):  # noqa: D205\n        \"\"\"\n        Sync the version data in the repo (on the build server).\n\n        Version data in the repo is synced with what we have in the database.\n\n        :returns: the identifiers for the versions that have been deleted.\n        \"\"\"\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n\n        # If the currently highest non-prerelease version is active, then make\n        # the new latest version active as well.\n        old_highest_version = determine_stable_version(project.versions.all())\n        if old_highest_version is not None:\n            activate_new_stable = old_highest_version.active\n        else:\n            activate_new_stable = False\n\n        try:\n            # Update All Versions\n            data = request.data\n            added_versions = set()\n            if 'tags' in data:\n                ret_set = api_utils.sync_versions(\n                    project=project, versions=data['tags'], type=TAG)\n                added_versions.update(ret_set)\n            if 'branches' in data:\n                ret_set = api_utils.sync_versions(\n                    project=project, versions=data['branches'], type=BRANCH)\n                added_versions.update(ret_set)\n            deleted_versions = api_utils.delete_versions(project, data)\n        except Exception as e:\n            log.exception('Sync Versions Error')\n            return Response(\n                {\n                    'error': str(e),\n                },\n                status=status.HTTP_400_BAD_REQUEST,\n            )\n\n        promoted_version = project.update_stable_version()\n        if promoted_version:\n            new_stable = project.get_stable_version()\n            log.info(\n                'Triggering new stable build: {project}:{version}'.format(\n                    project=project.slug,\n                    version=new_stable.identifier,\n                ))\n            trigger_build(project=project, version=new_stable)\n\n            # Marking the tag that is considered the new stable version as\n            # active and building it if it was just added.\n            if (activate_new_stable and\n                    promoted_version.slug in added_versions):\n                promoted_version.active = True\n                promoted_version.save()\n                trigger_build(project=project, version=promoted_version)\n\n        return Response({\n            'added_versions': added_versions,\n            'deleted_versions': deleted_versions,\n        })\n\n\nclass VersionViewSet(UserSelectViewSet):\n\n    permission_classes = [APIRestrictedPermission]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = VersionSerializer\n    admin_serializer_class = VersionAdminSerializer\n    model = Version\n    filter_fields = ('active', 'project__slug',)\n\n\nclass BuildViewSetBase(UserSelectViewSet):\n    permission_classes = [APIRestrictedPermission]\n    renderer_classes = (JSONRenderer, PlainTextBuildRenderer)\n    serializer_class = BuildSerializer\n    admin_serializer_class = BuildAdminSerializer\n    model = Build\n    filter_fields = ('project__slug',)\n\n\nclass BuildViewSet(SettingsOverrideObject):\n\n    \"\"\"A pluggable class to allow for build cold storage.\"\"\"\n\n    _default_class = BuildViewSetBase\n\n\nclass BuildCommandViewSet(UserSelectViewSet):\n    permission_classes = [APIRestrictedPermission]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = BuildCommandSerializer\n    model = BuildCommandResult\n\n\nclass NotificationViewSet(viewsets.ReadOnlyModelViewSet):\n    permission_classes = (permissions.IsAuthenticated, RelatedProjectIsOwner)\n    renderer_classes = (JSONRenderer,)\n    model = EmailHook\n\n    def get_queryset(self):\n        return self.model.objects.api(self.request.user)\n\n\nclass DomainViewSet(UserSelectViewSet):\n    permission_classes = [APIRestrictedPermission]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = DomainSerializer\n    model = Domain\n\n\nclass RemoteOrganizationViewSet(viewsets.ReadOnlyModelViewSet):\n    permission_classes = [IsOwner]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = RemoteOrganizationSerializer\n    model = RemoteOrganization\n    pagination_class = api_utils.RemoteOrganizationPagination\n\n    def get_queryset(self):\n        return (\n            self.model.objects.api(self.request.user).filter(\n                account__provider__in=[\n                    service.adapter.provider_id for service in registry\n                ]))\n\n\nclass RemoteRepositoryViewSet(viewsets.ReadOnlyModelViewSet):\n    permission_classes = [IsOwner]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = RemoteRepositorySerializer\n    model = RemoteRepository\n    pagination_class = api_utils.RemoteProjectPagination\n\n    def get_queryset(self):\n        query = self.model.objects.api(self.request.user)\n        org = self.request.query_params.get('org', None)\n        if org is not None:\n            query = query.filter(organization__pk=org)\n\n        own = self.request.query_params.get('own', None)\n        if own is not None:\n            query = query.filter(\n                account__provider=own,\n                organization=None,\n            )\n\n        query = query.filter(\n            account__provider__in=[\n                service.adapter.provider_id for service in registry\n            ])\n        return query\n\n\nclass SocialAccountViewSet(viewsets.ReadOnlyModelViewSet):\n    permission_classes = [IsOwner]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = SocialAccountSerializer\n    model = SocialAccount\n\n    def get_queryset(self):\n        return self.model.objects.filter(user=self.request.user.pk)\n", "path": "readthedocs/restapi/views/model_views.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Endpoints for listing Projects, Versions, Builds, etc.\"\"\"\n\nfrom __future__ import (\n    absolute_import, division, print_function, unicode_literals)\n\nimport logging\n\nfrom allauth.socialaccount.models import SocialAccount\nfrom builtins import str\nfrom django.shortcuts import get_object_or_404\nfrom django.template.loader import render_to_string\nfrom rest_framework import decorators, permissions, status, viewsets\nfrom rest_framework.decorators import detail_route\nfrom rest_framework.renderers import BaseRenderer, JSONRenderer\nfrom rest_framework.response import Response\n\nfrom readthedocs.builds.constants import BRANCH, TAG\nfrom readthedocs.builds.models import Build, BuildCommandResult, Version\nfrom readthedocs.core.utils import trigger_build\nfrom readthedocs.core.utils.extend import SettingsOverrideObject\nfrom readthedocs.oauth.models import RemoteOrganization, RemoteRepository\nfrom readthedocs.oauth.services import GitHubService, registry\nfrom readthedocs.projects.models import Domain, EmailHook, Project\nfrom readthedocs.projects.version_handling import determine_stable_version\n\nfrom .. import utils as api_utils\nfrom ..permissions import (\n    APIPermission, APIRestrictedPermission, IsOwner, RelatedProjectIsOwner)\nfrom ..serializers import (\n    BuildAdminSerializer, BuildCommandSerializer, BuildSerializer,\n    DomainSerializer, ProjectAdminSerializer, ProjectSerializer,\n    RemoteOrganizationSerializer, RemoteRepositorySerializer,\n    SocialAccountSerializer, VersionAdminSerializer, VersionSerializer)\n\nlog = logging.getLogger(__name__)\n\n\nclass PlainTextBuildRenderer(BaseRenderer):\n\n    \"\"\"\n    Custom renderer for text/plain format.\n\n    charset is 'utf-8' by default.\n    \"\"\"\n\n    media_type = 'text/plain'\n    format = 'txt'\n\n    def render(self, data, accepted_media_type=None, renderer_context=None):\n        renderer_context = renderer_context or {}\n        response = renderer_context.get('response')\n        if not response or response.exception:\n            return data.get('detail', '').encode(self.charset)\n        data = render_to_string(\n            'restapi/log.txt', {'build': data}\n        )\n        return data.encode(self.charset)\n\n\nclass UserSelectViewSet(viewsets.ModelViewSet):\n\n    \"\"\"\n    View set that varies serializer class based on request user credentials.\n\n    Viewsets using this class should have an attribute `admin_serializer_class`,\n    which is a serializer that might have more fields that only admin/staff\n    users require. If the user is staff, this class will be returned instead.\n    \"\"\"\n\n    def get_serializer_class(self):\n        try:\n            if (self.request.user.is_staff and\n                    self.admin_serializer_class is not None):\n                return self.admin_serializer_class\n        except AttributeError:\n            pass\n        return self.serializer_class\n\n    def get_queryset(self):\n        \"\"\"Use our API manager method to determine authorization on queryset.\"\"\"\n        return self.model.objects.api(self.request.user)\n\n\nclass ProjectViewSet(UserSelectViewSet):\n\n    \"\"\"List, filter, etc, Projects.\"\"\"\n\n    permission_classes = [APIPermission]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = ProjectSerializer\n    admin_serializer_class = ProjectAdminSerializer\n    model = Project\n    pagination_class = api_utils.ProjectPagination\n    filter_fields = ('slug',)\n\n    @decorators.detail_route()\n    def valid_versions(self, request, **kwargs):\n        \"\"\"Maintain state of versions that are wanted.\"\"\"\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        if (not project.num_major or not project.num_minor or\n                not project.num_point):\n            return Response(\n                {\n                    'error': 'Project does not support point version control',\n                },\n                status=status.HTTP_400_BAD_REQUEST,\n            )\n        version_strings = project.supported_versions()\n        # Disable making old versions inactive for now.\n        # project.versions.exclude(verbose_name__in=version_strings).update(active=False)\n        project.versions.filter(verbose_name__in=version_strings).update(\n            active=True,\n        )\n        return Response({\n            'flat': version_strings,\n        })\n\n    @detail_route()\n    def translations(self, *_, **__):\n        translations = self.get_object().translations.all()\n        return Response({\n            'translations': ProjectSerializer(translations, many=True).data,\n        })\n\n    @detail_route()\n    def subprojects(self, request, **kwargs):\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        rels = project.subprojects.all()\n        children = [rel.child for rel in rels]\n        return Response({\n            'subprojects': ProjectSerializer(children, many=True).data,\n        })\n\n    @detail_route()\n    def active_versions(self, request, **kwargs):\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        versions = project.versions.filter(active=True)\n        return Response({\n            'versions': VersionSerializer(versions, many=True).data,\n        })\n\n    @decorators.detail_route(permission_classes=[permissions.IsAdminUser])\n    def token(self, request, **kwargs):\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        token = GitHubService.get_token_for_project(project, force_local=True)\n        return Response({\n            'token': token,\n        })\n\n    @decorators.detail_route()\n    def canonical_url(self, request, **kwargs):\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n        return Response({\n            'url': project.get_docs_url(),\n        })\n\n    @decorators.detail_route(\n        permission_classes=[permissions.IsAdminUser], methods=['post'])\n    def sync_versions(self, request, **kwargs):  # noqa: D205\n        \"\"\"\n        Sync the version data in the repo (on the build server).\n\n        Version data in the repo is synced with what we have in the database.\n\n        :returns: the identifiers for the versions that have been deleted.\n        \"\"\"\n        project = get_object_or_404(\n            Project.objects.api(request.user), pk=kwargs['pk'])\n\n        # If the currently highest non-prerelease version is active, then make\n        # the new latest version active as well.\n        old_highest_version = determine_stable_version(project.versions.all())\n        if old_highest_version is not None:\n            activate_new_stable = old_highest_version.active\n        else:\n            activate_new_stable = False\n\n        try:\n            # Update All Versions\n            data = request.data\n            added_versions = set()\n            if 'tags' in data:\n                ret_set = api_utils.sync_versions(\n                    project=project, versions=data['tags'], type=TAG)\n                added_versions.update(ret_set)\n            if 'branches' in data:\n                ret_set = api_utils.sync_versions(\n                    project=project, versions=data['branches'], type=BRANCH)\n                added_versions.update(ret_set)\n            deleted_versions = api_utils.delete_versions(project, data)\n        except Exception as e:\n            log.exception('Sync Versions Error')\n            return Response(\n                {\n                    'error': str(e),\n                },\n                status=status.HTTP_400_BAD_REQUEST,\n            )\n\n        promoted_version = project.update_stable_version()\n        if promoted_version:\n            new_stable = project.get_stable_version()\n            log.info(\n                'Triggering new stable build: {project}:{version}'.format(\n                    project=project.slug,\n                    version=new_stable.identifier,\n                ))\n            trigger_build(project=project, version=new_stable)\n\n            # Marking the tag that is considered the new stable version as\n            # active and building it if it was just added.\n            if (activate_new_stable and\n                    promoted_version.slug in added_versions):\n                promoted_version.active = True\n                promoted_version.save()\n                trigger_build(project=project, version=promoted_version)\n\n        return Response({\n            'added_versions': added_versions,\n            'deleted_versions': deleted_versions,\n        })\n\n\nclass VersionViewSet(UserSelectViewSet):\n\n    permission_classes = [APIRestrictedPermission]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = VersionSerializer\n    admin_serializer_class = VersionAdminSerializer\n    model = Version\n    filter_fields = ('active', 'project__slug',)\n\n\nclass BuildViewSetBase(UserSelectViewSet):\n    permission_classes = [APIRestrictedPermission]\n    renderer_classes = (JSONRenderer, PlainTextBuildRenderer)\n    serializer_class = BuildSerializer\n    admin_serializer_class = BuildAdminSerializer\n    model = Build\n    filter_fields = ('project__slug', 'commit')\n\n\nclass BuildViewSet(SettingsOverrideObject):\n\n    \"\"\"A pluggable class to allow for build cold storage.\"\"\"\n\n    _default_class = BuildViewSetBase\n\n\nclass BuildCommandViewSet(UserSelectViewSet):\n    permission_classes = [APIRestrictedPermission]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = BuildCommandSerializer\n    model = BuildCommandResult\n\n\nclass NotificationViewSet(viewsets.ReadOnlyModelViewSet):\n    permission_classes = (permissions.IsAuthenticated, RelatedProjectIsOwner)\n    renderer_classes = (JSONRenderer,)\n    model = EmailHook\n\n    def get_queryset(self):\n        return self.model.objects.api(self.request.user)\n\n\nclass DomainViewSet(UserSelectViewSet):\n    permission_classes = [APIRestrictedPermission]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = DomainSerializer\n    model = Domain\n\n\nclass RemoteOrganizationViewSet(viewsets.ReadOnlyModelViewSet):\n    permission_classes = [IsOwner]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = RemoteOrganizationSerializer\n    model = RemoteOrganization\n    pagination_class = api_utils.RemoteOrganizationPagination\n\n    def get_queryset(self):\n        return (\n            self.model.objects.api(self.request.user).filter(\n                account__provider__in=[\n                    service.adapter.provider_id for service in registry\n                ]))\n\n\nclass RemoteRepositoryViewSet(viewsets.ReadOnlyModelViewSet):\n    permission_classes = [IsOwner]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = RemoteRepositorySerializer\n    model = RemoteRepository\n    pagination_class = api_utils.RemoteProjectPagination\n\n    def get_queryset(self):\n        query = self.model.objects.api(self.request.user)\n        org = self.request.query_params.get('org', None)\n        if org is not None:\n            query = query.filter(organization__pk=org)\n\n        own = self.request.query_params.get('own', None)\n        if own is not None:\n            query = query.filter(\n                account__provider=own,\n                organization=None,\n            )\n\n        query = query.filter(\n            account__provider__in=[\n                service.adapter.provider_id for service in registry\n            ])\n        return query\n\n\nclass SocialAccountViewSet(viewsets.ReadOnlyModelViewSet):\n    permission_classes = [IsOwner]\n    renderer_classes = (JSONRenderer,)\n    serializer_class = SocialAccountSerializer\n    model = SocialAccount\n\n    def get_queryset(self):\n        return self.model.objects.filter(user=self.request.user.pk)\n", "path": "readthedocs/restapi/views/model_views.py"}]}