We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Why does SSR rendering /en-US/docs/Web require 54 SQL queries?
<img width="1500" alt="Screen Shot 2019-08-13 at 3 05 10 PM" src="https://user-images.githubusercontent.com/26739/62969706-da54f880-bddb-11e9-8405-dd1ecd25b657.png">



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kuma/api/v1/views.py`
Content:
```
1 from django.conf import settings
2 from django.http import HttpResponsePermanentRedirect, JsonResponse
3 from django.shortcuts import get_object_or_404
4 from django.utils.translation import activate, ugettext as _
5 from django.views.decorators.cache import never_cache
6 from django.views.decorators.http import require_GET
7 from elasticsearch_dsl import Q, query
8 from rest_framework import status
9 from rest_framework.decorators import api_view
10 from rest_framework.response import Response
11 from waffle.decorators import waffle_flag
12 from waffle.models import Flag, Sample, Switch
13 
14 from kuma.api.v1.serializers import BCSignalSerializer
15 from kuma.core.urlresolvers import reverse
16 from kuma.users.templatetags.jinja_helpers import gravatar_url
17 from kuma.wiki.jobs import DocumentContributorsJob
18 from kuma.wiki.models import Document
19 from kuma.wiki.search import WikiDocumentType
20 from kuma.wiki.templatetags.jinja_helpers import absolutify
21 
22 
23 @never_cache
24 @require_GET
25 def doc(request, locale, slug):
26     """
27     Return a JSON object that includes document content and metadata
28     for the document specified by the locale and path. Raises a 404
29     error if no such document exists. This is an API with URL
30     /api/v1/doc/<locale>/<path>
31     """
32     # TODO: This API endpoint probably needs to handle redirect documents
33     # and documents that fall back to the en-US locale. See
34     # the document() function in wiki/views/document.py for a model to follow.
35 
36     # Since we don't have the locale at the start of the path, our
37     # locale middleware can't set the translation language correctly
38     # and we need to do it explicitly. (We need to know the language
39     # so that we can provide translated language names for the
40     # translations menu.)
41     activate(locale)
42     document = get_object_or_404(Document, locale=locale, slug=slug)
43 
44     redirect = get_content_based_redirect(document)
45     if redirect:
46         redirect_url, is_redirect_to_document = redirect
47         if is_redirect_to_document:
48             return HttpResponsePermanentRedirect(redirect_url)
49         return JsonResponse(document_api_data(redirect_url=redirect_url))
50 
51     return JsonResponse(document_api_data(document))
52 
53 
54 def get_s3_key(doc=None, locale=None, slug=None,
55                prefix_with_forward_slash=False):
56     if doc:
57         locale, slug = doc.locale, doc.slug
58     key = reverse('api.v1.doc', args=(locale, slug))
59     if prefix_with_forward_slash:
60         # Redirects within an S3 bucket must be prefixed with "/".
61         return key
62     return key.lstrip('/')
63 
64 
65 def get_cdn_key(locale, slug):
66     """Given a document's locale and slug, return the "key" for the CDN."""
67     return get_s3_key(locale=locale, slug=slug, prefix_with_forward_slash=True)
68 
69 
70 def get_content_based_redirect(document):
71     """
72     Returns None if the document is not a content-based redirect, otherwise a
73     tuple pair comprising the redirect URL as well as a boolean value. The
74     boolean value will be True if this is a redirect to another document,
75     otherwise False. If the document is a redirect to another document or a
76     redirect to the homepage, a relative URL will be returned, otherwise it
77     will be a full URL to the wiki site.
78     """
79     redirect_url = document.get_redirect_url()
80     if redirect_url and (redirect_url != document.get_absolute_url()):
81         redirect_document = document.get_redirect_document(id_only=False)
82         if redirect_document:
83             # This is a redirect to another document.
84             return (
85                 get_s3_key(redirect_document, prefix_with_forward_slash=True),
86                 True
87             )
88         # This is a redirect to non-document page. For now, if it's the home
89         # page, return a relative path (so we stay on the read-only domain),
90         # otherwise return the full URL for the wiki site.
91         locale = document.locale
92         is_home_page = (redirect_url in
93                         ('/', '/' + locale, '/{}/'.format(locale)))
94         if is_home_page:
95             # Let's return a relative URL to the home page for this locale.
96             return ('/{}/'.format(locale), False)
97         # Otherwise, let's return a full URL to the Wiki site.
98         return (absolutify(redirect_url, for_wiki_site=True), False)
99     return None
100 
101 
102 def document_api_data(doc=None, ensure_contributors=False, redirect_url=None):
103     """
104     Returns the JSON data for the document for the document API.
105     """
106     if redirect_url:
107         return {
108             'documentData': None,
109             'redirectURL': redirect_url,
110         }
111 
112     job = DocumentContributorsJob()
113     # If "ensure_contributors" is True, we need the contributors since the
114     # result will likely be cached, so we'll set "fetch_on_miss" and wait
115     # for the result if it's not already available or stale.
116     job.fetch_on_miss = ensure_contributors
117     contributors = [c['username'] for c in job.get(doc.pk)]
118 
119     # The original english slug for this document, for google analytics
120     if doc.locale == 'en-US':
121         en_slug = doc.slug
122     elif doc.parent_id and doc.parent.locale == 'en-US':
123         en_slug = doc.parent.slug
124     else:
125         en_slug = ''
126 
127     other_translations = doc.get_other_translations(
128         fields=('locale', 'slug', 'title'))
129     available_locales = (
130         set([doc.locale]) | set(t.locale for t in other_translations))
131 
132     return {
133         'documentData': {
134             'locale': doc.locale,
135             'slug': doc.slug,
136             'enSlug': en_slug,
137             'id': doc.id,
138             'title': doc.title,
139             'summary': doc.get_summary_html(),
140             'language': doc.language,
141             'hrefLang': doc.get_hreflang(available_locales),
142             'absoluteURL': doc.get_absolute_url(),
143             'editURL': absolutify(doc.get_edit_url(), for_wiki_site=True),
144             'translateURL': (
145                 absolutify(
146                     reverse(
147                         'wiki.select_locale',
148                         args=(doc.slug,),
149                         locale=doc.locale,
150                     ),
151                     for_wiki_site=True
152                 )
153                 if doc.is_localizable else
154                 None
155             ),
156             'bodyHTML': doc.get_body_html(),
157             'quickLinksHTML': doc.get_quick_links_html(),
158             'tocHTML': doc.get_toc_html(),
159             'parents': [
160                 {
161                     'url': d.get_absolute_url(),
162                     'title': d.title
163                 } for d in doc.parents
164             ],
165             'translations': [
166                 {
167                     'language': t.language,
168                     'hrefLang': t.get_hreflang(available_locales),
169                     'localizedLanguage': _(settings.LOCALES[t.locale].english),
170                     'locale': t.locale,
171                     'url': t.get_absolute_url(),
172                     'title': t.title
173                 } for t in other_translations
174             ],
175             'contributors': contributors,
176             'lastModified': (doc.current_revision and
177                              doc.current_revision.created.isoformat()),
178             'lastModifiedBy': (doc.current_revision and
179                                str(doc.current_revision.creator))
180         },
181         'redirectURL': None,
182     }
183 
184 
185 @never_cache
186 @require_GET
187 def whoami(request):
188     """
189     Return a JSON object representing the current user, either
190     authenticated or anonymous.
191     """
192     user = request.user
193     if user.is_authenticated:
194         data = {
195             'username': user.username,
196             'timezone': user.timezone,
197             'is_authenticated': True,
198             'is_staff': user.is_staff,
199             'is_superuser': user.is_superuser,
200             'is_beta_tester': user.is_beta_tester,
201             'gravatar_url': {
202                 'small': gravatar_url(user.email, size=50),
203                 'large': gravatar_url(user.email, size=200),
204             }
205         }
206     else:
207         data = {
208             'username': None,
209             'timezone': settings.TIME_ZONE,
210             'is_authenticated': False,
211             'is_staff': False,
212             'is_superuser': False,
213             'is_beta_tester': False,
214             'gravatar_url': {
215                 'small': None,
216                 'large': None,
217             }
218         }
219 
220     # Add waffle data to the dict we're going to be returning.
221     # This is what the waffle.wafflejs() template tag does, but we're
222     # doing it via an API instead of hardcoding the settings into
223     # the HTML page. See also from waffle.views._generate_waffle_js.
224     #
225     # Note that if we upgrade django-waffle, version 15 introduces a
226     # pluggable flag model, and the approved way to get all flag
227     # objects will then become:
228     #    get_waffle_flag_model().get_all()
229     #
230     data['waffle'] = {
231         'flags': {f.name: f.is_active(request) for f in Flag.get_all()},
232         'switches': {s.name: s.is_active() for s in Switch.get_all()},
233         'samples': {s.name: s.is_active() for s in Sample.get_all()},
234     }
235 
236     return JsonResponse(data)
237 
238 
239 @never_cache
240 @require_GET
241 def search(request, locale):
242     """ An API endpoint to return search results as a JSON blob.
243     This endpoint makes a relatively simple ElasticSearch query
244     for documents matching the value of the q parameter.
245     """
246     # TODO: I'm betting that a simple search like this will be faster
247     # and just as good as the more complex searches implemented by the
248     # code in kuma/search/. Peter disagrees and thinks that we might
249     # eventually want to make this endpoint use code from kuma/search/.
250     # An alternative is to just abandon this API endpoint and have
251     # the frontend call wiki.d.m.o/locale/search.json?q=query. On the
252     # other hand, if we're ever going to implement any kind of
253     # search-as-you-type interface, we'll need a super-fast custom
254     # endpoint like this one.
255     query_string = request.GET.get('q')
256     if locale == 'en-US':
257         search = (WikiDocumentType.search()
258                   .filter('term', locale=locale)
259                   .source(['slug', 'title', 'summary'])
260                   .query('multi_match', query=query_string,
261                          fields=['title^7', 'summary^2', 'content']))
262     else:
263         search = (WikiDocumentType.search()
264                   .filter('terms', locale=[locale, 'en-US'])
265                   .source(['slug', 'title', 'summary', 'locale'])
266                   .query(query.Bool(
267                       must=Q('multi_match', query=query_string,
268                              fields=['title^7', 'summary^2', 'content']),
269                       should=[
270                           # boost the score if the document is translated
271                           Q('term', locale={'value': locale, 'boost': 8}),
272                       ])))
273 
274     # Add excerpts with search results highlighted
275     search = search.highlight('content')
276     search = search.highlight_options(order='score',
277                                       pre_tags=['<mark>'],
278                                       post_tags=['</mark>'])
279 
280     # Return as many as 40 matches, since we're not implementing pagination yet
281     response = search[0:40].execute()
282     return JsonResponse(response.to_dict())
283 
284 
285 @waffle_flag('bc-signals')
286 @api_view(['POST'])
287 def bc_signal(request):
288     serializer = BCSignalSerializer(data=request.data)
289     if serializer.is_valid():
290         serializer.save()
291         return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
292     return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
293
```
--- 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/kuma/api/v1/views.py b/kuma/api/v1/views.py
--- a/kuma/api/v1/views.py
+++ b/kuma/api/v1/views.py
@@ -125,7 +125,7 @@
         en_slug = ''
 
     other_translations = doc.get_other_translations(
-        fields=('locale', 'slug', 'title'))
+        fields=('locale', 'slug', 'title', 'parent'))
     available_locales = (
         set([doc.locale]) | set(t.locale for t in other_translations))

{"golden_diff": "diff --git a/kuma/api/v1/views.py b/kuma/api/v1/views.py\n--- a/kuma/api/v1/views.py\n+++ b/kuma/api/v1/views.py\n@@ -125,7 +125,7 @@\n         en_slug = ''\n \n     other_translations = doc.get_other_translations(\n-        fields=('locale', 'slug', 'title'))\n+        fields=('locale', 'slug', 'title', 'parent'))\n     available_locales = (\n         set([doc.locale]) | set(t.locale for t in other_translations))\n", "issue": "Why does SSR rendering /en-US/docs/Web require 54 SQL queries?\n<img width=\"1500\" alt=\"Screen Shot 2019-08-13 at 3 05 10 PM\" src=\"https://user-images.githubusercontent.com/26739/62969706-da54f880-bddb-11e9-8405-dd1ecd25b657.png\">\r\n\r\n\n", "before_files": [{"content": "from django.conf import settings\nfrom django.http import HttpResponsePermanentRedirect, JsonResponse\nfrom django.shortcuts import get_object_or_404\nfrom django.utils.translation import activate, ugettext as _\nfrom django.views.decorators.cache import never_cache\nfrom django.views.decorators.http import require_GET\nfrom elasticsearch_dsl import Q, query\nfrom rest_framework import status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom waffle.decorators import waffle_flag\nfrom waffle.models import Flag, Sample, Switch\n\nfrom kuma.api.v1.serializers import BCSignalSerializer\nfrom kuma.core.urlresolvers import reverse\nfrom kuma.users.templatetags.jinja_helpers import gravatar_url\nfrom kuma.wiki.jobs import DocumentContributorsJob\nfrom kuma.wiki.models import Document\nfrom kuma.wiki.search import WikiDocumentType\nfrom kuma.wiki.templatetags.jinja_helpers import absolutify\n\n\n@never_cache\n@require_GET\ndef doc(request, locale, slug):\n    \"\"\"\n    Return a JSON object that includes document content and metadata\n    for the document specified by the locale and path. Raises a 404\n    error if no such document exists. This is an API with URL\n    /api/v1/doc/<locale>/<path>\n    \"\"\"\n    # TODO: This API endpoint probably needs to handle redirect documents\n    # and documents that fall back to the en-US locale. See\n    # the document() function in wiki/views/document.py for a model to follow.\n\n    # Since we don't have the locale at the start of the path, our\n    # locale middleware can't set the translation language correctly\n    # and we need to do it explicitly. (We need to know the language\n    # so that we can provide translated language names for the\n    # translations menu.)\n    activate(locale)\n    document = get_object_or_404(Document, locale=locale, slug=slug)\n\n    redirect = get_content_based_redirect(document)\n    if redirect:\n        redirect_url, is_redirect_to_document = redirect\n        if is_redirect_to_document:\n            return HttpResponsePermanentRedirect(redirect_url)\n        return JsonResponse(document_api_data(redirect_url=redirect_url))\n\n    return JsonResponse(document_api_data(document))\n\n\ndef get_s3_key(doc=None, locale=None, slug=None,\n               prefix_with_forward_slash=False):\n    if doc:\n        locale, slug = doc.locale, doc.slug\n    key = reverse('api.v1.doc', args=(locale, slug))\n    if prefix_with_forward_slash:\n        # Redirects within an S3 bucket must be prefixed with \"/\".\n        return key\n    return key.lstrip('/')\n\n\ndef get_cdn_key(locale, slug):\n    \"\"\"Given a document's locale and slug, return the \"key\" for the CDN.\"\"\"\n    return get_s3_key(locale=locale, slug=slug, prefix_with_forward_slash=True)\n\n\ndef get_content_based_redirect(document):\n    \"\"\"\n    Returns None if the document is not a content-based redirect, otherwise a\n    tuple pair comprising the redirect URL as well as a boolean value. The\n    boolean value will be True if this is a redirect to another document,\n    otherwise False. If the document is a redirect to another document or a\n    redirect to the homepage, a relative URL will be returned, otherwise it\n    will be a full URL to the wiki site.\n    \"\"\"\n    redirect_url = document.get_redirect_url()\n    if redirect_url and (redirect_url != document.get_absolute_url()):\n        redirect_document = document.get_redirect_document(id_only=False)\n        if redirect_document:\n            # This is a redirect to another document.\n            return (\n                get_s3_key(redirect_document, prefix_with_forward_slash=True),\n                True\n            )\n        # This is a redirect to non-document page. For now, if it's the home\n        # page, return a relative path (so we stay on the read-only domain),\n        # otherwise return the full URL for the wiki site.\n        locale = document.locale\n        is_home_page = (redirect_url in\n                        ('/', '/' + locale, '/{}/'.format(locale)))\n        if is_home_page:\n            # Let's return a relative URL to the home page for this locale.\n            return ('/{}/'.format(locale), False)\n        # Otherwise, let's return a full URL to the Wiki site.\n        return (absolutify(redirect_url, for_wiki_site=True), False)\n    return None\n\n\ndef document_api_data(doc=None, ensure_contributors=False, redirect_url=None):\n    \"\"\"\n    Returns the JSON data for the document for the document API.\n    \"\"\"\n    if redirect_url:\n        return {\n            'documentData': None,\n            'redirectURL': redirect_url,\n        }\n\n    job = DocumentContributorsJob()\n    # If \"ensure_contributors\" is True, we need the contributors since the\n    # result will likely be cached, so we'll set \"fetch_on_miss\" and wait\n    # for the result if it's not already available or stale.\n    job.fetch_on_miss = ensure_contributors\n    contributors = [c['username'] for c in job.get(doc.pk)]\n\n    # The original english slug for this document, for google analytics\n    if doc.locale == 'en-US':\n        en_slug = doc.slug\n    elif doc.parent_id and doc.parent.locale == 'en-US':\n        en_slug = doc.parent.slug\n    else:\n        en_slug = ''\n\n    other_translations = doc.get_other_translations(\n        fields=('locale', 'slug', 'title'))\n    available_locales = (\n        set([doc.locale]) | set(t.locale for t in other_translations))\n\n    return {\n        'documentData': {\n            'locale': doc.locale,\n            'slug': doc.slug,\n            'enSlug': en_slug,\n            'id': doc.id,\n            'title': doc.title,\n            'summary': doc.get_summary_html(),\n            'language': doc.language,\n            'hrefLang': doc.get_hreflang(available_locales),\n            'absoluteURL': doc.get_absolute_url(),\n            'editURL': absolutify(doc.get_edit_url(), for_wiki_site=True),\n            'translateURL': (\n                absolutify(\n                    reverse(\n                        'wiki.select_locale',\n                        args=(doc.slug,),\n                        locale=doc.locale,\n                    ),\n                    for_wiki_site=True\n                )\n                if doc.is_localizable else\n                None\n            ),\n            'bodyHTML': doc.get_body_html(),\n            'quickLinksHTML': doc.get_quick_links_html(),\n            'tocHTML': doc.get_toc_html(),\n            'parents': [\n                {\n                    'url': d.get_absolute_url(),\n                    'title': d.title\n                } for d in doc.parents\n            ],\n            'translations': [\n                {\n                    'language': t.language,\n                    'hrefLang': t.get_hreflang(available_locales),\n                    'localizedLanguage': _(settings.LOCALES[t.locale].english),\n                    'locale': t.locale,\n                    'url': t.get_absolute_url(),\n                    'title': t.title\n                } for t in other_translations\n            ],\n            'contributors': contributors,\n            'lastModified': (doc.current_revision and\n                             doc.current_revision.created.isoformat()),\n            'lastModifiedBy': (doc.current_revision and\n                               str(doc.current_revision.creator))\n        },\n        'redirectURL': None,\n    }\n\n\n@never_cache\n@require_GET\ndef whoami(request):\n    \"\"\"\n    Return a JSON object representing the current user, either\n    authenticated or anonymous.\n    \"\"\"\n    user = request.user\n    if user.is_authenticated:\n        data = {\n            'username': user.username,\n            'timezone': user.timezone,\n            'is_authenticated': True,\n            'is_staff': user.is_staff,\n            'is_superuser': user.is_superuser,\n            'is_beta_tester': user.is_beta_tester,\n            'gravatar_url': {\n                'small': gravatar_url(user.email, size=50),\n                'large': gravatar_url(user.email, size=200),\n            }\n        }\n    else:\n        data = {\n            'username': None,\n            'timezone': settings.TIME_ZONE,\n            'is_authenticated': False,\n            'is_staff': False,\n            'is_superuser': False,\n            'is_beta_tester': False,\n            'gravatar_url': {\n                'small': None,\n                'large': None,\n            }\n        }\n\n    # Add waffle data to the dict we're going to be returning.\n    # This is what the waffle.wafflejs() template tag does, but we're\n    # doing it via an API instead of hardcoding the settings into\n    # the HTML page. See also from waffle.views._generate_waffle_js.\n    #\n    # Note that if we upgrade django-waffle, version 15 introduces a\n    # pluggable flag model, and the approved way to get all flag\n    # objects will then become:\n    #    get_waffle_flag_model().get_all()\n    #\n    data['waffle'] = {\n        'flags': {f.name: f.is_active(request) for f in Flag.get_all()},\n        'switches': {s.name: s.is_active() for s in Switch.get_all()},\n        'samples': {s.name: s.is_active() for s in Sample.get_all()},\n    }\n\n    return JsonResponse(data)\n\n\n@never_cache\n@require_GET\ndef search(request, locale):\n    \"\"\" An API endpoint to return search results as a JSON blob.\n    This endpoint makes a relatively simple ElasticSearch query\n    for documents matching the value of the q parameter.\n    \"\"\"\n    # TODO: I'm betting that a simple search like this will be faster\n    # and just as good as the more complex searches implemented by the\n    # code in kuma/search/. Peter disagrees and thinks that we might\n    # eventually want to make this endpoint use code from kuma/search/.\n    # An alternative is to just abandon this API endpoint and have\n    # the frontend call wiki.d.m.o/locale/search.json?q=query. On the\n    # other hand, if we're ever going to implement any kind of\n    # search-as-you-type interface, we'll need a super-fast custom\n    # endpoint like this one.\n    query_string = request.GET.get('q')\n    if locale == 'en-US':\n        search = (WikiDocumentType.search()\n                  .filter('term', locale=locale)\n                  .source(['slug', 'title', 'summary'])\n                  .query('multi_match', query=query_string,\n                         fields=['title^7', 'summary^2', 'content']))\n    else:\n        search = (WikiDocumentType.search()\n                  .filter('terms', locale=[locale, 'en-US'])\n                  .source(['slug', 'title', 'summary', 'locale'])\n                  .query(query.Bool(\n                      must=Q('multi_match', query=query_string,\n                             fields=['title^7', 'summary^2', 'content']),\n                      should=[\n                          # boost the score if the document is translated\n                          Q('term', locale={'value': locale, 'boost': 8}),\n                      ])))\n\n    # Add excerpts with search results highlighted\n    search = search.highlight('content')\n    search = search.highlight_options(order='score',\n                                      pre_tags=['<mark>'],\n                                      post_tags=['</mark>'])\n\n    # Return as many as 40 matches, since we're not implementing pagination yet\n    response = search[0:40].execute()\n    return JsonResponse(response.to_dict())\n\n\n@waffle_flag('bc-signals')\n@api_view(['POST'])\ndef bc_signal(request):\n    serializer = BCSignalSerializer(data=request.data)\n    if serializer.is_valid():\n        serializer.save()\n        return Response(serializer.validated_data, status=status.HTTP_201_CREATED)\n    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n", "path": "kuma/api/v1/views.py"}], "after_files": [{"content": "from django.conf import settings\nfrom django.http import HttpResponsePermanentRedirect, JsonResponse\nfrom django.shortcuts import get_object_or_404\nfrom django.utils.translation import activate, ugettext as _\nfrom django.views.decorators.cache import never_cache\nfrom django.views.decorators.http import require_GET\nfrom elasticsearch_dsl import Q, query\nfrom rest_framework import status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom waffle.decorators import waffle_flag\nfrom waffle.models import Flag, Sample, Switch\n\nfrom kuma.api.v1.serializers import BCSignalSerializer\nfrom kuma.core.urlresolvers import reverse\nfrom kuma.users.templatetags.jinja_helpers import gravatar_url\nfrom kuma.wiki.jobs import DocumentContributorsJob\nfrom kuma.wiki.models import Document\nfrom kuma.wiki.search import WikiDocumentType\nfrom kuma.wiki.templatetags.jinja_helpers import absolutify\n\n\n@never_cache\n@require_GET\ndef doc(request, locale, slug):\n    \"\"\"\n    Return a JSON object that includes document content and metadata\n    for the document specified by the locale and path. Raises a 404\n    error if no such document exists. This is an API with URL\n    /api/v1/doc/<locale>/<path>\n    \"\"\"\n    # TODO: This API endpoint probably needs to handle redirect documents\n    # and documents that fall back to the en-US locale. See\n    # the document() function in wiki/views/document.py for a model to follow.\n\n    # Since we don't have the locale at the start of the path, our\n    # locale middleware can't set the translation language correctly\n    # and we need to do it explicitly. (We need to know the language\n    # so that we can provide translated language names for the\n    # translations menu.)\n    activate(locale)\n    document = get_object_or_404(Document, locale=locale, slug=slug)\n\n    redirect = get_content_based_redirect(document)\n    if redirect:\n        redirect_url, is_redirect_to_document = redirect\n        if is_redirect_to_document:\n            return HttpResponsePermanentRedirect(redirect_url)\n        return JsonResponse(document_api_data(redirect_url=redirect_url))\n\n    return JsonResponse(document_api_data(document))\n\n\ndef get_s3_key(doc=None, locale=None, slug=None,\n               prefix_with_forward_slash=False):\n    if doc:\n        locale, slug = doc.locale, doc.slug\n    key = reverse('api.v1.doc', args=(locale, slug))\n    if prefix_with_forward_slash:\n        # Redirects within an S3 bucket must be prefixed with \"/\".\n        return key\n    return key.lstrip('/')\n\n\ndef get_cdn_key(locale, slug):\n    \"\"\"Given a document's locale and slug, return the \"key\" for the CDN.\"\"\"\n    return get_s3_key(locale=locale, slug=slug, prefix_with_forward_slash=True)\n\n\ndef get_content_based_redirect(document):\n    \"\"\"\n    Returns None if the document is not a content-based redirect, otherwise a\n    tuple pair comprising the redirect URL as well as a boolean value. The\n    boolean value will be True if this is a redirect to another document,\n    otherwise False. If the document is a redirect to another document or a\n    redirect to the homepage, a relative URL will be returned, otherwise it\n    will be a full URL to the wiki site.\n    \"\"\"\n    redirect_url = document.get_redirect_url()\n    if redirect_url and (redirect_url != document.get_absolute_url()):\n        redirect_document = document.get_redirect_document(id_only=False)\n        if redirect_document:\n            # This is a redirect to another document.\n            return (\n                get_s3_key(redirect_document, prefix_with_forward_slash=True),\n                True\n            )\n        # This is a redirect to non-document page. For now, if it's the home\n        # page, return a relative path (so we stay on the read-only domain),\n        # otherwise return the full URL for the wiki site.\n        locale = document.locale\n        is_home_page = (redirect_url in\n                        ('/', '/' + locale, '/{}/'.format(locale)))\n        if is_home_page:\n            # Let's return a relative URL to the home page for this locale.\n            return ('/{}/'.format(locale), False)\n        # Otherwise, let's return a full URL to the Wiki site.\n        return (absolutify(redirect_url, for_wiki_site=True), False)\n    return None\n\n\ndef document_api_data(doc=None, ensure_contributors=False, redirect_url=None):\n    \"\"\"\n    Returns the JSON data for the document for the document API.\n    \"\"\"\n    if redirect_url:\n        return {\n            'documentData': None,\n            'redirectURL': redirect_url,\n        }\n\n    job = DocumentContributorsJob()\n    # If \"ensure_contributors\" is True, we need the contributors since the\n    # result will likely be cached, so we'll set \"fetch_on_miss\" and wait\n    # for the result if it's not already available or stale.\n    job.fetch_on_miss = ensure_contributors\n    contributors = [c['username'] for c in job.get(doc.pk)]\n\n    # The original english slug for this document, for google analytics\n    if doc.locale == 'en-US':\n        en_slug = doc.slug\n    elif doc.parent_id and doc.parent.locale == 'en-US':\n        en_slug = doc.parent.slug\n    else:\n        en_slug = ''\n\n    other_translations = doc.get_other_translations(\n        fields=('locale', 'slug', 'title', 'parent'))\n    available_locales = (\n        set([doc.locale]) | set(t.locale for t in other_translations))\n\n    return {\n        'documentData': {\n            'locale': doc.locale,\n            'slug': doc.slug,\n            'enSlug': en_slug,\n            'id': doc.id,\n            'title': doc.title,\n            'summary': doc.get_summary_html(),\n            'language': doc.language,\n            'hrefLang': doc.get_hreflang(available_locales),\n            'absoluteURL': doc.get_absolute_url(),\n            'editURL': absolutify(doc.get_edit_url(), for_wiki_site=True),\n            'translateURL': (\n                absolutify(\n                    reverse(\n                        'wiki.select_locale',\n                        args=(doc.slug,),\n                        locale=doc.locale,\n                    ),\n                    for_wiki_site=True\n                )\n                if doc.is_localizable else\n                None\n            ),\n            'bodyHTML': doc.get_body_html(),\n            'quickLinksHTML': doc.get_quick_links_html(),\n            'tocHTML': doc.get_toc_html(),\n            'parents': [\n                {\n                    'url': d.get_absolute_url(),\n                    'title': d.title\n                } for d in doc.parents\n            ],\n            'translations': [\n                {\n                    'language': t.language,\n                    'hrefLang': t.get_hreflang(available_locales),\n                    'localizedLanguage': _(settings.LOCALES[t.locale].english),\n                    'locale': t.locale,\n                    'url': t.get_absolute_url(),\n                    'title': t.title\n                } for t in other_translations\n            ],\n            'contributors': contributors,\n            'lastModified': (doc.current_revision and\n                             doc.current_revision.created.isoformat()),\n            'lastModifiedBy': (doc.current_revision and\n                               str(doc.current_revision.creator))\n        },\n        'redirectURL': None,\n    }\n\n\n@never_cache\n@require_GET\ndef whoami(request):\n    \"\"\"\n    Return a JSON object representing the current user, either\n    authenticated or anonymous.\n    \"\"\"\n    user = request.user\n    if user.is_authenticated:\n        data = {\n            'username': user.username,\n            'timezone': user.timezone,\n            'is_authenticated': True,\n            'is_staff': user.is_staff,\n            'is_superuser': user.is_superuser,\n            'is_beta_tester': user.is_beta_tester,\n            'gravatar_url': {\n                'small': gravatar_url(user.email, size=50),\n                'large': gravatar_url(user.email, size=200),\n            }\n        }\n    else:\n        data = {\n            'username': None,\n            'timezone': settings.TIME_ZONE,\n            'is_authenticated': False,\n            'is_staff': False,\n            'is_superuser': False,\n            'is_beta_tester': False,\n            'gravatar_url': {\n                'small': None,\n                'large': None,\n            }\n        }\n\n    # Add waffle data to the dict we're going to be returning.\n    # This is what the waffle.wafflejs() template tag does, but we're\n    # doing it via an API instead of hardcoding the settings into\n    # the HTML page. See also from waffle.views._generate_waffle_js.\n    #\n    # Note that if we upgrade django-waffle, version 15 introduces a\n    # pluggable flag model, and the approved way to get all flag\n    # objects will then become:\n    #    get_waffle_flag_model().get_all()\n    #\n    data['waffle'] = {\n        'flags': {f.name: f.is_active(request) for f in Flag.get_all()},\n        'switches': {s.name: s.is_active() for s in Switch.get_all()},\n        'samples': {s.name: s.is_active() for s in Sample.get_all()},\n    }\n\n    return JsonResponse(data)\n\n\n@never_cache\n@require_GET\ndef search(request, locale):\n    \"\"\" An API endpoint to return search results as a JSON blob.\n    This endpoint makes a relatively simple ElasticSearch query\n    for documents matching the value of the q parameter.\n    \"\"\"\n    # TODO: I'm betting that a simple search like this will be faster\n    # and just as good as the more complex searches implemented by the\n    # code in kuma/search/. Peter disagrees and thinks that we might\n    # eventually want to make this endpoint use code from kuma/search/.\n    # An alternative is to just abandon this API endpoint and have\n    # the frontend call wiki.d.m.o/locale/search.json?q=query. On the\n    # other hand, if we're ever going to implement any kind of\n    # search-as-you-type interface, we'll need a super-fast custom\n    # endpoint like this one.\n    query_string = request.GET.get('q')\n    if locale == 'en-US':\n        search = (WikiDocumentType.search()\n                  .filter('term', locale=locale)\n                  .source(['slug', 'title', 'summary'])\n                  .query('multi_match', query=query_string,\n                         fields=['title^7', 'summary^2', 'content']))\n    else:\n        search = (WikiDocumentType.search()\n                  .filter('terms', locale=[locale, 'en-US'])\n                  .source(['slug', 'title', 'summary', 'locale'])\n                  .query(query.Bool(\n                      must=Q('multi_match', query=query_string,\n                             fields=['title^7', 'summary^2', 'content']),\n                      should=[\n                          # boost the score if the document is translated\n                          Q('term', locale={'value': locale, 'boost': 8}),\n                      ])))\n\n    # Add excerpts with search results highlighted\n    search = search.highlight('content')\n    search = search.highlight_options(order='score',\n                                      pre_tags=['<mark>'],\n                                      post_tags=['</mark>'])\n\n    # Return as many as 40 matches, since we're not implementing pagination yet\n    response = search[0:40].execute()\n    return JsonResponse(response.to_dict())\n\n\n@waffle_flag('bc-signals')\n@api_view(['POST'])\ndef bc_signal(request):\n    serializer = BCSignalSerializer(data=request.data)\n    if serializer.is_valid():\n        serializer.save()\n        return Response(serializer.validated_data, status=status.HTTP_201_CREATED)\n    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n", "path": "kuma/api/v1/views.py"}]}