We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Continuation from #414: comment defaults
On initial page load the comment language does not default to English if no comments are present in the user's language

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nextcloudappstore/core/views.py`
Content:
```
1 from urllib.parse import urlencode
2 
3 from django.conf import settings
4 from django.contrib.auth.mixins import LoginRequiredMixin
5 from django.contrib.auth.models import User
6 from django.core.exceptions import ObjectDoesNotExist
7 from django.db.models import Q
8 from django.http import HttpResponse
9 from django.shortcuts import get_object_or_404, redirect
10 from django.utils.functional import cached_property
11 from django.utils.translation import get_language, get_language_info
12 from django.views.decorators.http import etag
13 from django.views.generic import FormView
14 from django.views.generic.base import TemplateView
15 from django.views.generic.detail import DetailView
16 from django.views.generic.list import ListView
17 from rest_framework.generics import ListAPIView
18 from semantic_version import Version
19 
20 from nextcloudappstore.core.api.v1.serializers import AppRatingSerializer
21 from nextcloudappstore.core.caching import app_etag
22 from nextcloudappstore.core.facades import flatmap
23 from nextcloudappstore.core.forms import AppRatingForm, AppReleaseUploadForm, \
24     AppRegisterForm
25 from nextcloudappstore.core.models import App, Category, AppRating, \
26     NextcloudRelease
27 from nextcloudappstore.core.scaffolding.archive import build_archive
28 from nextcloudappstore.core.scaffolding.forms import AppScaffoldingForm
29 from nextcloudappstore.core.versioning import pad_min_version
30 
31 
32 @etag(app_etag)
33 def app_description(request, id):
34     app = get_object_or_404(App, id=id)
35     return HttpResponse(app.description, content_type='text/plain')
36 
37 
38 class AppRatingApi(ListAPIView):
39     serializer_class = AppRatingSerializer
40 
41     def get_queryset(self):
42         id = self.kwargs.get('id')
43         app = get_object_or_404(App, id=id)
44         return AppRating.objects.language(self.request.LANGUAGE_CODE).filter(
45             app=app)
46 
47 
48 class LegalNoticeView(TemplateView):
49     template_name = 'legal.html'
50 
51 
52 class AppDetailView(DetailView):
53     queryset = App.objects.prefetch_related('releases', 'screenshots', 'owner',
54                                             'co_maintainers')
55     template_name = 'app/detail.html'
56     slug_field = 'id'
57     slug_url_kwarg = 'id'
58 
59     def post(self, request, id):
60         form = AppRatingForm(request.POST, id=id, user=request.user)
61         # there is no way that a rating can be invalid by default
62         if form.is_valid() and request.user.is_authenticated:
63             form.save()
64         return redirect('app-detail', id=id)
65 
66     def get_context_data(self, **kwargs):
67         context = super().get_context_data(**kwargs)
68         context['DISCOURSE_URL'] = settings.DISCOURSE_URL.rstrip('/')
69         context['rating_form'] = AppRatingForm(
70             initial={'language_code': get_language()})
71 
72         ratings = AppRating.objects.filter(app=context['app'])
73         rating_languages = flatmap(
74             lambda r: r.get_available_languages(), ratings)
75 
76         # make sure current session language is in the list even if there are
77         # no comments
78         rating_languages = list(rating_languages)
79         if get_language() not in rating_languages:
80             rating_languages.append(get_language())
81 
82         context['languages'] = sorted(rating_languages)
83         context['user_has_rated_app'] = False
84         if self.request.user.is_authenticated:
85             try:
86                 app_rating = AppRating.objects.get(user=self.request.user,
87                                                    app=context['app'])
88                 # when accessing an empty comment django-parler tries to
89                 # fall back to the default language. However for comments
90                 # the default (English) does not always exist. Unfortunately
91                 # it throws the same exception as non existing models,
92                 # so we need to access it beforehand
93                 try:
94                     comment = app_rating.comment
95                 except AppRating.DoesNotExist:
96                     comment = ''
97 
98                 context['rating_form'] = AppRatingForm({
99                     'rating': app_rating.rating,
100                     'comment': comment,
101                     'language_code': app_rating.get_current_language(),
102                 })
103                 context['user_has_rated_app'] = True
104             except AppRating.DoesNotExist:
105                 pass
106         context['categories'] = Category.objects.prefetch_related(
107             'translations').all()
108         context['latest_releases_by_platform_v'] = \
109             self.object.latest_releases_by_platform_v()
110         return context
111 
112 
113 class AppReleasesView(DetailView):
114     queryset = App.objects.prefetch_related(
115         'releases',
116         'releases__databases',
117         'releases__licenses',
118         'releases__phpextensiondependencies__php_extension',
119         'releases__databasedependencies__database',
120         'releases__shell_commands'
121     )
122     template_name = 'app/releases.html'
123     slug_field = 'id'
124     slug_url_kwarg = 'id'
125 
126     def get_context_data(self, **kwargs):
127         context = super().get_context_data(**kwargs)
128         context['categories'] = Category.objects.prefetch_related(
129             'translations').all()
130 
131         releases = self.object.releases_by_platform_v()
132         unstables = self.object.unstable_releases_by_platform_v()
133         versions = set(list(releases.keys()) + list(unstables.keys()))
134         all_releases = list(map(
135             lambda v: (v, releases.get(v, []) + unstables.get(v, [])),
136             versions))
137         context['releases_by_platform_v'] = \
138             self._sort_by_platform_v(all_releases)
139         return context
140 
141     def _sort_by_platform_v(self, releases_by_platform, reverse=True):
142         """Sorts a list of tuples like (<platform version>, [releases]) by
143         platform version.
144 
145         :param releases_by_platform: A list of tuples.
146         :param reverse: Descending order if True, ascending otherwise.
147         :return sorted list of tuples.
148         """
149 
150         return sorted(releases_by_platform, reverse=reverse,
151                       key=lambda v: Version(pad_min_version(v[0])))
152 
153 
154 class CategoryAppListView(ListView):
155     model = App
156     template_name = 'app/list.html'
157     allow_empty = True
158 
159     def get_queryset(self):
160         order_by = self.request.GET.get('order_by', 'rating_overall')
161         ordering = self.request.GET.get('ordering', 'desc')
162         is_featured = self.request.GET.get('is_featured', False)
163         maintainer = self.request.GET.get('maintainer', False)
164         sort_columns = []
165 
166         allowed_order_by = {'name', 'last_release', 'rating_overall',
167                             'rating_recent'}
168         if order_by in allowed_order_by:
169             if order_by == 'name':
170                 order_by = 'translations__name'
171             if ordering == 'desc':
172                 sort_columns.append('-' + order_by)
173             else:
174                 sort_columns.append(order_by)
175 
176         lang = get_language_info(get_language())['code']
177         category_id = self.kwargs['id']
178         queryset = App.objects.search(self.search_terms, lang).order_by(
179             *sort_columns).filter(releases__gt=0)
180         if maintainer:
181             try:
182                 user = User.objects.get_by_natural_key(maintainer)
183                 queryset = queryset.filter(Q(owner=user) |
184                                            Q(co_maintainers=user))
185             except ObjectDoesNotExist:
186                 return queryset.none()
187         if category_id:
188             queryset = queryset.filter(categories__id=category_id)
189         if is_featured == "true":
190             queryset = queryset.filter(is_featured=True)
191         return queryset.prefetch_related('screenshots', 'translations')
192 
193     def get_context_data(self, **kwargs):
194         context = super().get_context_data(**kwargs)
195         context['categories'] = Category.objects.prefetch_related(
196             'translations').all()
197         category_id = self.kwargs['id']
198         if category_id:
199             context['current_category'] = Category.objects.get(id=category_id)
200         if self.search_terms:
201             context['search_query'] = ' '.join(self.search_terms)
202         context['url_params'] = self.url_params
203         return context
204 
205     @cached_property
206     def url_params(self):
207         """URL encoded strings with the GET params of the last request.
208 
209         Intended for preserving GET params upon clicking a link by including
210         one (and only one) of these strings in the "href" attribute.
211 
212         The parameters are divided into three groups: search, filters and
213         ordering. In addition to these three, the returned dict also contains
214         some combinations of them, as specified by the dict keys.
215 
216         No leading "?" or "&".
217 
218         :return dict with URL encoded strings.
219         """
220 
221         search = self._url_params_str('search')
222         filters = self._url_params_str('is_featured', 'maintainer')
223         ordering = self._url_params_str('order_by', 'ordering')
224 
225         return {
226             'search': search,
227             'filters': filters,
228             'ordering': ordering,
229             'search_filters': self._join_url_params_strs(search, filters),
230             'filters_ordering': self._join_url_params_strs(filters, ordering),
231         }
232 
233     def _url_params_str(self, *params):
234         args = map(lambda param: (param, self.request.GET.get(param, '')),
235                    params)
236         present_args = filter(lambda a: a[1], args)
237         return urlencode(dict(present_args))
238 
239     def _join_url_params_strs(self, *strings):
240         return '&'.join(filter(None, strings))
241 
242     @cached_property
243     def search_terms(self):
244         return self.request.GET.get('search', '').strip().split()
245 
246 
247 class AppUploadView(LoginRequiredMixin, TemplateView):
248     template_name = 'app/upload.html'
249 
250     def get_context_data(self, **kwargs):
251         context = super().get_context_data(**kwargs)
252         context['form'] = AppReleaseUploadForm()
253         return context
254 
255 
256 class AppScaffoldingView(FormView):
257     template_name = 'app/scaffold.html'
258     form_class = AppScaffoldingForm
259 
260     def get_initial(self):
261         init = {
262             'platform': NextcloudRelease.get_current_main(),
263             'categories': ('tools',)
264         }
265         if self.request.user.is_authenticated:
266             user = self.request.user
267             init['author_name'] = '%s %s' % (user.first_name, user.last_name)
268             init['author_email'] = user.email
269         return init
270 
271     def form_valid(self, form):
272         buffer = build_archive(form.cleaned_data)
273         response = HttpResponse(content_type='application/tar+gzip')
274         response['Content-Disposition'] = 'attachment; filename="app.tar.gz"'
275         value = buffer.getvalue()
276         buffer.close()
277         response.write(value)
278         return response
279 
280 
281 class AppRegisterView(LoginRequiredMixin, TemplateView):
282     template_name = 'app/register.html'
283 
284     def get_context_data(self, **kwargs):
285         context = super().get_context_data(**kwargs)
286         context['form'] = AppRegisterForm()
287         return context
288
```
--- 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/nextcloudappstore/core/views.py b/nextcloudappstore/core/views.py
--- a/nextcloudappstore/core/views.py
+++ b/nextcloudappstore/core/views.py
@@ -74,7 +74,7 @@
             lambda r: r.get_available_languages(), ratings)
 
         # make sure current session language is in the list even if there are
-        # no comments
+        # no comments.
         rating_languages = list(rating_languages)
         if get_language() not in rating_languages:
             rating_languages.append(get_language())
@@ -85,6 +85,18 @@
             try:
                 app_rating = AppRating.objects.get(user=self.request.user,
                                                    app=context['app'])
+
+                # if parler fallsback to a fallback language
+                # it doesn't set the language as current language
+                # and we can't select the correct language in the
+                # frontend. So we try and find a languge that is
+                # available
+                language_code = app_rating.get_current_language()
+                if not app_rating.has_translation(language_code):
+                    for fallback in app_rating.get_fallback_languages():
+                        if app_rating.has_translation(fallback):
+                            app_rating.set_current_language(fallback)
+
                 # when accessing an empty comment django-parler tries to
                 # fall back to the default language. However for comments
                 # the default (English) does not always exist. Unfortunately

{"golden_diff": "diff --git a/nextcloudappstore/core/views.py b/nextcloudappstore/core/views.py\n--- a/nextcloudappstore/core/views.py\n+++ b/nextcloudappstore/core/views.py\n@@ -74,7 +74,7 @@\n             lambda r: r.get_available_languages(), ratings)\n \n         # make sure current session language is in the list even if there are\n-        # no comments\n+        # no comments.\n         rating_languages = list(rating_languages)\n         if get_language() not in rating_languages:\n             rating_languages.append(get_language())\n@@ -85,6 +85,18 @@\n             try:\n                 app_rating = AppRating.objects.get(user=self.request.user,\n                                                    app=context['app'])\n+\n+                # if parler fallsback to a fallback language\n+                # it doesn't set the language as current language\n+                # and we can't select the correct language in the\n+                # frontend. So we try and find a languge that is\n+                # available\n+                language_code = app_rating.get_current_language()\n+                if not app_rating.has_translation(language_code):\n+                    for fallback in app_rating.get_fallback_languages():\n+                        if app_rating.has_translation(fallback):\n+                            app_rating.set_current_language(fallback)\n+\n                 # when accessing an empty comment django-parler tries to\n                 # fall back to the default language. However for comments\n                 # the default (English) does not always exist. Unfortunately\n", "issue": "Continuation from #414: comment defaults\nOn initial page load the comment language does not default to English if no comments are present in the user's language\n", "before_files": [{"content": "from urllib.parse import urlencode\n\nfrom django.conf import settings\nfrom django.contrib.auth.mixins import LoginRequiredMixin\nfrom django.contrib.auth.models import User\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db.models import Q\nfrom django.http import HttpResponse\nfrom django.shortcuts import get_object_or_404, redirect\nfrom django.utils.functional import cached_property\nfrom django.utils.translation import get_language, get_language_info\nfrom django.views.decorators.http import etag\nfrom django.views.generic import FormView\nfrom django.views.generic.base import TemplateView\nfrom django.views.generic.detail import DetailView\nfrom django.views.generic.list import ListView\nfrom rest_framework.generics import ListAPIView\nfrom semantic_version import Version\n\nfrom nextcloudappstore.core.api.v1.serializers import AppRatingSerializer\nfrom nextcloudappstore.core.caching import app_etag\nfrom nextcloudappstore.core.facades import flatmap\nfrom nextcloudappstore.core.forms import AppRatingForm, AppReleaseUploadForm, \\\n    AppRegisterForm\nfrom nextcloudappstore.core.models import App, Category, AppRating, \\\n    NextcloudRelease\nfrom nextcloudappstore.core.scaffolding.archive import build_archive\nfrom nextcloudappstore.core.scaffolding.forms import AppScaffoldingForm\nfrom nextcloudappstore.core.versioning import pad_min_version\n\n\n@etag(app_etag)\ndef app_description(request, id):\n    app = get_object_or_404(App, id=id)\n    return HttpResponse(app.description, content_type='text/plain')\n\n\nclass AppRatingApi(ListAPIView):\n    serializer_class = AppRatingSerializer\n\n    def get_queryset(self):\n        id = self.kwargs.get('id')\n        app = get_object_or_404(App, id=id)\n        return AppRating.objects.language(self.request.LANGUAGE_CODE).filter(\n            app=app)\n\n\nclass LegalNoticeView(TemplateView):\n    template_name = 'legal.html'\n\n\nclass AppDetailView(DetailView):\n    queryset = App.objects.prefetch_related('releases', 'screenshots', 'owner',\n                                            'co_maintainers')\n    template_name = 'app/detail.html'\n    slug_field = 'id'\n    slug_url_kwarg = 'id'\n\n    def post(self, request, id):\n        form = AppRatingForm(request.POST, id=id, user=request.user)\n        # there is no way that a rating can be invalid by default\n        if form.is_valid() and request.user.is_authenticated:\n            form.save()\n        return redirect('app-detail', id=id)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['DISCOURSE_URL'] = settings.DISCOURSE_URL.rstrip('/')\n        context['rating_form'] = AppRatingForm(\n            initial={'language_code': get_language()})\n\n        ratings = AppRating.objects.filter(app=context['app'])\n        rating_languages = flatmap(\n            lambda r: r.get_available_languages(), ratings)\n\n        # make sure current session language is in the list even if there are\n        # no comments\n        rating_languages = list(rating_languages)\n        if get_language() not in rating_languages:\n            rating_languages.append(get_language())\n\n        context['languages'] = sorted(rating_languages)\n        context['user_has_rated_app'] = False\n        if self.request.user.is_authenticated:\n            try:\n                app_rating = AppRating.objects.get(user=self.request.user,\n                                                   app=context['app'])\n                # when accessing an empty comment django-parler tries to\n                # fall back to the default language. However for comments\n                # the default (English) does not always exist. Unfortunately\n                # it throws the same exception as non existing models,\n                # so we need to access it beforehand\n                try:\n                    comment = app_rating.comment\n                except AppRating.DoesNotExist:\n                    comment = ''\n\n                context['rating_form'] = AppRatingForm({\n                    'rating': app_rating.rating,\n                    'comment': comment,\n                    'language_code': app_rating.get_current_language(),\n                })\n                context['user_has_rated_app'] = True\n            except AppRating.DoesNotExist:\n                pass\n        context['categories'] = Category.objects.prefetch_related(\n            'translations').all()\n        context['latest_releases_by_platform_v'] = \\\n            self.object.latest_releases_by_platform_v()\n        return context\n\n\nclass AppReleasesView(DetailView):\n    queryset = App.objects.prefetch_related(\n        'releases',\n        'releases__databases',\n        'releases__licenses',\n        'releases__phpextensiondependencies__php_extension',\n        'releases__databasedependencies__database',\n        'releases__shell_commands'\n    )\n    template_name = 'app/releases.html'\n    slug_field = 'id'\n    slug_url_kwarg = 'id'\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['categories'] = Category.objects.prefetch_related(\n            'translations').all()\n\n        releases = self.object.releases_by_platform_v()\n        unstables = self.object.unstable_releases_by_platform_v()\n        versions = set(list(releases.keys()) + list(unstables.keys()))\n        all_releases = list(map(\n            lambda v: (v, releases.get(v, []) + unstables.get(v, [])),\n            versions))\n        context['releases_by_platform_v'] = \\\n            self._sort_by_platform_v(all_releases)\n        return context\n\n    def _sort_by_platform_v(self, releases_by_platform, reverse=True):\n        \"\"\"Sorts a list of tuples like (<platform version>, [releases]) by\n        platform version.\n\n        :param releases_by_platform: A list of tuples.\n        :param reverse: Descending order if True, ascending otherwise.\n        :return sorted list of tuples.\n        \"\"\"\n\n        return sorted(releases_by_platform, reverse=reverse,\n                      key=lambda v: Version(pad_min_version(v[0])))\n\n\nclass CategoryAppListView(ListView):\n    model = App\n    template_name = 'app/list.html'\n    allow_empty = True\n\n    def get_queryset(self):\n        order_by = self.request.GET.get('order_by', 'rating_overall')\n        ordering = self.request.GET.get('ordering', 'desc')\n        is_featured = self.request.GET.get('is_featured', False)\n        maintainer = self.request.GET.get('maintainer', False)\n        sort_columns = []\n\n        allowed_order_by = {'name', 'last_release', 'rating_overall',\n                            'rating_recent'}\n        if order_by in allowed_order_by:\n            if order_by == 'name':\n                order_by = 'translations__name'\n            if ordering == 'desc':\n                sort_columns.append('-' + order_by)\n            else:\n                sort_columns.append(order_by)\n\n        lang = get_language_info(get_language())['code']\n        category_id = self.kwargs['id']\n        queryset = App.objects.search(self.search_terms, lang).order_by(\n            *sort_columns).filter(releases__gt=0)\n        if maintainer:\n            try:\n                user = User.objects.get_by_natural_key(maintainer)\n                queryset = queryset.filter(Q(owner=user) |\n                                           Q(co_maintainers=user))\n            except ObjectDoesNotExist:\n                return queryset.none()\n        if category_id:\n            queryset = queryset.filter(categories__id=category_id)\n        if is_featured == \"true\":\n            queryset = queryset.filter(is_featured=True)\n        return queryset.prefetch_related('screenshots', 'translations')\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['categories'] = Category.objects.prefetch_related(\n            'translations').all()\n        category_id = self.kwargs['id']\n        if category_id:\n            context['current_category'] = Category.objects.get(id=category_id)\n        if self.search_terms:\n            context['search_query'] = ' '.join(self.search_terms)\n        context['url_params'] = self.url_params\n        return context\n\n    @cached_property\n    def url_params(self):\n        \"\"\"URL encoded strings with the GET params of the last request.\n\n        Intended for preserving GET params upon clicking a link by including\n        one (and only one) of these strings in the \"href\" attribute.\n\n        The parameters are divided into three groups: search, filters and\n        ordering. In addition to these three, the returned dict also contains\n        some combinations of them, as specified by the dict keys.\n\n        No leading \"?\" or \"&\".\n\n        :return dict with URL encoded strings.\n        \"\"\"\n\n        search = self._url_params_str('search')\n        filters = self._url_params_str('is_featured', 'maintainer')\n        ordering = self._url_params_str('order_by', 'ordering')\n\n        return {\n            'search': search,\n            'filters': filters,\n            'ordering': ordering,\n            'search_filters': self._join_url_params_strs(search, filters),\n            'filters_ordering': self._join_url_params_strs(filters, ordering),\n        }\n\n    def _url_params_str(self, *params):\n        args = map(lambda param: (param, self.request.GET.get(param, '')),\n                   params)\n        present_args = filter(lambda a: a[1], args)\n        return urlencode(dict(present_args))\n\n    def _join_url_params_strs(self, *strings):\n        return '&'.join(filter(None, strings))\n\n    @cached_property\n    def search_terms(self):\n        return self.request.GET.get('search', '').strip().split()\n\n\nclass AppUploadView(LoginRequiredMixin, TemplateView):\n    template_name = 'app/upload.html'\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['form'] = AppReleaseUploadForm()\n        return context\n\n\nclass AppScaffoldingView(FormView):\n    template_name = 'app/scaffold.html'\n    form_class = AppScaffoldingForm\n\n    def get_initial(self):\n        init = {\n            'platform': NextcloudRelease.get_current_main(),\n            'categories': ('tools',)\n        }\n        if self.request.user.is_authenticated:\n            user = self.request.user\n            init['author_name'] = '%s %s' % (user.first_name, user.last_name)\n            init['author_email'] = user.email\n        return init\n\n    def form_valid(self, form):\n        buffer = build_archive(form.cleaned_data)\n        response = HttpResponse(content_type='application/tar+gzip')\n        response['Content-Disposition'] = 'attachment; filename=\"app.tar.gz\"'\n        value = buffer.getvalue()\n        buffer.close()\n        response.write(value)\n        return response\n\n\nclass AppRegisterView(LoginRequiredMixin, TemplateView):\n    template_name = 'app/register.html'\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['form'] = AppRegisterForm()\n        return context\n", "path": "nextcloudappstore/core/views.py"}], "after_files": [{"content": "from urllib.parse import urlencode\n\nfrom django.conf import settings\nfrom django.contrib.auth.mixins import LoginRequiredMixin\nfrom django.contrib.auth.models import User\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db.models import Q\nfrom django.http import HttpResponse\nfrom django.shortcuts import get_object_or_404, redirect\nfrom django.utils.functional import cached_property\nfrom django.utils.translation import get_language, get_language_info\nfrom django.views.decorators.http import etag\nfrom django.views.generic import FormView\nfrom django.views.generic.base import TemplateView\nfrom django.views.generic.detail import DetailView\nfrom django.views.generic.list import ListView\nfrom rest_framework.generics import ListAPIView\nfrom semantic_version import Version\n\nfrom nextcloudappstore.core.api.v1.serializers import AppRatingSerializer\nfrom nextcloudappstore.core.caching import app_etag\nfrom nextcloudappstore.core.facades import flatmap\nfrom nextcloudappstore.core.forms import AppRatingForm, AppReleaseUploadForm, \\\n    AppRegisterForm\nfrom nextcloudappstore.core.models import App, Category, AppRating, \\\n    NextcloudRelease\nfrom nextcloudappstore.core.scaffolding.archive import build_archive\nfrom nextcloudappstore.core.scaffolding.forms import AppScaffoldingForm\nfrom nextcloudappstore.core.versioning import pad_min_version\n\n\n@etag(app_etag)\ndef app_description(request, id):\n    app = get_object_or_404(App, id=id)\n    return HttpResponse(app.description, content_type='text/plain')\n\n\nclass AppRatingApi(ListAPIView):\n    serializer_class = AppRatingSerializer\n\n    def get_queryset(self):\n        id = self.kwargs.get('id')\n        app = get_object_or_404(App, id=id)\n        return AppRating.objects.language(self.request.LANGUAGE_CODE).filter(\n            app=app)\n\n\nclass LegalNoticeView(TemplateView):\n    template_name = 'legal.html'\n\n\nclass AppDetailView(DetailView):\n    queryset = App.objects.prefetch_related('releases', 'screenshots', 'owner',\n                                            'co_maintainers')\n    template_name = 'app/detail.html'\n    slug_field = 'id'\n    slug_url_kwarg = 'id'\n\n    def post(self, request, id):\n        form = AppRatingForm(request.POST, id=id, user=request.user)\n        # there is no way that a rating can be invalid by default\n        if form.is_valid() and request.user.is_authenticated:\n            form.save()\n        return redirect('app-detail', id=id)\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['DISCOURSE_URL'] = settings.DISCOURSE_URL.rstrip('/')\n        context['rating_form'] = AppRatingForm(\n            initial={'language_code': get_language()})\n\n        ratings = AppRating.objects.filter(app=context['app'])\n        rating_languages = flatmap(\n            lambda r: r.get_available_languages(), ratings)\n\n        # make sure current session language is in the list even if there are\n        # no comments.\n        rating_languages = list(rating_languages)\n        if get_language() not in rating_languages:\n            rating_languages.append(get_language())\n\n        context['languages'] = sorted(rating_languages)\n        context['user_has_rated_app'] = False\n        if self.request.user.is_authenticated:\n            try:\n                app_rating = AppRating.objects.get(user=self.request.user,\n                                                   app=context['app'])\n\n                # if parler fallsback to a fallback language\n                # it doesn't set the language as current language\n                # and we can't select the correct language in the\n                # frontend. So we try and find a languge that is\n                # available\n                language_code = app_rating.get_current_language()\n                if not app_rating.has_translation(language_code):\n                    for fallback in app_rating.get_fallback_languages():\n                        if app_rating.has_translation(fallback):\n                            app_rating.set_current_language(fallback)\n\n                # when accessing an empty comment django-parler tries to\n                # fall back to the default language. However for comments\n                # the default (English) does not always exist. Unfortunately\n                # it throws the same exception as non existing models,\n                # so we need to access it beforehand\n                try:\n                    comment = app_rating.comment\n                except AppRating.DoesNotExist:\n                    comment = ''\n\n                context['rating_form'] = AppRatingForm({\n                    'rating': app_rating.rating,\n                    'comment': comment,\n                    'language_code': app_rating.get_current_language(),\n                })\n                context['user_has_rated_app'] = True\n            except AppRating.DoesNotExist:\n                pass\n        context['categories'] = Category.objects.prefetch_related(\n            'translations').all()\n        context['latest_releases_by_platform_v'] = \\\n            self.object.latest_releases_by_platform_v()\n        return context\n\n\nclass AppReleasesView(DetailView):\n    queryset = App.objects.prefetch_related(\n        'releases',\n        'releases__databases',\n        'releases__licenses',\n        'releases__phpextensiondependencies__php_extension',\n        'releases__databasedependencies__database',\n        'releases__shell_commands'\n    )\n    template_name = 'app/releases.html'\n    slug_field = 'id'\n    slug_url_kwarg = 'id'\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['categories'] = Category.objects.prefetch_related(\n            'translations').all()\n\n        releases = self.object.releases_by_platform_v()\n        unstables = self.object.unstable_releases_by_platform_v()\n        versions = set(list(releases.keys()) + list(unstables.keys()))\n        all_releases = list(map(\n            lambda v: (v, releases.get(v, []) + unstables.get(v, [])),\n            versions))\n        context['releases_by_platform_v'] = \\\n            self._sort_by_platform_v(all_releases)\n        return context\n\n    def _sort_by_platform_v(self, releases_by_platform, reverse=True):\n        \"\"\"Sorts a list of tuples like (<platform version>, [releases]) by\n        platform version.\n\n        :param releases_by_platform: A list of tuples.\n        :param reverse: Descending order if True, ascending otherwise.\n        :return sorted list of tuples.\n        \"\"\"\n\n        return sorted(releases_by_platform, reverse=reverse,\n                      key=lambda v: Version(pad_min_version(v[0])))\n\n\nclass CategoryAppListView(ListView):\n    model = App\n    template_name = 'app/list.html'\n    allow_empty = True\n\n    def get_queryset(self):\n        order_by = self.request.GET.get('order_by', 'rating_overall')\n        ordering = self.request.GET.get('ordering', 'desc')\n        is_featured = self.request.GET.get('is_featured', False)\n        maintainer = self.request.GET.get('maintainer', False)\n        sort_columns = []\n\n        allowed_order_by = {'name', 'last_release', 'rating_overall',\n                            'rating_recent'}\n        if order_by in allowed_order_by:\n            if order_by == 'name':\n                order_by = 'translations__name'\n            if ordering == 'desc':\n                sort_columns.append('-' + order_by)\n            else:\n                sort_columns.append(order_by)\n\n        lang = get_language_info(get_language())['code']\n        category_id = self.kwargs['id']\n        queryset = App.objects.search(self.search_terms, lang).order_by(\n            *sort_columns).filter(releases__gt=0)\n        if maintainer:\n            try:\n                user = User.objects.get_by_natural_key(maintainer)\n                queryset = queryset.filter(Q(owner=user) |\n                                           Q(co_maintainers=user))\n            except ObjectDoesNotExist:\n                return queryset.none()\n        if category_id:\n            queryset = queryset.filter(categories__id=category_id)\n        if is_featured == \"true\":\n            queryset = queryset.filter(is_featured=True)\n        return queryset.prefetch_related('screenshots', 'translations')\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['categories'] = Category.objects.prefetch_related(\n            'translations').all()\n        category_id = self.kwargs['id']\n        if category_id:\n            context['current_category'] = Category.objects.get(id=category_id)\n        if self.search_terms:\n            context['search_query'] = ' '.join(self.search_terms)\n        context['url_params'] = self.url_params\n        return context\n\n    @cached_property\n    def url_params(self):\n        \"\"\"URL encoded strings with the GET params of the last request.\n\n        Intended for preserving GET params upon clicking a link by including\n        one (and only one) of these strings in the \"href\" attribute.\n\n        The parameters are divided into three groups: search, filters and\n        ordering. In addition to these three, the returned dict also contains\n        some combinations of them, as specified by the dict keys.\n\n        No leading \"?\" or \"&\".\n\n        :return dict with URL encoded strings.\n        \"\"\"\n\n        search = self._url_params_str('search')\n        filters = self._url_params_str('is_featured', 'maintainer')\n        ordering = self._url_params_str('order_by', 'ordering')\n\n        return {\n            'search': search,\n            'filters': filters,\n            'ordering': ordering,\n            'search_filters': self._join_url_params_strs(search, filters),\n            'filters_ordering': self._join_url_params_strs(filters, ordering),\n        }\n\n    def _url_params_str(self, *params):\n        args = map(lambda param: (param, self.request.GET.get(param, '')),\n                   params)\n        present_args = filter(lambda a: a[1], args)\n        return urlencode(dict(present_args))\n\n    def _join_url_params_strs(self, *strings):\n        return '&'.join(filter(None, strings))\n\n    @cached_property\n    def search_terms(self):\n        return self.request.GET.get('search', '').strip().split()\n\n\nclass AppUploadView(LoginRequiredMixin, TemplateView):\n    template_name = 'app/upload.html'\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['form'] = AppReleaseUploadForm()\n        return context\n\n\nclass AppScaffoldingView(FormView):\n    template_name = 'app/scaffold.html'\n    form_class = AppScaffoldingForm\n\n    def get_initial(self):\n        init = {\n            'platform': NextcloudRelease.get_current_main(),\n            'categories': ('tools',)\n        }\n        if self.request.user.is_authenticated:\n            user = self.request.user\n            init['author_name'] = '%s %s' % (user.first_name, user.last_name)\n            init['author_email'] = user.email\n        return init\n\n    def form_valid(self, form):\n        buffer = build_archive(form.cleaned_data)\n        response = HttpResponse(content_type='application/tar+gzip')\n        response['Content-Disposition'] = 'attachment; filename=\"app.tar.gz\"'\n        value = buffer.getvalue()\n        buffer.close()\n        response.write(value)\n        return response\n\n\nclass AppRegisterView(LoginRequiredMixin, TemplateView):\n    template_name = 'app/register.html'\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['form'] = AppRegisterForm()\n        return context\n", "path": "nextcloudappstore/core/views.py"}]}