We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Apps without releases should not be rendered on the app list page


--- 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.contrib.auth.models import User
4 from django.core.exceptions import ObjectDoesNotExist
5 from django.db.models import Q
6 from django.http import HttpResponse
7 from django.shortcuts import get_object_or_404, redirect
8 from django.utils.functional import cached_property
9 from django.utils.translation import get_language, get_language_info
10 from django.views.generic.base import TemplateView
11 from django.views.generic.detail import DetailView
12 from django.views.generic.list import ListView
13 from rest_framework.generics import ListAPIView
14 from semantic_version import Version
15 
16 from nextcloudappstore.core.api.v1.serializers import AppRatingSerializer
17 from nextcloudappstore.core.forms import AppRatingForm
18 from nextcloudappstore.core.models import App, Category, AppRating
19 from nextcloudappstore.core.versioning import pad_min_version
20 
21 
22 def app_description(request, id):
23     app = get_object_or_404(App, id=id)
24     return HttpResponse(app.description, content_type='text/plain')
25 
26 
27 class AppRatingApi(ListAPIView):
28     serializer_class = AppRatingSerializer
29 
30     def get_queryset(self):
31         id = self.kwargs.get('id')
32         app = get_object_or_404(App, id=id)
33         return AppRating.objects.language(self.request.LANGUAGE_CODE).filter(
34             app=app)
35 
36 
37 class LegalNoticeView(TemplateView):
38     template_name = 'legal.html'
39 
40 
41 class AppDetailView(DetailView):
42     model = App
43     template_name = 'app/detail.html'
44     slug_field = 'id'
45     slug_url_kwarg = 'id'
46 
47     def post(self, request, id):
48         form = AppRatingForm(request.POST, id=id, user=request.user,
49                              language_code=request.LANGUAGE_CODE)
50         # there is no way that a rating can be invalid by default
51         if form.is_valid() and request.user.is_authenticated():
52             form.save()
53         return redirect('app-detail', id=id)
54 
55     def get_context_data(self, **kwargs):
56         context = super().get_context_data(**kwargs)
57         context['rating_form'] = AppRatingForm()
58         context['user_has_rated_app'] = False
59         if self.request.user.is_authenticated():
60             try:
61                 app_rating = AppRating.objects.get(user=self.request.user,
62                                                    app=context['app'])
63                 # when accessing an empty comment django-parler tries to
64                 # fall back to the default language. However for comments
65                 # the default (English) does not always exist. Unfortunately
66                 # it throws the same exception as non existing models,
67                 # so we need to access it beforehand
68                 try:
69                     comment = app_rating.comment
70                 except AppRating.DoesNotExist:
71                     comment = ''
72 
73                 context['rating_form'] = AppRatingForm(initial={
74                     'rating': app_rating.rating,
75                     'comment': comment
76                 })
77                 context['user_has_rated_app'] = True
78             except AppRating.DoesNotExist:
79                 pass
80         context['categories'] = Category.objects.all()
81         context['latest_releases_by_platform_v'] = \
82             self.object.latest_releases_by_platform_v()
83         return context
84 
85 
86 class AppReleasesView(DetailView):
87     model = App
88     template_name = 'app/releases.html'
89     slug_field = 'id'
90     slug_url_kwarg = 'id'
91 
92     def get_context_data(self, **kwargs):
93         context = super().get_context_data(**kwargs)
94         context['categories'] = Category.objects.all()
95 
96         releases = self.object.releases_by_platform_v()
97         nightlies = self.object.nightly_releases_by_platform_v()
98         versions = set(list(releases.keys()) + list(nightlies.keys()))
99         all_releases = list(map(
100             lambda v: (v, releases.get(v, []) + nightlies.get(v, [])),
101             versions))
102         context['releases_by_platform_v'] = \
103             self._sort_by_platform_v(all_releases)
104 
105         return context
106 
107     def _sort_by_platform_v(self, releases_by_platform, reverse=True):
108         """Sorts a list of tuples like (<platform version>, [releases]) by
109         platform version.
110 
111         :param releases_by_platform: A list of tuples.
112         :param reverse: Descending order if True, ascending otherwise.
113         :return sorted list of tuples.
114         """
115 
116         return sorted(releases_by_platform, reverse=reverse,
117                       key=lambda v: Version(pad_min_version(v[0])))
118 
119 
120 class CategoryAppListView(ListView):
121     model = App
122     template_name = 'app/list.html'
123     allow_empty = True
124 
125     def get_queryset(self):
126         order_by = self.request.GET.get('order_by', 'rating_overall')
127         ordering = self.request.GET.get('ordering', 'desc')
128         featured = self.request.GET.get('featured', False)
129         maintainer = self.request.GET.get('maintainer', False)
130         sort_columns = []
131 
132         allowed_order_by = {'name', 'last_release', 'rating_overall',
133                             'rating_recent'}
134         if order_by in allowed_order_by:
135             if order_by == 'name':
136                 order_by = 'translations__name'
137             if ordering == 'desc':
138                 sort_columns.append('-' + order_by)
139             else:
140                 sort_columns.append(order_by)
141 
142         lang = get_language_info(get_language())['code']
143         category_id = self.kwargs['id']
144         queryset = App.objects.search(self.search_terms, lang).order_by(
145             *sort_columns)
146         if maintainer:
147             try:
148                 user = User.objects.get_by_natural_key(maintainer)
149                 queryset = queryset.filter(Q(owner=user) |
150                                            Q(co_maintainers=user))
151             except ObjectDoesNotExist:
152                 return queryset.none()
153         if category_id:
154             queryset = queryset.filter(categories__id=category_id)
155         if featured == "true":
156             queryset = queryset.filter(featured=True)
157         return queryset
158 
159     def get_context_data(self, **kwargs):
160         context = super().get_context_data(**kwargs)
161         context['categories'] = Category.objects.all()
162         category_id = self.kwargs['id']
163         if category_id:
164             context['current_category'] = Category.objects.get(id=category_id)
165         if self.search_terms:
166             context['search_query'] = ' '.join(self.search_terms)
167         context['url_params'] = self.url_params
168         return context
169 
170     @cached_property
171     def url_params(self):
172         """URL encoded strings with the GET params of the last request.
173 
174         Intended for preserving GET params upon clicking a link by including
175         one (and only one) of these strings in the "href" attribute.
176 
177         The parameters are divided into three groups: search, filters and
178         ordering. In addition to these three, the returned dict also contains
179         some combinations of them, as specified by the dict keys.
180 
181         No leading "?" or "&".
182 
183         :return dict with URL encoded strings.
184         """
185 
186         search = self._url_params_str('search')
187         filters = self._url_params_str('featured', 'maintainer')
188         ordering = self._url_params_str('order_by', 'ordering')
189 
190         return {
191             'search': search,
192             'filters': filters,
193             'ordering': ordering,
194             'search_filters': self._join_url_params_strs(search, filters),
195             'filters_ordering': self._join_url_params_strs(filters, ordering),
196         }
197 
198     def _url_params_str(self, *params):
199         args = map(lambda param: (param, self.request.GET.get(param, '')),
200                    params)
201         present_args = filter(lambda a: a[1], args)
202         return urlencode(dict(present_args))
203 
204     def _join_url_params_strs(self, *strings):
205         return '&'.join(filter(None, strings))
206 
207     @cached_property
208     def search_terms(self):
209         return self.request.GET.get('search', '').strip().split()
210 
211 
212 class AppUploadView(TemplateView):
213     template_name = 'app/upload.html'
214
```
--- 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
@@ -142,7 +142,7 @@
         lang = get_language_info(get_language())['code']
         category_id = self.kwargs['id']
         queryset = App.objects.search(self.search_terms, lang).order_by(
-            *sort_columns)
+            *sort_columns).filter(releases__gt=0)
         if maintainer:
             try:
                 user = User.objects.get_by_natural_key(maintainer)

{"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@@ -142,7 +142,7 @@\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)\n+            *sort_columns).filter(releases__gt=0)\n         if maintainer:\n             try:\n                 user = User.objects.get_by_natural_key(maintainer)\n", "issue": "Apps without releases should not be rendered on the app list page\n\n", "before_files": [{"content": "from urllib.parse import urlencode\n\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.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.forms import AppRatingForm\nfrom nextcloudappstore.core.models import App, Category, AppRating\nfrom nextcloudappstore.core.versioning import pad_min_version\n\n\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    model = App\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                             language_code=request.LANGUAGE_CODE)\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['rating_form'] = AppRatingForm()\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(initial={\n                    'rating': app_rating.rating,\n                    'comment': comment\n                })\n                context['user_has_rated_app'] = True\n            except AppRating.DoesNotExist:\n                pass\n        context['categories'] = Category.objects.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    model = App\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.all()\n\n        releases = self.object.releases_by_platform_v()\n        nightlies = self.object.nightly_releases_by_platform_v()\n        versions = set(list(releases.keys()) + list(nightlies.keys()))\n        all_releases = list(map(\n            lambda v: (v, releases.get(v, []) + nightlies.get(v, [])),\n            versions))\n        context['releases_by_platform_v'] = \\\n            self._sort_by_platform_v(all_releases)\n\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        featured = self.request.GET.get('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)\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 featured == \"true\":\n            queryset = queryset.filter(featured=True)\n        return queryset\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['categories'] = Category.objects.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('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(TemplateView):\n    template_name = 'app/upload.html'\n", "path": "nextcloudappstore/core/views.py"}], "after_files": [{"content": "from urllib.parse import urlencode\n\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.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.forms import AppRatingForm\nfrom nextcloudappstore.core.models import App, Category, AppRating\nfrom nextcloudappstore.core.versioning import pad_min_version\n\n\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    model = App\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                             language_code=request.LANGUAGE_CODE)\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['rating_form'] = AppRatingForm()\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(initial={\n                    'rating': app_rating.rating,\n                    'comment': comment\n                })\n                context['user_has_rated_app'] = True\n            except AppRating.DoesNotExist:\n                pass\n        context['categories'] = Category.objects.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    model = App\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.all()\n\n        releases = self.object.releases_by_platform_v()\n        nightlies = self.object.nightly_releases_by_platform_v()\n        versions = set(list(releases.keys()) + list(nightlies.keys()))\n        all_releases = list(map(\n            lambda v: (v, releases.get(v, []) + nightlies.get(v, [])),\n            versions))\n        context['releases_by_platform_v'] = \\\n            self._sort_by_platform_v(all_releases)\n\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        featured = self.request.GET.get('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 featured == \"true\":\n            queryset = queryset.filter(featured=True)\n        return queryset\n\n    def get_context_data(self, **kwargs):\n        context = super().get_context_data(**kwargs)\n        context['categories'] = Category.objects.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('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(TemplateView):\n    template_name = 'app/upload.html'\n", "path": "nextcloudappstore/core/views.py"}]}