We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
API should exclude unpublished projects by default?
The API currently filters out private projects for unauthenticated users, but doesn't do the same for unpublished projects. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `akvo/rest/viewsets.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 # Akvo RSR is covered by the GNU Affero General Public License.
4 # See more details in the license.txt file located at the root folder of the Akvo RSR module.
5 # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
6 
7 from django.db.models.fields import FieldDoesNotExist
8 from django.db.models.fields.related import ForeignObject
9 from django.core.exceptions import FieldError
10 
11 from akvo.rest.models import TastyTokenAuthentication
12 
13 from rest_framework import authentication, filters, permissions, viewsets
14 
15 from .filters import RSRGenericFilterBackend
16 from .pagination import TastypieOffsetPagination
17 
18 
19 class SafeMethodsPermissions(permissions.DjangoObjectPermissions):
20     """
21     Base class to allow any safe methods ('GET', 'OPTIONS' and 'HEAD') without needing to
22     authenticate.
23     """
24 
25     def has_permission(self, request, view):
26         if request.method in permissions.SAFE_METHODS:
27             return True
28         return super(SafeMethodsPermissions, self).has_permission(request, view)
29 
30 
31 class BaseRSRViewSet(viewsets.ModelViewSet):
32     """
33     Base class used for the view sets for RSR models. Provides unified auth and perms settings.
34     """
35     authentication_classes = (authentication.SessionAuthentication, TastyTokenAuthentication, )
36     permission_classes = (SafeMethodsPermissions, )
37     filter_backends = (filters.OrderingFilter, RSRGenericFilterBackend,)
38     ordering_fields = '__all__'
39 
40     def paginate_queryset(self, queryset):
41         """ Custom offset-based pagination for the Tastypie API emulation
42         """
43         if self.request and '/api/v1/' in self.request.path:
44             self.pagination_class = TastypieOffsetPagination
45         return super(BaseRSRViewSet, self).paginate_queryset(queryset)
46 
47     def get_queryset(self):
48 
49         def django_filter_filters(request):
50             """
51             Support emulating the DjangoFilterBackend-based filtering that some views used to have
52             """
53             # query string keys reserved by the RSRGenericFilterBackend
54             qs_params = ['filter', 'exclude', 'select_related', 'prefetch_related', ]
55             # query string keys used by core DRF, OrderingFilter and Akvo custom views
56             exclude_params = ['limit', 'format', 'page', 'offset', 'ordering', 'partner_type',
57                               'sync_owner', 'reporting_org', ]
58             filters = {}
59             for key in request.query_params.keys():
60                 if key not in qs_params + exclude_params and not key.startswith('image_thumb_'):
61                     filters.update({key: request.query_params.get(key)})
62             return filters
63 
64         def get_lookups_from_filters(legacy_filters):
65             """
66             Cast the values in DjangoFilterBackend-styled query string filters to correct types to
67             be able to use them in regular queryset-filter() calls
68             """
69             # types of lookups supported by the views using DjangoFilterBackend
70             LEGACY_FIELD_LOOKUPS = ['exact', 'contains', 'icontains', 'gt', 'gte', 'lt',
71                                     'lte', ]
72             query_set_lookups = []
73             for key, value in legacy_filters.items():
74                 parts = key.split('__')
75                 if parts[-1] in LEGACY_FIELD_LOOKUPS:
76                     parts = parts[:-1]
77                     model = queryset.model
78                     for part in parts:
79                         try:
80                             field_object, related_model, direct, m2m = model._meta.\
81                                 get_field_by_name(part)
82 
83                             if direct:
84                                 if issubclass(field_object.__class__, ForeignObject):
85                                     model = field_object.related.parent_model
86                                 else:
87                                     value = field_object.to_python(value)
88                                     break
89                             else:
90                                 model = related_model
91                         except FieldDoesNotExist:
92                             pass
93                 query_set_lookups += [{key: value}]
94             return query_set_lookups
95 
96         queryset = super(BaseRSRViewSet, self).get_queryset()
97 
98         # support for old DjangoFilterBackend-based filtering if not pk is given
99         if not self.kwargs.get(u'pk'):
100             # find all "old styled" filters
101             legacy_filters = django_filter_filters(self.request)
102             # create lookup dicts from the filters found
103             lookups = get_lookups_from_filters(legacy_filters)
104             for lookup in lookups:
105                 try:
106                     queryset = queryset.filter(**lookup)
107                 except (FieldError, ValueError):
108                     # In order to mimick 'old' behaviour of the API, we should ignore non-valid
109                     # parameters or values. Returning a warning would be more preferable.
110                     pass
111 
112         return queryset
113 
114 
115 class PublicProjectViewSet(BaseRSRViewSet):
116     """
117     Only public projects or objects related to public projects will be shown.
118     """
119     # project_relation is the default string for constructing a field lookup to the is_public field
120     # on the related Project. Override this in when the viewset is for a model that doesn't have a
121     # direct FK to Project or the FK field isn't named project. E.g. IndicatorViewSet:
122     #   project_relation = 'result__project__'
123     # The lookup is used to filter out objects associated with private projects, see below.
124     project_relation = 'project__'
125 
126     def get_queryset(self):
127 
128         request = self.request
129         user = request.user
130 
131         queryset = super(PublicProjectViewSet, self).get_queryset()
132 
133         # filter projects if user is "non-privileged"
134         if user.is_anonymous() or not (user.is_superuser or user.is_admin):
135             queryset = self.projects_filter_for_non_privileged_users(user, queryset, self.project_relation)
136 
137         return queryset.distinct()
138 
139     @staticmethod
140     def projects_filter_for_non_privileged_users(user, queryset, project_relation):
141 
142         if not user.is_anonymous() and (user.is_admin or user.is_superuser):
143             return queryset.distinct()
144 
145         # Construct the public projects filter field lookup.
146         project_filter = project_relation + 'is_public'
147 
148         # Filter the object list into two querysets;
149         # One where the related Projects are public and one where they are private
150         public_objects = queryset.filter(**{project_filter: True}).distinct()
151         private_objects = queryset.filter(**{project_filter: False}).distinct()
152 
153         # In case of an anonymous user, only return the public objects
154         if user.is_anonymous():
155             queryset = public_objects
156 
157         # Otherwise, check to which objects the user has (change) permission
158         elif private_objects.exists():
159             permission = type(private_objects[0])._meta.db_table.replace('_', '.change_')
160             filter_ = user.get_permission_filter(permission, project_relation)
161             queryset = public_objects | private_objects.filter(filter_).distinct()
162 
163         return queryset.distinct()
164
```
--- 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/akvo/rest/viewsets.py b/akvo/rest/viewsets.py
--- a/akvo/rest/viewsets.py
+++ b/akvo/rest/viewsets.py
@@ -8,6 +8,7 @@
 from django.db.models.fields.related import ForeignObject
 from django.core.exceptions import FieldError
 
+from akvo.rsr.models import PublishingStatus
 from akvo.rest.models import TastyTokenAuthentication
 
 from rest_framework import authentication, filters, permissions, viewsets
@@ -152,7 +153,10 @@
 
         # In case of an anonymous user, only return the public objects
         if user.is_anonymous():
-            queryset = public_objects
+            unpublished_exclude = project_relation + 'publishingstatus__status'
+            queryset = public_objects.exclude(
+                **{unpublished_exclude: PublishingStatus.STATUS_UNPUBLISHED}
+            ).distinct()
 
         # Otherwise, check to which objects the user has (change) permission
         elif private_objects.exists():

{"golden_diff": "diff --git a/akvo/rest/viewsets.py b/akvo/rest/viewsets.py\n--- a/akvo/rest/viewsets.py\n+++ b/akvo/rest/viewsets.py\n@@ -8,6 +8,7 @@\n from django.db.models.fields.related import ForeignObject\n from django.core.exceptions import FieldError\n \n+from akvo.rsr.models import PublishingStatus\n from akvo.rest.models import TastyTokenAuthentication\n \n from rest_framework import authentication, filters, permissions, viewsets\n@@ -152,7 +153,10 @@\n \n         # In case of an anonymous user, only return the public objects\n         if user.is_anonymous():\n-            queryset = public_objects\n+            unpublished_exclude = project_relation + 'publishingstatus__status'\n+            queryset = public_objects.exclude(\n+                **{unpublished_exclude: PublishingStatus.STATUS_UNPUBLISHED}\n+            ).distinct()\n \n         # Otherwise, check to which objects the user has (change) permission\n         elif private_objects.exists():\n", "issue": "API should exclude unpublished projects by default?\nThe API currently filters out private projects for unauthenticated users, but doesn't do the same for unpublished projects. \n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Akvo RSR is covered by the GNU Affero General Public License.\n# See more details in the license.txt file located at the root folder of the Akvo RSR module.\n# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n\nfrom django.db.models.fields import FieldDoesNotExist\nfrom django.db.models.fields.related import ForeignObject\nfrom django.core.exceptions import FieldError\n\nfrom akvo.rest.models import TastyTokenAuthentication\n\nfrom rest_framework import authentication, filters, permissions, viewsets\n\nfrom .filters import RSRGenericFilterBackend\nfrom .pagination import TastypieOffsetPagination\n\n\nclass SafeMethodsPermissions(permissions.DjangoObjectPermissions):\n    \"\"\"\n    Base class to allow any safe methods ('GET', 'OPTIONS' and 'HEAD') without needing to\n    authenticate.\n    \"\"\"\n\n    def has_permission(self, request, view):\n        if request.method in permissions.SAFE_METHODS:\n            return True\n        return super(SafeMethodsPermissions, self).has_permission(request, view)\n\n\nclass BaseRSRViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    Base class used for the view sets for RSR models. Provides unified auth and perms settings.\n    \"\"\"\n    authentication_classes = (authentication.SessionAuthentication, TastyTokenAuthentication, )\n    permission_classes = (SafeMethodsPermissions, )\n    filter_backends = (filters.OrderingFilter, RSRGenericFilterBackend,)\n    ordering_fields = '__all__'\n\n    def paginate_queryset(self, queryset):\n        \"\"\" Custom offset-based pagination for the Tastypie API emulation\n        \"\"\"\n        if self.request and '/api/v1/' in self.request.path:\n            self.pagination_class = TastypieOffsetPagination\n        return super(BaseRSRViewSet, self).paginate_queryset(queryset)\n\n    def get_queryset(self):\n\n        def django_filter_filters(request):\n            \"\"\"\n            Support emulating the DjangoFilterBackend-based filtering that some views used to have\n            \"\"\"\n            # query string keys reserved by the RSRGenericFilterBackend\n            qs_params = ['filter', 'exclude', 'select_related', 'prefetch_related', ]\n            # query string keys used by core DRF, OrderingFilter and Akvo custom views\n            exclude_params = ['limit', 'format', 'page', 'offset', 'ordering', 'partner_type',\n                              'sync_owner', 'reporting_org', ]\n            filters = {}\n            for key in request.query_params.keys():\n                if key not in qs_params + exclude_params and not key.startswith('image_thumb_'):\n                    filters.update({key: request.query_params.get(key)})\n            return filters\n\n        def get_lookups_from_filters(legacy_filters):\n            \"\"\"\n            Cast the values in DjangoFilterBackend-styled query string filters to correct types to\n            be able to use them in regular queryset-filter() calls\n            \"\"\"\n            # types of lookups supported by the views using DjangoFilterBackend\n            LEGACY_FIELD_LOOKUPS = ['exact', 'contains', 'icontains', 'gt', 'gte', 'lt',\n                                    'lte', ]\n            query_set_lookups = []\n            for key, value in legacy_filters.items():\n                parts = key.split('__')\n                if parts[-1] in LEGACY_FIELD_LOOKUPS:\n                    parts = parts[:-1]\n                    model = queryset.model\n                    for part in parts:\n                        try:\n                            field_object, related_model, direct, m2m = model._meta.\\\n                                get_field_by_name(part)\n\n                            if direct:\n                                if issubclass(field_object.__class__, ForeignObject):\n                                    model = field_object.related.parent_model\n                                else:\n                                    value = field_object.to_python(value)\n                                    break\n                            else:\n                                model = related_model\n                        except FieldDoesNotExist:\n                            pass\n                query_set_lookups += [{key: value}]\n            return query_set_lookups\n\n        queryset = super(BaseRSRViewSet, self).get_queryset()\n\n        # support for old DjangoFilterBackend-based filtering if not pk is given\n        if not self.kwargs.get(u'pk'):\n            # find all \"old styled\" filters\n            legacy_filters = django_filter_filters(self.request)\n            # create lookup dicts from the filters found\n            lookups = get_lookups_from_filters(legacy_filters)\n            for lookup in lookups:\n                try:\n                    queryset = queryset.filter(**lookup)\n                except (FieldError, ValueError):\n                    # In order to mimick 'old' behaviour of the API, we should ignore non-valid\n                    # parameters or values. Returning a warning would be more preferable.\n                    pass\n\n        return queryset\n\n\nclass PublicProjectViewSet(BaseRSRViewSet):\n    \"\"\"\n    Only public projects or objects related to public projects will be shown.\n    \"\"\"\n    # project_relation is the default string for constructing a field lookup to the is_public field\n    # on the related Project. Override this in when the viewset is for a model that doesn't have a\n    # direct FK to Project or the FK field isn't named project. E.g. IndicatorViewSet:\n    #   project_relation = 'result__project__'\n    # The lookup is used to filter out objects associated with private projects, see below.\n    project_relation = 'project__'\n\n    def get_queryset(self):\n\n        request = self.request\n        user = request.user\n\n        queryset = super(PublicProjectViewSet, self).get_queryset()\n\n        # filter projects if user is \"non-privileged\"\n        if user.is_anonymous() or not (user.is_superuser or user.is_admin):\n            queryset = self.projects_filter_for_non_privileged_users(user, queryset, self.project_relation)\n\n        return queryset.distinct()\n\n    @staticmethod\n    def projects_filter_for_non_privileged_users(user, queryset, project_relation):\n\n        if not user.is_anonymous() and (user.is_admin or user.is_superuser):\n            return queryset.distinct()\n\n        # Construct the public projects filter field lookup.\n        project_filter = project_relation + 'is_public'\n\n        # Filter the object list into two querysets;\n        # One where the related Projects are public and one where they are private\n        public_objects = queryset.filter(**{project_filter: True}).distinct()\n        private_objects = queryset.filter(**{project_filter: False}).distinct()\n\n        # In case of an anonymous user, only return the public objects\n        if user.is_anonymous():\n            queryset = public_objects\n\n        # Otherwise, check to which objects the user has (change) permission\n        elif private_objects.exists():\n            permission = type(private_objects[0])._meta.db_table.replace('_', '.change_')\n            filter_ = user.get_permission_filter(permission, project_relation)\n            queryset = public_objects | private_objects.filter(filter_).distinct()\n\n        return queryset.distinct()\n", "path": "akvo/rest/viewsets.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Akvo RSR is covered by the GNU Affero General Public License.\n# See more details in the license.txt file located at the root folder of the Akvo RSR module.\n# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n\nfrom django.db.models.fields import FieldDoesNotExist\nfrom django.db.models.fields.related import ForeignObject\nfrom django.core.exceptions import FieldError\n\nfrom akvo.rsr.models import PublishingStatus\nfrom akvo.rest.models import TastyTokenAuthentication\n\nfrom rest_framework import authentication, filters, permissions, viewsets\n\nfrom .filters import RSRGenericFilterBackend\nfrom .pagination import TastypieOffsetPagination\n\n\nclass SafeMethodsPermissions(permissions.DjangoObjectPermissions):\n    \"\"\"\n    Base class to allow any safe methods ('GET', 'OPTIONS' and 'HEAD') without needing to\n    authenticate.\n    \"\"\"\n\n    def has_permission(self, request, view):\n        if request.method in permissions.SAFE_METHODS:\n            return True\n        return super(SafeMethodsPermissions, self).has_permission(request, view)\n\n\nclass BaseRSRViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    Base class used for the view sets for RSR models. Provides unified auth and perms settings.\n    \"\"\"\n    authentication_classes = (authentication.SessionAuthentication, TastyTokenAuthentication, )\n    permission_classes = (SafeMethodsPermissions, )\n    filter_backends = (filters.OrderingFilter, RSRGenericFilterBackend,)\n    ordering_fields = '__all__'\n\n    def paginate_queryset(self, queryset):\n        \"\"\" Custom offset-based pagination for the Tastypie API emulation\n        \"\"\"\n        if self.request and '/api/v1/' in self.request.path:\n            self.pagination_class = TastypieOffsetPagination\n        return super(BaseRSRViewSet, self).paginate_queryset(queryset)\n\n    def get_queryset(self):\n\n        def django_filter_filters(request):\n            \"\"\"\n            Support emulating the DjangoFilterBackend-based filtering that some views used to have\n            \"\"\"\n            # query string keys reserved by the RSRGenericFilterBackend\n            qs_params = ['filter', 'exclude', 'select_related', 'prefetch_related', ]\n            # query string keys used by core DRF, OrderingFilter and Akvo custom views\n            exclude_params = ['limit', 'format', 'page', 'offset', 'ordering', 'partner_type',\n                              'sync_owner', 'reporting_org', ]\n            filters = {}\n            for key in request.query_params.keys():\n                if key not in qs_params + exclude_params and not key.startswith('image_thumb_'):\n                    filters.update({key: request.query_params.get(key)})\n            return filters\n\n        def get_lookups_from_filters(legacy_filters):\n            \"\"\"\n            Cast the values in DjangoFilterBackend-styled query string filters to correct types to\n            be able to use them in regular queryset-filter() calls\n            \"\"\"\n            # types of lookups supported by the views using DjangoFilterBackend\n            LEGACY_FIELD_LOOKUPS = ['exact', 'contains', 'icontains', 'gt', 'gte', 'lt',\n                                    'lte', ]\n            query_set_lookups = []\n            for key, value in legacy_filters.items():\n                parts = key.split('__')\n                if parts[-1] in LEGACY_FIELD_LOOKUPS:\n                    parts = parts[:-1]\n                    model = queryset.model\n                    for part in parts:\n                        try:\n                            field_object, related_model, direct, m2m = model._meta.\\\n                                get_field_by_name(part)\n\n                            if direct:\n                                if issubclass(field_object.__class__, ForeignObject):\n                                    model = field_object.related.parent_model\n                                else:\n                                    value = field_object.to_python(value)\n                                    break\n                            else:\n                                model = related_model\n                        except FieldDoesNotExist:\n                            pass\n                query_set_lookups += [{key: value}]\n            return query_set_lookups\n\n        queryset = super(BaseRSRViewSet, self).get_queryset()\n\n        # support for old DjangoFilterBackend-based filtering if not pk is given\n        if not self.kwargs.get(u'pk'):\n            # find all \"old styled\" filters\n            legacy_filters = django_filter_filters(self.request)\n            # create lookup dicts from the filters found\n            lookups = get_lookups_from_filters(legacy_filters)\n            for lookup in lookups:\n                try:\n                    queryset = queryset.filter(**lookup)\n                except (FieldError, ValueError):\n                    # In order to mimick 'old' behaviour of the API, we should ignore non-valid\n                    # parameters or values. Returning a warning would be more preferable.\n                    pass\n\n        return queryset\n\n\nclass PublicProjectViewSet(BaseRSRViewSet):\n    \"\"\"\n    Only public projects or objects related to public projects will be shown.\n    \"\"\"\n    # project_relation is the default string for constructing a field lookup to the is_public field\n    # on the related Project. Override this in when the viewset is for a model that doesn't have a\n    # direct FK to Project or the FK field isn't named project. E.g. IndicatorViewSet:\n    #   project_relation = 'result__project__'\n    # The lookup is used to filter out objects associated with private projects, see below.\n    project_relation = 'project__'\n\n    def get_queryset(self):\n\n        request = self.request\n        user = request.user\n\n        queryset = super(PublicProjectViewSet, self).get_queryset()\n\n        # filter projects if user is \"non-privileged\"\n        if user.is_anonymous() or not (user.is_superuser or user.is_admin):\n            queryset = self.projects_filter_for_non_privileged_users(user, queryset, self.project_relation)\n\n        return queryset.distinct()\n\n    @staticmethod\n    def projects_filter_for_non_privileged_users(user, queryset, project_relation):\n\n        if not user.is_anonymous() and (user.is_admin or user.is_superuser):\n            return queryset.distinct()\n\n        # Construct the public projects filter field lookup.\n        project_filter = project_relation + 'is_public'\n\n        # Filter the object list into two querysets;\n        # One where the related Projects are public and one where they are private\n        public_objects = queryset.filter(**{project_filter: True}).distinct()\n        private_objects = queryset.filter(**{project_filter: False}).distinct()\n\n        # In case of an anonymous user, only return the public objects\n        if user.is_anonymous():\n            unpublished_exclude = project_relation + 'publishingstatus__status'\n            queryset = public_objects.exclude(\n                **{unpublished_exclude: PublishingStatus.STATUS_UNPUBLISHED}\n            ).distinct()\n\n        # Otherwise, check to which objects the user has (change) permission\n        elif private_objects.exists():\n            permission = type(private_objects[0])._meta.db_table.replace('_', '.change_')\n            filter_ = user.get_permission_filter(permission, project_relation)\n            queryset = public_objects | private_objects.filter(filter_).distinct()\n\n        return queryset.distinct()\n", "path": "akvo/rest/viewsets.py"}]}