We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
T - Add email to whoami userData so that Stripe form can be prefilled. 


--- 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 Http404, HttpResponsePermanentRedirect, JsonResponse
3 from django.shortcuts import get_object_or_404
4 from django.utils.translation import activate, gettext as _
5 from django.views.decorators.cache import never_cache
6 from django.views.decorators.http import require_GET, require_safe
7 from ratelimit.decorators import ratelimit
8 from rest_framework import serializers, status
9 from rest_framework.decorators import api_view
10 from rest_framework.renderers import JSONRenderer
11 from rest_framework.response import Response
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.search.filters import (
17     HighlightFilterBackend,
18     KeywordQueryBackend,
19     LanguageFilterBackend,
20     SearchQueryBackend,
21     TagGroupFilterBackend,
22 )
23 from kuma.search.search import SearchView
24 from kuma.users.models import User, UserSubscription
25 from kuma.users.templatetags.jinja_helpers import get_avatar_url
26 from kuma.wiki.models import Document
27 from kuma.wiki.templatetags.jinja_helpers import absolutify
28 
29 
30 @never_cache
31 @require_GET
32 def doc(request, locale, slug):
33     """
34     Return a JSON object that includes document content and metadata
35     for the document specified by the locale and path. Raises a 404
36     error if no such document exists. This is an API with URL
37     /api/v1/doc/<locale>/<path>
38     """
39     # TODO: This API endpoint probably needs to handle redirect documents
40     # and documents that fall back to the en-US locale. See
41     # the document() function in wiki/views/document.py for a model to follow.
42 
43     # Since we don't have the locale at the start of the path, our
44     # locale middleware can't set the translation language correctly
45     # and we need to do it explicitly. (We need to know the language
46     # so that we can provide translated language names for the
47     # translations menu.)
48     activate(locale)
49     document = get_object_or_404(Document, locale=locale, slug=slug)
50 
51     redirect = get_content_based_redirect(document)
52     if redirect:
53         redirect_url, is_redirect_to_document = redirect
54         if is_redirect_to_document:
55             return HttpResponsePermanentRedirect(redirect_url)
56         return JsonResponse(document_api_data(redirect_url=redirect_url))
57 
58     return JsonResponse(document_api_data(document))
59 
60 
61 def get_s3_key(
62     doc=None,
63     locale=None,
64     slug=None,
65     prefix_with_forward_slash=False,
66     suffix_file_extension=True,
67 ):
68     if doc:
69         locale, slug = doc.locale, doc.slug
70     key = reverse("api.v1.doc", args=(locale, slug))
71     if suffix_file_extension:
72         key += ".json"
73     if prefix_with_forward_slash:
74         # Redirects within an S3 bucket must be prefixed with "/".
75         return key
76     return key.lstrip("/")
77 
78 
79 def get_cdn_key(locale, slug):
80     """Given a document's locale and slug, return the "key" for the CDN."""
81     return get_s3_key(
82         locale=locale,
83         slug=slug,
84         prefix_with_forward_slash=True,
85         suffix_file_extension=False,
86     )
87 
88 
89 def get_content_based_redirect(document):
90     """
91     Returns None if the document is not a content-based redirect, otherwise a
92     tuple pair comprising the redirect URL as well as a boolean value. The
93     boolean value will be True if this is a redirect to another document,
94     otherwise False. If the document is a redirect to another document or a
95     redirect to the homepage, a relative URL will be returned, otherwise it
96     will be a full URL to the wiki site.
97     """
98     redirect_url = document.get_redirect_url()
99     if redirect_url and (redirect_url != document.get_absolute_url()):
100         redirect_document = document.get_redirect_document(id_only=False)
101         if redirect_document:
102             # This is a redirect to another document.
103             return (
104                 get_s3_key(
105                     redirect_document,
106                     prefix_with_forward_slash=True,
107                     suffix_file_extension=False,
108                 ),
109                 True,
110             )
111         # This is a redirect to non-document page. For now, if it's the home
112         # page, return a relative path (so we stay on the read-only domain),
113         # otherwise return the full URL for the wiki site.
114         locale = document.locale
115         is_home_page = redirect_url in ("/", "/" + locale, "/{}/".format(locale))
116         if is_home_page:
117             # Let's return a relative URL to the home page for this locale.
118             return ("/{}/".format(locale), False)
119         # Otherwise, let's return a full URL to the Wiki site.
120         return (absolutify(redirect_url, for_wiki_site=True), False)
121     return None
122 
123 
124 def document_api_data(doc=None, redirect_url=None):
125     """
126     Returns the JSON data for the document for the document API.
127     """
128     if redirect_url:
129         return {
130             "documentData": None,
131             "redirectURL": redirect_url,
132         }
133 
134     # The original english slug for this document, for google analytics
135     if doc.locale == "en-US":
136         en_slug = doc.slug
137     elif doc.parent_id and doc.parent.locale == "en-US":
138         en_slug = doc.parent.slug
139     else:
140         en_slug = ""
141 
142     other_translations = doc.get_other_translations(
143         fields=("locale", "slug", "title", "parent")
144     )
145     available_locales = {doc.locale} | set(t.locale for t in other_translations)
146 
147     doc_absolute_url = doc.get_absolute_url()
148     revision = doc.current_or_latest_revision()
149     translation_status = None
150     if doc.parent_id and revision and revision.localization_in_progress:
151         translation_status = (
152             "outdated" if revision.translation_age >= 10 else "in-progress"
153         )
154     return {
155         "documentData": {
156             "locale": doc.locale,
157             "slug": doc.slug,
158             "enSlug": en_slug,
159             "id": doc.id,
160             "title": doc.title,
161             "summary": doc.get_summary_html(),
162             "language": doc.language,
163             "hrefLang": doc.get_hreflang(available_locales),
164             "absoluteURL": doc_absolute_url,
165             "wikiURL": absolutify(doc_absolute_url, for_wiki_site=True),
166             "editURL": absolutify(
167                 reverse("wiki.edit", args=(doc.slug,), locale=doc.locale),
168                 for_wiki_site=True,
169             ),
170             "translateURL": (
171                 absolutify(
172                     reverse("wiki.select_locale", args=(doc.slug,), locale=doc.locale),
173                     for_wiki_site=True,
174                 )
175                 if doc.is_localizable
176                 else None
177             ),
178             "translationStatus": translation_status,
179             "bodyHTML": doc.get_body_html(),
180             "quickLinksHTML": doc.get_quick_links_html(),
181             "tocHTML": doc.get_toc_html(),
182             "raw": doc.html,
183             "parents": [
184                 {"url": d.get_absolute_url(), "title": d.title} for d in doc.parents
185             ],
186             "translations": [
187                 {
188                     "language": t.language,
189                     "hrefLang": t.get_hreflang(available_locales),
190                     "localizedLanguage": _(settings.LOCALES[t.locale].english),
191                     "locale": t.locale,
192                     "url": t.get_absolute_url(),
193                     "title": t.title,
194                 }
195                 for t in other_translations
196             ],
197             "lastModified": (
198                 doc.current_revision and doc.current_revision.created.isoformat()
199             ),
200         },
201         "redirectURL": None,
202     }
203 
204 
205 @never_cache
206 @require_GET
207 def whoami(request):
208     """
209     Return a JSON object representing the current user, either
210     authenticated or anonymous.
211     """
212     user = request.user
213     if user.is_authenticated:
214         data = {
215             "username": user.username,
216             "timezone": user.timezone,
217             "is_authenticated": True,
218             "is_staff": user.is_staff,
219             "is_superuser": user.is_superuser,
220             "is_beta_tester": user.is_beta_tester,
221             "avatar_url": get_avatar_url(user),
222             "is_subscriber": UserSubscription.objects.filter(
223                 user=user, canceled__isnull=True
224             ).exists(),
225         }
226     else:
227         data = {
228             "username": None,
229             "timezone": settings.TIME_ZONE,
230             "is_authenticated": False,
231             "is_staff": False,
232             "is_superuser": False,
233             "is_beta_tester": False,
234             "avatar_url": None,
235             "is_subscriber": False,
236         }
237 
238     # Add waffle data to the dict we're going to be returning.
239     # This is what the waffle.wafflejs() template tag does, but we're
240     # doing it via an API instead of hardcoding the settings into
241     # the HTML page. See also from waffle.views._generate_waffle_js.
242     #
243     # Note that if we upgrade django-waffle, version 15 introduces a
244     # pluggable flag model, and the approved way to get all flag
245     # objects will then become:
246     #    get_waffle_flag_model().get_all()
247     #
248     data["waffle"] = {
249         "flags": {f.name: f.is_active(request) for f in Flag.get_all()},
250         "switches": {s.name: s.is_active() for s in Switch.get_all()},
251         "samples": {s.name: s.is_active() for s in Sample.get_all()},
252     }
253     return JsonResponse(data)
254 
255 
256 class APIDocumentSerializer(serializers.Serializer):
257     title = serializers.CharField(read_only=True, max_length=255)
258     slug = serializers.CharField(read_only=True, max_length=255)
259     locale = serializers.CharField(read_only=True, max_length=7)
260     excerpt = serializers.ReadOnlyField(source="get_excerpt")
261 
262 
263 class APILanguageFilterBackend(LanguageFilterBackend):
264     """Override of kuma.search.filters:LanguageFilterBackend that is almost
265     exactly the same except the locale comes from custom code rather than
266     via kuma.core.i18n.get_language_from_request because that can't be used
267     in the API.
268 
269     Basically, it's the same exact functionality but ...
270     """
271 
272     def filter_queryset(self, request, queryset, view):
273         locale = request.GET.get("locale") or settings.LANGUAGE_CODE
274         if locale not in settings.ACCEPTED_LOCALES:
275             raise serializers.ValidationError({"error": "Not a valid locale code"})
276         request.LANGUAGE_CODE = locale
277         return super(APILanguageFilterBackend, self).filter_queryset(
278             request, queryset, view
279         )
280 
281 
282 class APISearchQueryBackend(SearchQueryBackend):
283     """Override of kuma.search.filters.SearchQueryBackend that makes a
284     stink if the 'q' query parameter is falsy."""
285 
286     def filter_queryset(self, request, queryset, view):
287         search_term = (view.query_params.get("q") or "").strip()
288         if not search_term:
289             raise serializers.ValidationError({"error": "Search term 'q' must be set"})
290         return super(APISearchQueryBackend, self).filter_queryset(
291             request, queryset, view
292         )
293 
294 
295 class APISearchView(SearchView):
296     serializer_class = APIDocumentSerializer
297     renderer_classes = [JSONRenderer]
298     filter_backends = (
299         APISearchQueryBackend,
300         KeywordQueryBackend,
301         TagGroupFilterBackend,
302         APILanguageFilterBackend,
303         HighlightFilterBackend,
304     )
305 
306 
307 search = never_cache(APISearchView.as_view())
308 
309 
310 @ratelimit(key="user_or_ip", rate="10/d", block=True)
311 @api_view(["POST"])
312 def bc_signal(request):
313     if not settings.ENABLE_BCD_SIGNAL:
314         return Response("not enabled", status=status.HTTP_400_BAD_REQUEST)
315 
316     serializer = BCSignalSerializer(data=request.data)
317     if serializer.is_valid():
318         serializer.save()
319         return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
320     return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
321 
322 
323 @never_cache
324 @require_safe
325 def get_user(request, username):
326     """
327     Returns a JSON response with a small subset of public information if a
328     user with the given username exists, otherwise returns a status code of
329     404. The case of the username is not important, since the collation of
330     the username column of the user table in MySQL is case-insensitive.
331     """
332     fields = (
333         "username",
334         "title",
335         "fullname",
336         "organization",
337         "location",
338         "timezone",
339         "locale",
340     )
341     try:
342         user = User.objects.only(*fields).get(username=username)
343     except User.DoesNotExist:
344         raise Http404(f'No user exists with the username "{username}".')
345     data = {field: getattr(user, field) for field in fields}
346     data["avatar_url"] = get_avatar_url(user)
347     return JsonResponse(data)
348
```
--- 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
@@ -222,6 +222,7 @@
             "is_subscriber": UserSubscription.objects.filter(
                 user=user, canceled__isnull=True
             ).exists(),
+            "email": user.email,
         }
     else:
         data = {
@@ -233,6 +234,7 @@
             "is_beta_tester": False,
             "avatar_url": None,
             "is_subscriber": False,
+            "email": None,
         }
 
     # Add waffle data to the dict we're going to be returning.

{"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@@ -222,6 +222,7 @@\n             \"is_subscriber\": UserSubscription.objects.filter(\n                 user=user, canceled__isnull=True\n             ).exists(),\n+            \"email\": user.email,\n         }\n     else:\n         data = {\n@@ -233,6 +234,7 @@\n             \"is_beta_tester\": False,\n             \"avatar_url\": None,\n             \"is_subscriber\": False,\n+            \"email\": None,\n         }\n \n     # Add waffle data to the dict we're going to be returning.\n", "issue": "T - Add email to whoami userData so that Stripe form can be prefilled. \n\n", "before_files": [{"content": "from django.conf import settings\nfrom django.http import Http404, HttpResponsePermanentRedirect, JsonResponse\nfrom django.shortcuts import get_object_or_404\nfrom django.utils.translation import activate, gettext as _\nfrom django.views.decorators.cache import never_cache\nfrom django.views.decorators.http import require_GET, require_safe\nfrom ratelimit.decorators import ratelimit\nfrom rest_framework import serializers, status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.response import Response\nfrom waffle.models import Flag, Sample, Switch\n\nfrom kuma.api.v1.serializers import BCSignalSerializer\nfrom kuma.core.urlresolvers import reverse\nfrom kuma.search.filters import (\n    HighlightFilterBackend,\n    KeywordQueryBackend,\n    LanguageFilterBackend,\n    SearchQueryBackend,\n    TagGroupFilterBackend,\n)\nfrom kuma.search.search import SearchView\nfrom kuma.users.models import User, UserSubscription\nfrom kuma.users.templatetags.jinja_helpers import get_avatar_url\nfrom kuma.wiki.models import Document\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(\n    doc=None,\n    locale=None,\n    slug=None,\n    prefix_with_forward_slash=False,\n    suffix_file_extension=True,\n):\n    if doc:\n        locale, slug = doc.locale, doc.slug\n    key = reverse(\"api.v1.doc\", args=(locale, slug))\n    if suffix_file_extension:\n        key += \".json\"\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(\n        locale=locale,\n        slug=slug,\n        prefix_with_forward_slash=True,\n        suffix_file_extension=False,\n    )\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(\n                    redirect_document,\n                    prefix_with_forward_slash=True,\n                    suffix_file_extension=False,\n                ),\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 (\"/\", \"/\" + 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, 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    # 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    )\n    available_locales = {doc.locale} | set(t.locale for t in other_translations)\n\n    doc_absolute_url = doc.get_absolute_url()\n    revision = doc.current_or_latest_revision()\n    translation_status = None\n    if doc.parent_id and revision and revision.localization_in_progress:\n        translation_status = (\n            \"outdated\" if revision.translation_age >= 10 else \"in-progress\"\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_absolute_url,\n            \"wikiURL\": absolutify(doc_absolute_url, for_wiki_site=True),\n            \"editURL\": absolutify(\n                reverse(\"wiki.edit\", args=(doc.slug,), locale=doc.locale),\n                for_wiki_site=True,\n            ),\n            \"translateURL\": (\n                absolutify(\n                    reverse(\"wiki.select_locale\", args=(doc.slug,), locale=doc.locale),\n                    for_wiki_site=True,\n                )\n                if doc.is_localizable\n                else None\n            ),\n            \"translationStatus\": translation_status,\n            \"bodyHTML\": doc.get_body_html(),\n            \"quickLinksHTML\": doc.get_quick_links_html(),\n            \"tocHTML\": doc.get_toc_html(),\n            \"raw\": doc.html,\n            \"parents\": [\n                {\"url\": d.get_absolute_url(), \"title\": d.title} 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                }\n                for t in other_translations\n            ],\n            \"lastModified\": (\n                doc.current_revision and doc.current_revision.created.isoformat()\n            ),\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            \"avatar_url\": get_avatar_url(user),\n            \"is_subscriber\": UserSubscription.objects.filter(\n                user=user, canceled__isnull=True\n            ).exists(),\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            \"avatar_url\": None,\n            \"is_subscriber\": False,\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    return JsonResponse(data)\n\n\nclass APIDocumentSerializer(serializers.Serializer):\n    title = serializers.CharField(read_only=True, max_length=255)\n    slug = serializers.CharField(read_only=True, max_length=255)\n    locale = serializers.CharField(read_only=True, max_length=7)\n    excerpt = serializers.ReadOnlyField(source=\"get_excerpt\")\n\n\nclass APILanguageFilterBackend(LanguageFilterBackend):\n    \"\"\"Override of kuma.search.filters:LanguageFilterBackend that is almost\n    exactly the same except the locale comes from custom code rather than\n    via kuma.core.i18n.get_language_from_request because that can't be used\n    in the API.\n\n    Basically, it's the same exact functionality but ...\n    \"\"\"\n\n    def filter_queryset(self, request, queryset, view):\n        locale = request.GET.get(\"locale\") or settings.LANGUAGE_CODE\n        if locale not in settings.ACCEPTED_LOCALES:\n            raise serializers.ValidationError({\"error\": \"Not a valid locale code\"})\n        request.LANGUAGE_CODE = locale\n        return super(APILanguageFilterBackend, self).filter_queryset(\n            request, queryset, view\n        )\n\n\nclass APISearchQueryBackend(SearchQueryBackend):\n    \"\"\"Override of kuma.search.filters.SearchQueryBackend that makes a\n    stink if the 'q' query parameter is falsy.\"\"\"\n\n    def filter_queryset(self, request, queryset, view):\n        search_term = (view.query_params.get(\"q\") or \"\").strip()\n        if not search_term:\n            raise serializers.ValidationError({\"error\": \"Search term 'q' must be set\"})\n        return super(APISearchQueryBackend, self).filter_queryset(\n            request, queryset, view\n        )\n\n\nclass APISearchView(SearchView):\n    serializer_class = APIDocumentSerializer\n    renderer_classes = [JSONRenderer]\n    filter_backends = (\n        APISearchQueryBackend,\n        KeywordQueryBackend,\n        TagGroupFilterBackend,\n        APILanguageFilterBackend,\n        HighlightFilterBackend,\n    )\n\n\nsearch = never_cache(APISearchView.as_view())\n\n\n@ratelimit(key=\"user_or_ip\", rate=\"10/d\", block=True)\n@api_view([\"POST\"])\ndef bc_signal(request):\n    if not settings.ENABLE_BCD_SIGNAL:\n        return Response(\"not enabled\", status=status.HTTP_400_BAD_REQUEST)\n\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\n\n@never_cache\n@require_safe\ndef get_user(request, username):\n    \"\"\"\n    Returns a JSON response with a small subset of public information if a\n    user with the given username exists, otherwise returns a status code of\n    404. The case of the username is not important, since the collation of\n    the username column of the user table in MySQL is case-insensitive.\n    \"\"\"\n    fields = (\n        \"username\",\n        \"title\",\n        \"fullname\",\n        \"organization\",\n        \"location\",\n        \"timezone\",\n        \"locale\",\n    )\n    try:\n        user = User.objects.only(*fields).get(username=username)\n    except User.DoesNotExist:\n        raise Http404(f'No user exists with the username \"{username}\".')\n    data = {field: getattr(user, field) for field in fields}\n    data[\"avatar_url\"] = get_avatar_url(user)\n    return JsonResponse(data)\n", "path": "kuma/api/v1/views.py"}], "after_files": [{"content": "from django.conf import settings\nfrom django.http import Http404, HttpResponsePermanentRedirect, JsonResponse\nfrom django.shortcuts import get_object_or_404\nfrom django.utils.translation import activate, gettext as _\nfrom django.views.decorators.cache import never_cache\nfrom django.views.decorators.http import require_GET, require_safe\nfrom ratelimit.decorators import ratelimit\nfrom rest_framework import serializers, status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.response import Response\nfrom waffle.models import Flag, Sample, Switch\n\nfrom kuma.api.v1.serializers import BCSignalSerializer\nfrom kuma.core.urlresolvers import reverse\nfrom kuma.search.filters import (\n    HighlightFilterBackend,\n    KeywordQueryBackend,\n    LanguageFilterBackend,\n    SearchQueryBackend,\n    TagGroupFilterBackend,\n)\nfrom kuma.search.search import SearchView\nfrom kuma.users.models import User, UserSubscription\nfrom kuma.users.templatetags.jinja_helpers import get_avatar_url\nfrom kuma.wiki.models import Document\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(\n    doc=None,\n    locale=None,\n    slug=None,\n    prefix_with_forward_slash=False,\n    suffix_file_extension=True,\n):\n    if doc:\n        locale, slug = doc.locale, doc.slug\n    key = reverse(\"api.v1.doc\", args=(locale, slug))\n    if suffix_file_extension:\n        key += \".json\"\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(\n        locale=locale,\n        slug=slug,\n        prefix_with_forward_slash=True,\n        suffix_file_extension=False,\n    )\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(\n                    redirect_document,\n                    prefix_with_forward_slash=True,\n                    suffix_file_extension=False,\n                ),\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 (\"/\", \"/\" + 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, 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    # 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    )\n    available_locales = {doc.locale} | set(t.locale for t in other_translations)\n\n    doc_absolute_url = doc.get_absolute_url()\n    revision = doc.current_or_latest_revision()\n    translation_status = None\n    if doc.parent_id and revision and revision.localization_in_progress:\n        translation_status = (\n            \"outdated\" if revision.translation_age >= 10 else \"in-progress\"\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_absolute_url,\n            \"wikiURL\": absolutify(doc_absolute_url, for_wiki_site=True),\n            \"editURL\": absolutify(\n                reverse(\"wiki.edit\", args=(doc.slug,), locale=doc.locale),\n                for_wiki_site=True,\n            ),\n            \"translateURL\": (\n                absolutify(\n                    reverse(\"wiki.select_locale\", args=(doc.slug,), locale=doc.locale),\n                    for_wiki_site=True,\n                )\n                if doc.is_localizable\n                else None\n            ),\n            \"translationStatus\": translation_status,\n            \"bodyHTML\": doc.get_body_html(),\n            \"quickLinksHTML\": doc.get_quick_links_html(),\n            \"tocHTML\": doc.get_toc_html(),\n            \"raw\": doc.html,\n            \"parents\": [\n                {\"url\": d.get_absolute_url(), \"title\": d.title} 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                }\n                for t in other_translations\n            ],\n            \"lastModified\": (\n                doc.current_revision and doc.current_revision.created.isoformat()\n            ),\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            \"avatar_url\": get_avatar_url(user),\n            \"is_subscriber\": UserSubscription.objects.filter(\n                user=user, canceled__isnull=True\n            ).exists(),\n            \"email\": user.email,\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            \"avatar_url\": None,\n            \"is_subscriber\": False,\n            \"email\": None,\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    return JsonResponse(data)\n\n\nclass APIDocumentSerializer(serializers.Serializer):\n    title = serializers.CharField(read_only=True, max_length=255)\n    slug = serializers.CharField(read_only=True, max_length=255)\n    locale = serializers.CharField(read_only=True, max_length=7)\n    excerpt = serializers.ReadOnlyField(source=\"get_excerpt\")\n\n\nclass APILanguageFilterBackend(LanguageFilterBackend):\n    \"\"\"Override of kuma.search.filters:LanguageFilterBackend that is almost\n    exactly the same except the locale comes from custom code rather than\n    via kuma.core.i18n.get_language_from_request because that can't be used\n    in the API.\n\n    Basically, it's the same exact functionality but ...\n    \"\"\"\n\n    def filter_queryset(self, request, queryset, view):\n        locale = request.GET.get(\"locale\") or settings.LANGUAGE_CODE\n        if locale not in settings.ACCEPTED_LOCALES:\n            raise serializers.ValidationError({\"error\": \"Not a valid locale code\"})\n        request.LANGUAGE_CODE = locale\n        return super(APILanguageFilterBackend, self).filter_queryset(\n            request, queryset, view\n        )\n\n\nclass APISearchQueryBackend(SearchQueryBackend):\n    \"\"\"Override of kuma.search.filters.SearchQueryBackend that makes a\n    stink if the 'q' query parameter is falsy.\"\"\"\n\n    def filter_queryset(self, request, queryset, view):\n        search_term = (view.query_params.get(\"q\") or \"\").strip()\n        if not search_term:\n            raise serializers.ValidationError({\"error\": \"Search term 'q' must be set\"})\n        return super(APISearchQueryBackend, self).filter_queryset(\n            request, queryset, view\n        )\n\n\nclass APISearchView(SearchView):\n    serializer_class = APIDocumentSerializer\n    renderer_classes = [JSONRenderer]\n    filter_backends = (\n        APISearchQueryBackend,\n        KeywordQueryBackend,\n        TagGroupFilterBackend,\n        APILanguageFilterBackend,\n        HighlightFilterBackend,\n    )\n\n\nsearch = never_cache(APISearchView.as_view())\n\n\n@ratelimit(key=\"user_or_ip\", rate=\"10/d\", block=True)\n@api_view([\"POST\"])\ndef bc_signal(request):\n    if not settings.ENABLE_BCD_SIGNAL:\n        return Response(\"not enabled\", status=status.HTTP_400_BAD_REQUEST)\n\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\n\n@never_cache\n@require_safe\ndef get_user(request, username):\n    \"\"\"\n    Returns a JSON response with a small subset of public information if a\n    user with the given username exists, otherwise returns a status code of\n    404. The case of the username is not important, since the collation of\n    the username column of the user table in MySQL is case-insensitive.\n    \"\"\"\n    fields = (\n        \"username\",\n        \"title\",\n        \"fullname\",\n        \"organization\",\n        \"location\",\n        \"timezone\",\n        \"locale\",\n    )\n    try:\n        user = User.objects.only(*fields).get(username=username)\n    except User.DoesNotExist:\n        raise Http404(f'No user exists with the username \"{username}\".')\n    data = {field: getattr(user, field) for field in fields}\n    data[\"avatar_url\"] = get_avatar_url(user)\n    return JsonResponse(data)\n", "path": "kuma/api/v1/views.py"}]}